How to exclude url in workbox runtime caching? - progressive-web-apps

I am using workbox-build for Gulp in my django project. All works correct, but there are some problems with admin urls. As I see, /admin/* urls caching in runtimes - I can see them in Chrome DevTools/Application/Cache. How can I exclude admin urls from runtime caching?
gulp.js:
gulp.task('service-worker', () => {
return workboxBuild.injectManifest({
globDirectory: '/var/www/example.com/',
swSrc: '/var/www/example.com/core/templates/core/serviceWorker/sw-dev.js',
swDest: '/var/www/example.com/core/templates/core/serviceWorker/sw.js',
globPatterns:['**/*.{html,js,css,jpg,png,ttf,otf}'],
globIgnores: ['admin\/**','media\/**','core\/**','static/admin\/**','static/core/scripts/plugins/**']
}).then(({count, size, warnings}) => {
});
sw.js:
importScripts("https://storage.googleapis.com/workbox- cdn/releases/3.4.1/workbox-sw.js");
workbox.precaching.precacheAndRoute([]);
workbox.googleAnalytics.initialize();
workbox.routing.registerRoute(
workbox.strategies.cacheFirst({
// Use a custom cache name
cacheName: 'image-cache',
plugins: [
new workbox.expiration.Plugin({
// Cache only 20 images
maxEntries: 30,
// Cache for a maximum of a week
maxAgeSeconds: 7 * 24 * 60 * 60,
})
],
})
);
workbox.routing.registerRoute(
/.*\.(?:ttf|otf)/,
workbox.strategies.cacheFirst({
cacheName: 'font-cache',
})
);
workbox.routing.registerRoute(
new RegExp('\/$'),
workbox.strategies.staleWhileRevalidate()
);
workbox.routing.registerRoute(
new RegExp('contacts\/$'),
workbox.strategies.staleWhileRevalidate()
);
workbox.routing.registerRoute(
new RegExp('pricelist\/$'),
workbox.strategies.staleWhileRevalidate()
);

In addition to providing RegExps for routing, Workbox's registerRoute() method supports matchCallback functions. I think that they're easier to make sense of, and recently most of the examples in the public documentation have migrated to use them.
workbox.routing.registerRoute(
// Match all navigation requests, except those for URLs whose
// path starts with '/admin/'
({request, url}) => request.mode === 'navigate' &&
!url.pathname.startsWith('/admin/'),
new workbox.strategies.StaleWhileRevalidate()
);

Related

How to detect `eval` calls when addressing UI5 modules in runtime?

As part of the preparation for the "Asynchronous Module Loading" enablement in FLP, one of its prerequisites is making sure that no eval call is triggered due to the HTML document being served with the response header Content-Security-Policy and its directive script-src omitting unsafe-eval.
Is there a feature in UI5 that logs all eval calls in runtime triggered by incorrectly addressed modules?
Resolution
Open the browser console (F12) and ensure that "Warnings" logs are visible.
Start the app with the UI5 config URL parameter sap-ui-xx-debugModuleLoading=true and make sure that the debug mode is not unnecessarily enabled.
In the browser console, filter by "(using eval)".
Example
Given
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/comp/library",
], function(Controller, sapUiCompLib) {
// ...
const ValueHelpRangeOperation = sapUiCompLib.valuehelpdialog.ValueHelpRangeOperation; // enum from module "sap/ui/comp/library"
const ValueHelpDialog = sapUiCompLib.valuehelpdialog.ValueHelpDialog; // control from module "sap/ui/comp/valuehelpdialog/ValueHelpDialog"
return Controller.extend("my.controller.MyController", {
onSomething: function () {
const vhDialog = new ValueHelpDialog(/*...*/);
// ...
},
// ...
});
});
Logs
Solution
Declare the missing dependency to "sap/ui/comp/valuehelpdialog/ValueHelpDialog".
No need to declare transitive dependencies that were also logged ( : ...).
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/comp/library",
"sap/ui/comp/valuehelpdialog/ValueHelpDialog" // <-- Declare the module dependency
], function(Controller, sapUiCompLib, ValueHelpDialog) {
// ...
const ValueHelpRangeOperation = sapUiCompLib.valuehelpdialog.ValueHelpRangeOperation; // enum from module "sap/ui/comp/library"
// const ValueHelpDialog = sapUiCompLib.valuehelpdialog.ValueHelpDialog; <-- Remove
return Controller.extend("my.controller.MyController", {
onSomething: function () {
const vhDialog = new ValueHelpDialog(/*...*/);
// ...
},
// ...
});
});
Module name to declare as dependency can be seen in the UI5 API reference:

Why is my Workbox GenerateSW showing my offline page while connected?

