fix(desktop): uninstaller arg bug + no-cache for bridge static assets

Bug 1 (uninstall.bat could not delete the folder): %~dp0 ends with a
backslash, so -BundleDir "%~dp0" passed "...\dir\" — the trailing \" escaped
the closing quote and PowerShell received a path with a literal quote
("Illegal characters in path"). Resolve-Path failed, the deleter ran rd on a
garbage path, and nothing was removed. Strip the trailing backslash in the
.bat before passing, and defensively trim stray quotes/separators in the ps1.

Bug 2 root cause (shortcut prompt / UI showed stale code after updates):
the bridge served app.js via aiohttp's static handler with only
ETag/Last-Modified and no Cache-Control, so WebView2 heuristically cached it
and kept serving an old app.js after rebuilds. Add a no_cache_middleware
that sets Cache-Control: no-cache, no-store, must-revalidate on GET/HEAD.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
yiqi-017andClaude Opus 4.8 committed 2026-06-22 00:31:11 +08:00
1 parent b60f3535af
commit f3a85d1f37
3 files changed
+25 -2

No files matched your search

@@ -24,7 +24,12 @@ if /i not "%CONFIRM%"=="Y" (
)
echo.
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0runtime\uninstall_windows.ps1" -BundleDir "%~dp0"
rem %~dp0 ends with a backslash; passing "...\dir\" makes the trailing \" escape the closing
rem quote, so PowerShell receives a path with a literal quote ("Illegal characters in path").
rem Strip the trailing backslash before passing the bundle dir.
set "BUNDLE=%~dp0"
if "%BUNDLE:~-1%"=="\" set "BUNDLE=%BUNDLE:~0,-1%"
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0runtime\uninstall_windows.ps1" -BundleDir "%BUNDLE%"
echo.
echo You can close this window now.
@@ -29,6 +29,9 @@ function Write-Ok([string]$m) { Write-Host "[OK] $m" -ForegroundColor Green }
function Write-Info([string]$m) { Write-Host " $m" -ForegroundColor Gray }
# Normalize the bundle dir to an absolute path with a trailing separator for prefix checks.
# Defensive: strip stray quotes / trailing separators a caller may have leaked in (e.g. the
# classic "%~dp0" trailing-backslash-before-quote bug).
$BundleDir = $BundleDir.Trim().Trim('"').TrimEnd('\', '/')
try { $bundle = (Resolve-Path -LiteralPath $BundleDir).Path } catch { $bundle = $BundleDir }
$bundlePrefix = ($bundle.TrimEnd('\') + '\').ToLowerInvariant()
+16 -1
View File
@@ -1050,6 +1050,21 @@ async def cors_middleware(request, handler):
return resp
@web.middleware
async def no_cache_middleware(request, handler):
# WebView2 (Chromium) heuristically caches assets that ship only ETag/Last-Modified with no
# Cache-Control (aiohttp's static handler does exactly that), so a rebuilt app.js/index.html
# keeps serving stale from cache for ~10% of its age — users saw old UI after updates. Force
# revalidation on every GET so the desktop shell always loads the bundle's current frontend.
resp = await handler(request)
try:
if request.method in ("GET", "HEAD"):
resp.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
except Exception:
pass
return resp
def json_ok(data: dict, status: int = 200):
return web.json_response(data, status=status, headers=cors_headers(), dumps=lambda x: json.dumps(x, ensure_ascii=False, default=str))
@@ -1585,7 +1600,7 @@ async def post_token_history_handler(request):
def create_app():
app = web.Application(middlewares=[cors_middleware], client_max_size=500 * 1024 * 1024)
app = web.Application(middlewares=[cors_middleware, no_cache_middleware], client_max_size=500 * 1024 * 1024)
app.router.add_get("/ws", ws_handler)
app.router.add_get("/status", status_handler)
app.router.add_get("/config", get_config_handler)