Fix SW scope to intercept page navigations for offline support

The SW was served from /static/sw.js with default scope /static/,
so it only intercepted static asset requests. Page navigations to
/capture, /queue, /browser were not handled by the SW at all —
Safari showed its native offline error.

Fix: serve SW from /sw.js route with Service-Worker-Allowed: / header
and register with scope: /. Now the SW intercepts all navigations and
serves the offline fallback page when the network is unavailable.

Also remove auth-protected page routes from precache (they would cache
the login redirect). Pages are cached via network-first on visit instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 22:16:07 -06:00
parent ac241fae2e
commit 37f417eb5b
3 changed files with 17 additions and 9 deletions

View File

@@ -1,7 +1,19 @@
from flask import Blueprint, render_template, redirect, url_for, session, jsonify from flask import Blueprint, render_template, redirect, url_for, session, jsonify, send_from_directory, current_app
bp = Blueprint('views', __name__) bp = Blueprint('views', __name__)
@bp.route('/sw.js')
def service_worker():
"""Serve service worker from root scope."""
response = send_from_directory(
current_app.static_folder, 'sw.js',
mimetype='application/javascript'
)
response.headers['Service-Worker-Allowed'] = '/'
response.headers['Cache-Control'] = 'no-cache'
return response
@bp.route('/') @bp.route('/')
def index(): def index():
"""Root route - redirect to capture if logged in, otherwise show login.""" """Root route - redirect to capture if logged in, otherwise show login."""

View File

@@ -1,9 +1,9 @@
// NextSnap Service Worker // NextSnap Service Worker
// Provides offline-first caching for the app shell // Provides offline-first caching for the app shell
const CACHE_VERSION = 'nextsnap-v21'; const CACHE_VERSION = 'nextsnap-v22';
const APP_SHELL_CACHE = 'nextsnap-shell-v17'; const APP_SHELL_CACHE = 'nextsnap-shell-v18';
const RUNTIME_CACHE = 'nextsnap-runtime-v17'; const RUNTIME_CACHE = 'nextsnap-runtime-v18';
// Offline fallback page (served when network fails and no cached version exists) // Offline fallback page (served when network fails and no cached version exists)
const OFFLINE_PAGE = `<!DOCTYPE html> const OFFLINE_PAGE = `<!DOCTYPE html>
@@ -26,10 +26,6 @@ button:active{opacity:0.8}
// Assets to cache on install // Assets to cache on install
const APP_SHELL_ASSETS = [ const APP_SHELL_ASSETS = [
'/',
'/capture',
'/queue',
'/browser',
'/static/css/style.css', '/static/css/style.css',
'/static/js/app.js', '/static/js/app.js',
'/static/js/auth.js', '/static/js/auth.js',

View File

@@ -65,7 +65,7 @@
<script> <script>
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
window.addEventListener('load', () => { window.addEventListener('load', () => {
navigator.serviceWorker.register('/static/sw.js') navigator.serviceWorker.register('/sw.js', { scope: '/' })
.then((registration) => { .then((registration) => {
console.log('Service Worker registered:', registration.scope); console.log('Service Worker registered:', registration.scope);