I'm trying to setup my offline page using Workbox GenerateSW() and running into an issue where on the first load after I clear site data and hard refresh displays my homepage, but on subsequent loads I am getting the offline page I set up even though I'm online. I have a multi page PHP app that has the assets served up by a CDN. I run the GenerateSW() task in a JS file called by an npm node script.
Here is my GenerateSW() code...
// Pull in .env file values...
const dotEnv = require('dotenv').config({ path: '/var/www/my-project/sites/www/.env' });
if (dotEnv.error) {
throw dotEnv.error
}
const {generateSW} = require('workbox-build');
// Used to break cache on generate of new SW where file is composed of multiple pieces that can't be watched.
const genRanHex = (size = 24) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
const mode = 'development';
generateSW({
swDest: './sites/www/public/service-worker.js',
skipWaiting: true,
clientsClaim: true,
cleanupOutdatedCaches: true,
cacheId: genRanHex(),
mode: mode,
navigateFallback: '/offline',
offlineGoogleAnalytics: mode === 'production',
globDirectory: './sites/assets/public',
globPatterns: [
'img/shell/**/*.{svg,png}',
'dist/**/*.{js,css}',
'manifest.json'
],
modifyURLPrefix: {
'dist/': `${dotEnv.parsed.APPLICATION_ASSETS_CDN}/dist/`,
'img/shell/': `${dotEnv.parsed.APPLICATION_ASSETS_CDN}/img/shell/`,
},
ignoreURLParametersMatching: [/v/],
additionalManifestEntries: [
{
"url": "/offline",
"revision": genRanHex()
}
],
runtimeCaching: []
}).then(({count, size}) => {
console.log(`Generated service worker, which will precache ${count} files, totaling ${size} bytes.`);
}).catch(console.error);
navigateFallback is not actually offline page. From workbox docs:
If specified, all navigation requests for URLs that aren't precached will be fulfilled with the HTML at the URL provided. You must pass in the URL of an HTML document that is listed in your precache manifest. This is meant to be used in a Single Page App scenario, in which you want all navigations to use common App Shell HTML.
For offline page, this question might help.
So the accepted answer was correct in my misuse of navigateFallback which I was trying to use as an offline fallback for non cached routes. After some digging and tinkering, I found the correct way to go about it. The important part that I missed or was not documented well enough on Workbox is that the offline fallback happens at the runtimeCache level...
// Pull in .env file values...
const dotEnv = require('dotenv').config({ path: '/var/www/my-project/sites/www/.env' });
if (dotEnv.error) {
throw dotEnv.error
}
const {generateSW} = require('workbox-build');
// Used to break cache on generate of new SW where file is composed of multiple pieces that can't be watched.
const genRanHex = (size = 24) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
const mode = 'development';
generateSW({
swDest: './sites/www/public/service-worker.js',
skipWaiting: true,
clientsClaim: true,
cleanupOutdatedCaches: true,
cacheId: genRanHex(),
mode: mode,
offlineGoogleAnalytics: mode === 'production',
globDirectory: './sites/assets/public',
globPatterns: [
'img/shell/**/*.{svg,png}',
'dist/**/*.{js,css}',
'manifest.json'
],
modifyURLPrefix: {
'dist/': `${dotEnv.parsed.APPLICATION_ASSETS_CDN}/dist/`,
'img/shell/': `${dotEnv.parsed.APPLICATION_ASSETS_CDN}/img/shell/`,
},
ignoreURLParametersMatching: [/v/],
additionalManifestEntries: [
{
"url": "/offline",
"revision": genRanHex()
}
],
runtimeCaching: [
{
urlPattern: /^https:\/\/([\w+\.\-]+www\.mysite\.tv)(|\/.*)$/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'core',
precacheFallback: {
fallbackURL: '/offline' // THIS IS THE KEY
}
}
}
]
}).then(({count, size}) => {
console.log(`Generated service worker, which will precache ${count} files, totaling ${size} bytes.`);
}).catch(console.error);

Workbox StaleWhileRevalidate and offline fallback

Is it possible to return an offline fallback for StaleWhileRevalidate strategie with Workbox ?
const urlHandler = new StaleWhileRevalidate({
cacheName: 'routes',
plugins,
});
registerRoute(
({ request }) => request.mode === 'navigate',
({ event }) =>
urlHandler.handle({ event }).catch(() => caches.match(FALLBACK_HTML_URL)),
);
This code work only if the request is on cache .. but for new URL not cached (but with network), it show directly the Offline fallback :/
Anyone have already test this usecase ?
In Workbox v6+, the cleanest way to do this would be to use a handlerDidError plugin:
import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
registerRoute(
({request}) => request.mode === 'navigate',
new StaleWhileRevalidate({
cacheName: 'routes',
plugins: [
{handlerDidError: () => caches.match(FALLBACK_HTML_URL)},
// Add any other plugins here.
],
})
);

where is the real quasar ssr express server?

