From 37a8ff75b2b59fb44271d017fce218e2d408fe83 Mon Sep 17 00:00:00 2001 From: Meredydd Luff Date: Fri, 15 May 2020 13:26:56 +0100 Subject: [PATCH] Anvil App Server v1.1 Changes: - Doc updates - More helpful error messages in several places - Add a simple Dockerfile - Fix HTTPS origin handling - Use the same Python interpreter for the downlink as launcher Fixes #1, closes #3, Better debug output to investigate #4, Fixes #7 Based-on: anvil 28d8a74b616655716ece2b2f34025cbc12332a46 --- client/js/runner.js | 8 + client/js/sw.js | 69 +++++++++ client/js/webpack.config.js | 3 +- client/runner.html | 44 +++--- doc/creating-and-editing-apps.md | 7 +- doc/getting-started.md | 10 +- .../python/anvil_downlink_host/__init__.py | 4 +- packaging/app-server/Dockerfile | 29 ++-- .../app-server/anvil_app_server/__init__.py | 16 +- packaging/app-server/setup.py | 6 +- packaging/app-server/templates/Blank/LICENSE | 5 + .../app-server/templates/Default/LICENSE | 5 + .../app-server/templates/TodoList/LICENSE | 5 + .../app-server/src/anvil/app_server/conf.clj | 2 + .../src/anvil/app_server/dispatch.clj | 3 +- .../src/anvil/app_server/postgres.clj | 141 ++++++++++-------- .../app-server/src/anvil/app_server/run.clj | 51 ++++--- server/core/src/anvil/executors/uplink.clj | 10 +- server/core/src/anvil/runtime/server.clj | 10 ++ 19 files changed, 293 insertions(+), 135 deletions(-) create mode 100644 client/js/sw.js create mode 100644 packaging/app-server/templates/Blank/LICENSE create mode 100644 packaging/app-server/templates/Default/LICENSE create mode 100644 packaging/app-server/templates/TodoList/LICENSE diff --git a/client/js/runner.js b/client/js/runner.js index db9a8b2..e44df7e 100644 --- a/client/js/runner.js +++ b/client/js/runner.js @@ -1092,6 +1092,14 @@ window.loadApp = function(params, preloadModules) { var appOrigin = params["appOrigin"]; if (appLoaded) { console.log("Rejected duplicate app load"); return {}; } + if ('serviceWorker' in navigator) { + navigator.serviceWorker.register(`${appOrigin}/_/service-worker`, {scope: `${appOrigin}`}) + .catch((error) => { + console.error('Service worker registration failed:', error); + }); + } + + var appLoadPromise = loadApp(params["app"], params["appId"], appOrigin, preloadModules); appLoaded = true; diff --git a/client/js/sw.js b/client/js/sw.js new file mode 100644 index 0000000..ad2c4b2 --- /dev/null +++ b/client/js/sw.js @@ -0,0 +1,69 @@ +let log = true; + +let ACTIVE_CACHE = 'v0'; +let OFFLINE_TIMEOUT = 5000; + +let lastOffline = 0; + +let cleanupOldCaches = async () => { + for (let key of await caches.keys()) { + if (key !== ACTIVE_CACHE) { + console.log("Removing old service worker cache:", key); + await caches.delete(key); + } + } +} + + +let _fetch = async e => { + + let cache = await caches.open(ACTIVE_CACHE); + let match = await cache.match(e.request); + + if (!navigator.onLine || lastOffline > Date.now() - OFFLINE_TIMEOUT) { + // Shortcut: Use cache if a request recently failed for something in the cache. + if (match) { + log && console.log("Fast offline cache hit:", e.request.url); + return match; + } else { + log && console.log("Fast offline cache miss:", e.request.url); + } + } + + try { + let resp = await fetch(e.request.clone()); + lastOffline = 0; + + // Allow caching anything that comes back with the X-Anvil-Cacheable header + if (e.request.method === "GET" && resp.status === 200 && resp.headers.has("X-Anvil-Cacheable")) { + log && console.log("Caching:", e.request.url); + cache.put(e.request, resp.clone()); + } else { + log && console.log("Not caching:", e.request.url); + cache.delete(e.request); + } + return resp; + } catch (err) { + if (match) { + lastOffline = Date.now(); + console.log("Serving Anvil resources from Service Worker cache"); + log && console.log("Offline cache hit:", e.request.url); + return match; + } else { + log && console.log("Offline cache miss:", e.request.url); + throw err; + } + } +}; + +addEventListener('install', e => { + console.log("Service Worker installed with scope:", registration.scope); +}); + +addEventListener('activate', e => { + e.waitUntil(cleanupOldCaches()); +}); + +addEventListener('fetch', e => { + e.respondWith(_fetch(e)) +}); diff --git a/client/js/webpack.config.js b/client/js/webpack.config.js index 4e66db0..0606b08 100644 --- a/client/js/webpack.config.js +++ b/client/js/webpack.config.js @@ -4,9 +4,10 @@ var webpack = require("webpack"); module.exports = { context: path.resolve(__dirname), - // We want to generate two bundles. One for designer, one for runner. + // We want to generate two bundles. One for runner. one for its Service Worker entry: { runner: ['babel-polyfill', './runner.js'], + sw: ['babel-polyfill', './sw.js'], }, // Make PyDefUtils available as window.PyDefUtils diff --git a/client/runner.html b/client/runner.html index da80939..e3774e0 100644 --- a/client/runner.html +++ b/client/runner.html @@ -20,13 +20,13 @@ {{app-title}} - - - - - + + + + + - + - - + + {{head-html}}
Built with Anvil - +
- +
@@ -121,7 +121,7 @@
- + - - - - - + + + + + - - + + - - + + - +