I am building a quasar and vue.js app and I want to add a MongoDB API with an express server, there that /ssr-src/ dir where there is an index.js file with basic express app routing:
/*
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
* the ES6 features that are supported by your Node version. https://node.green/
*
* WARNING!
* If you import anything from node_modules, then make sure that the package is specified
* in package.json > dependencies and NOT in devDependencies
*
* Note: This file is used only for PRODUCTION. It is not picked up while in dev mode.
* If you are looking to add common DEV & PROD logic to the express app, then use
* "src-ssr/extension.js"
*/
console.log("got here!") // I added
const express = require("express"),
compression = require("compression");
const ssr = require("quasar-ssr"),
extension = require("./extension"),
app = express(),
port = process.env.PORT || 3000;
const serve = (path, cache) =>
express.static(ssr.resolveWWW(path), {
maxAge: cache ? 1000 * 60 * 60 * 24 * 30 : 0
});
// gzip
app.use(compression({ threshold: 0 }));
// serve this with no cache, if built with PWA:
if (ssr.settings.pwa) {
app.use("/service-worker.js", serve("service-worker.js"));
}
// serve "www" folder
app.use("/", serve(".", true));
// we extend the custom common dev & prod parts here
extension.extendApp({ app, ssr });
// this should be last get(), rendering with SSR
app.get("*", (req, res) => {
res.setHeader("Content-Type", "text/html");
// SECURITY HEADERS
// read more about headers here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
// the following headers help protect your site from common XSS attacks in browsers that respect headers
// you will probably want to use .env variables to drop in appropriate URLs below,
// and potentially look here for inspiration:
// https://ponyfoo.com/articles/content-security-policy-in-express-apps
// https://developer.mozilla.org/en-us/docs/Web/HTTP/Headers/X-Frame-Options
// res.setHeader('X-frame-options', 'SAMEORIGIN') // one of DENY | SAMEORIGIN | ALLOW-FROM https://example.com
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
// res.setHeader('X-XSS-Protection', 1)
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
// res.setHeader('X-Content-Type-Options', 'nosniff')
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
// res.setHeader('Access-Control-Allow-Origin', '*') // one of '*', '<origin>' where origin is one SINGLE origin
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control
// res.setHeader('X-DNS-Prefetch-Control', 'off') // may be slower, but stops some leaks
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
// res.setHeader('Content-Security-Policy', 'default-src https:')
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox
// res.setHeader('Content-Security-Policy', 'sandbox') // this will lockdown your server!!!
// here are a few that you might like to consider adding to your CSP
// object-src, media-src, script-src, frame-src, unsafe-inline
ssr.renderToString({ req, res }, (err, html) => {
if (err) {
if (err.url) {
res.redirect(err.url);
} else if (err.code === 404) {
console.log(404,'!!')
res.status(404).send("404 | Page Not Found foo bar"); // I added foo bar
} else {
// Render Error Page or Redirect
res.status(500).send("500 | Internal Server Error");
if (ssr.settings.debug) {
console.error(`500 on ${req.url}`);
console.error(err);
console.error(err.stack);
}
}
} else {
res.send(html);
}
});
});
app.listen(port, () => {
console.log(`Server listening at port ${port}`);
});
but none of my logs or changings are happening when I run $ queser dev -m ssr
also the Server listening at port ${port} is not showing.
need your help!
quasar version 1.0.7
debian 10
src-ssr/index.js
From the comment notes, it seems like it is for Production only and will not visible in dev mode.
"Note: This file is used only for PRODUCTION"
You may want to use the src-ssr/extension.js instead.

Using react-hot-loader with custom babel preset

My app doesn't support older browsers, and I like to trim down the set of Babel transforms to make the code easier to debug (so that the code in the debugger looks like more the original source).
However, when I migrate to react-hot-loader 3, this no longer works. That is, I can get RHL 3 to work with the standard es2015 preset, but not with my custom set of transforms. What happens is that the react components are rendered but never mounted, and won't respond to any events.
The set of transforms I am trying to use is:
var babel_plugins = [
'transform-runtime',
'transform-object-rest-spread',
// Transforms needed for modern browsers only
'babel-plugin-check-es2015-constants',
'babel-plugin-transform-es2015-block-scoping',
'babel-plugin-transform-es2015-function-name',
'babel-plugin-transform-es2015-parameters',
'babel-plugin-transform-es2015-destructuring',
// No longer needed with Webpack 2
// 'babel-plugin-transform-es2015-modules-commonjs',
'react-hot-loader/babel',
];
In response to the comments, here's more information:
Here's how I'm using the AppContainer:
export default (
<AppContainer>
<Router history={browserHistory}>
(My routes here...)
</Router>
</AppContainer>
);
And here's my dev server setup:
// Adjust the config for hot reloading.
config.entry = {
main: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://127.0.0.1:8000', // WebpackDevServer host and port
'webpack/hot/only-dev-server', // "only" prevents reload on syntax errors
'./src/main.js', // Your appʼs entry point
],
frame: './src/frame_main.js', // Entry point for popup tab
};
config.plugins.push(new webpack.HotModuleReplacementPlugin());
const compiler = webpack(config);
const server = new WebpackDevServer(compiler, {
contentBase: path.resolve(__dirname, '../builds/'),
historyApiFallback: true,
stats: 'errors-only',
hot: true,
});
server.listen(8000, '127.0.0.1', () => {});
Here's the relevant portion of my webpack config:
test: /\.jsx?$/,
include: __dirname + '/src',
exclude: __dirname + '/src/libs',
use: [
{
loader: 'babel-loader',
options: {
plugins: babel_plugins,
presets: babel_presets
},
},
{
loader: 'eslint-loader',
},
]