Configure the sw-precache WebPack plugin to load a server rendered page as the navigateFallback route - reactjs

consider the following scenario:
My express server dynamically generates HTML for the "/" route of my single page application.
I would like to re-serve this same generated HTML as the service worker navigateFallback when the user is offline.
I'm using https://www.npmjs.com/package/sw-precache-webpack-plugin in my webpack configuration.
If I generate an index.html via html-webpack-plugin, say, and set index.html as my navigateFallback file, that generated file gets served correctly by the service worker.
However, I can see no way to cause the on-the-fly rendered index html (what the live server returns for the "/" path) to be cached and used as the offline html.

Use dynamicUrlToDependencies option of Service Worker Precache to cache your route url and its dependencies. Then set navigateFallback to '/' and navigateFallbackWhitelist to a regex matching your sublinks logic.
Take this configuration : (Add const glob = require('glob') atop of your webpack config)
new SWPrecacheWebpackPlugin({
cacheId: 'my-project',
filename: 'offline.js',
maximumFileSizeToCacheInBytes: 4194304,
dynamicUrlToDependencies: {
'/': [
...glob.sync(`[name].js`),
...glob.sync(`[name].css`)
]
},
navigateFallback: '/',
navigateFallbackWhitelist: [/^\/page\//],
staticFileGlobsIgnorePatterns: [/\.map$/],
minify: false, //set to "true" when going on production
runtimeCaching: [{
urlPattern: /^http:\/\/localhost:2000\/api/,
// Use network first and cache as a fallback
handler: 'networkFirst'
}],
})

That use case should be supported. I have an example of something similar using the underlying sw-precache library, and I believe the syntax should be equivalent when using the Webpack wrapper.
In this case, /shell is the URL used for dynamically generated content from the server, constituting the App Shell, but it sounds like your use case is similar, with / instead of /shell.
{
// Define the dependencies for the server-rendered /shell URL,
// so that it's kept up to date.
dynamicUrlToDependencies: {
'/shell': [
...glob.sync(`${BUILD_DIR}/rev/js/**/*.js`),
...glob.sync(`${BUILD_DIR}/rev/styles/all*.css`),
`${SRC_DIR}/views/index.handlebars`
]
},
// Brute force server worker routing:
// Tell the service worker to use /shell for all navigations.
// E.g. A request for /guides/12345 will be fulfilled with /shell
navigateFallback: '/shell',
// Other config goes here...
}

Related

Stop watching folder changes in reactjs

i'm working on a react project where user can uploads files,and the problem that i'am facing is when i upload a file to the server and save that file into folder called uploads which lives in the public folder,then when this process finished the page refreshed that's because the app watching for any changes and refresh the page
and i know that i can stop this by edit the webpackdevserver.config File but i don't know how to do that.
webpackdevserver.config:
// #remove-on-eject-begin
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// #remove-on-eject-end
'use strict';
const fs = require('fs');
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const ignoredFiles = require('react-dev-utils/ignoredFiles');
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
const paths = require('./paths');
const getHttpsConfig = require('./getHttpsConfig');
const host = process.env.HOST || '0.0.0.0';
const sockHost = process.env.WDS_SOCKET_HOST;
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node'
const sockPort = process.env.WDS_SOCKET_PORT;
module.exports = function(proxy, allowedHost) {
return {
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
// websites from potentially accessing local content through DNS rebinding:
// https://github.com/webpack/webpack-dev-server/issues/887
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
// However, it made several existing use cases such as development in cloud
// environment or subdomains in development significantly more complicated:
// https://github.com/facebook/create-react-app/issues/2271
// https://github.com/facebook/create-react-app/issues/2233
// While we're investigating better solutions, for now we will take a
// compromise. Since our WDS configuration only serves files in the `public`
// folder we won't consider accessing them a vulnerability. However, if you
// use the `proxy` feature, it gets more dangerous because it can expose
// remote code execution vulnerabilities in backends like Django and Rails.
// So we will disable the host check normally, but enable it if you have
// specified the `proxy` setting. Finally, we let you override it if you
// really know what you're doing with a special environment variable.
disableHostCheck:
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
// Enable gzip compression of generated files.
compress: true,
// Silence WebpackDevServer's own logs since they're generally not useful.
// It will still show compile warnings and errors with this setting.
clientLogLevel: 'none',
// By default WebpackDevServer serves physical files from current directory
// in addition to all the virtual build products that it serves from memory.
// This is confusing because those files won’t automatically be available in
// production build folder unless we copy them. However, copying the whole
// project directory is dangerous because we may expose sensitive files.
// Instead, we establish a convention that only files in `public` directory
// get served. Our build script will copy `public` into the `build` folder.
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
// Note that we only recommend to use `public` folder as an escape hatch
// for files like `favicon.ico`, `manifest.json`, and libraries that are
// for some reason broken when imported through webpack. If you just want to
// use an image, put it in `src` and `import` it from JavaScript instead.
contentBase: paths.appPublic,
contentBasePublicPath: paths.publicUrlOrPath,
// By default files from `contentBase` will not trigger a page reload.
watchContentBase: true,
// Enable hot reloading server. It will provide WDS_SOCKET_PATH endpoint
// for the WebpackDevServer client so it can learn when the files were
// updated. The WebpackDevServer client is included as an entry point
// in the webpack development configuration. Note that only changes
// to CSS are currently hot reloaded. JS changes will refresh the browser.
hot: true,
// Use 'ws' instead of 'sockjs-node' on server since we're using native
// websockets in `webpackHotDevClient`.
transportMode: 'ws',
// Prevent a WS client from getting injected as we're already including
// `webpackHotDevClient`.
injectClient: false,
// Enable custom sockjs pathname for websocket connection to hot reloading server.
// Enable custom sockjs hostname, pathname and port for websocket connection
// to hot reloading server.
sockHost,
sockPath,
sockPort,
// It is important to tell WebpackDevServer to use the same "publicPath" path as
// we specified in the webpack config. When homepage is '.', default to serving
// from the root.
// remove last slash so user can land on `/test` instead of `/test/`
publicPath: paths.publicUrlOrPath.slice(0, -1),
// WebpackDevServer is noisy by default so we emit custom message instead
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
quiet: true,
// Reportedly, this avoids CPU overload on some systems.
// https://github.com/facebook/create-react-app/issues/293
// src/node_modules is not ignored to support absolute imports
// https://github.com/facebook/create-react-app/issues/1065
watchOptions: {
ignored: ignoredFiles(paths.appSrc),
},
https: getHttpsConfig(),
host,
overlay: false,
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebook/create-react-app/issues/387.
disableDotRule: true,
index: paths.publicUrlOrPath,
},
public: allowedHost,
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
proxy,
before(app, server) {
// Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
// middlewares before `redirectServedPath` otherwise will not have any effect
// This lets us fetch source contents from webpack for the error overlay
app.use(evalSourceMapMiddleware(server));
// This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware());
if (fs.existsSync(paths.proxySetup)) {
// This registers user provided middleware for proxy reasons
require(paths.proxySetup)(app);
}
},
after(app) {
// Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
app.use(redirectServedPath(paths.publicUrlOrPath));
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if
// it used the same host and port.
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
},
};
};
and here where is store the files uploaded:
structureAPP
So my question is Like so:
How do i can edit webpackdevserver.config File to stop watching uploads File ?
i have been searching for this problem for two days and didn't fix it.
thanks in advance.
watchOptions: {
ignored: ignoredFiles(paths.appSrc),
},
This is indeed where ignored path are listed but ignoredFiles(paths.appSrc) is not the src folder. If you look higher you see ignoredFiles.js comes from the react-dev-utils module.
const ignoredFiles = require('react-dev-utils/ignoredFiles');
With react create app, the default ignoredFiles.js returns the node_modules folder which speeds up things by avoiding the watcher to go through all modules. Now it is possible to add more folders as watchOptions.ignored accepts an array So you can add your uploads path directly in the watchOptions.
watchOptions: {
ignored: [ignoredFiles(paths.appSrc), yourUploadPath],
},

sw-precache with angularJs application not caching

I am newbie to service worker concept so forgive me if I am overlooking something from documentation. I have an angular application already running in production and I am trying to introduce service worker using sw-precache.
To start with I am trying to precache all images/fonts and couple of js files and see if it works, so my precache config is like this -
{
"cacheId": "static-cache",
"importScripts": [
"sw-toolbox.js"
],
"stripPrefix": "dist/",
"verbose": true,
"staticFileGlobs": [
"dist/img/**.*",
"dist/javascripts/require.js",
"dist/stylesheets/**.*",
"dist/webfonts/**.{ttf, eot, woff}",
"sw-toolbox.js",
"service-worker.js"
]
}
Now I can see service worker registered and installed properly and cache storage shows all the urls with _sw-precache hashes.
But when I load the application and see in network tab all static content are still served from memory/disk, not from service worker and I am unable to debug why is it so. Am I missing something here -
UPDATE:
More information: I had wrong configurations since I have dynamic url and server side rendered html. Server side it's test.jsp which is giving me initial shell.
For now I have removed all other static files from cache and kept only show.css
So update config now is -
{
"importScripts": [
"sw-toolbox.js"
],
"stripPrefix": "dist/",
"verbose": true,
"staticFileGlobs": [
"dist/stylesheets/show.css"
],
"dynamicUrlToDependencies": {
"/developers": ["dist/stylesheets/show.css"]
},
"navigateFallback": "/developers"
}
Web root folder is named differently and it is -
- dashboard
-- img
-- javascripts
-- service-worker.js
-- sw-toolbox.js
- test.jsp
And I see /developers url as an entry in storage cache, but still it's not served from service worker for next refresh. I have tried all my energy to fix this, but I desperately need some clue here, what's missing in here. TIA.
Let me know if need more info.
It seems that whitespaces in your file extension list are not allowed. Your definition for webfonts should be:
"dist/webfonts/**.{ttf,eot,woff}",
I cloned the sw-precache repo and added a unit test where I compared two generated files with two diffrent staticFileGlobs, one with whitespace and one without.
it('should handle multiple file extensions', function(done) {
var config = {
logger: NOOP,
staticFileGlobs: [
'test/data/one/*.{txt,rmd}'
],
stripPrefix: 'test'
};
var configPrime = {
logger: NOOP,
staticFileGlobs: [
'test/data/one/*.{txt, rmd}'
],
};
generate(config, function(error, responseString) {
assert.ifError(error);
generate(configPrime, function(error, responseStringPrime) {
assert.ifError(error);
console.log('responseStringPrime',responseString);
assert.strictEqual(responseString, responseStringPrime);
done();
});
});
});
and it failed. The second config didn't include the .rmd file:
-var precacheConfig = [["/data/one/a.rmd","0cc175b9c0f1b6a831c399e269772661"],["/data/one/a.txt","933222b19ff3e7ea5f65517ea1f7d57e"],["/data/one/c.txt","fa1f726044eed39debea9998ab700388"]];
versus
+var precacheConfig = [["test/data/one/a.txt","933222b19ff3e7ea5f65517ea1f7d57e"],["test/data/one/c.txt","fa1f726044eed39debea9998ab700388"]];

Parsing application.yml in angularjs

I have a application.yml file in my application
spring:
profiles:
active: default,dev
app:
properties:
lucene:
indexInfoFile: ${spring.jpa.properties.hibernate.search.default.indexBase}/index.properties
reindex: false
storage:
home: ${user.home}/xxx
basePath: ${app.properties.storage.home}/uploads/
staticFilesPrefix: /files/
appUrl: /app/
spring:
profiles: dev
http:
multipart:
max-file-size: 3MB
max-request-Size: 3MB
Now in my controller, I am trying to get the data from yml file and the code for the same is
$http.get('/resources/application.yml').then(function (response) {
console.log('entire data is ', response.data);
console.log('basePath is ', response.data.basePath);
});
Entire Data is printing perfectly ( the whole yml file is getting printed) but when ever I am trying to print a particular property like basePath, max-file-size etc I am getting "undefined error".
My question is how to get a particular property to be printed on the console.
I would not recommend to access the yml file directly in Angular.
The format is difficult to parse (hence your question) and you sooner or later you may not want to expose all your confguration details.
Instead create a rest controller in spring mapped to something like /config
Let spring inject all the configuration values you need using #Value and return a Map or a simple PoJo with exactly the attributes you need.
Spring will convert this to JSON which you can easily be consumed in Angular.

Symfony2: allow all unmatched routes to be accessed anonymously

I have Symfony2 application separated into 2 bundles: BackendBundle for API and FrontendBundle for AngularJS "client". Everything works under firewall.
BackendBundle has entities, handles API routes; FrontendBundle has Angular views, routing etc. and has only one controller with wildcard:
class AngularController extends Controller {
/**
* #Route("/{route}", name="angular_index_all_unmatched_routes", requirements={"route" = ".*"})
* #Template("FrontendBundle::index.html.twig")
*/
public function angularIndexAction($route) {
return ['route' => $route];
}
}
FrontendBundle routing is defined as last resource in app/config/routing.yml, to be invoked only if any other route was not matched. Thanks to that, it can handle Angular HTML5-mode routes if they're accessed directly (for example copy-paste) - and it works ok.
What I want to do, is define firewall and/or access control in way that all those unmatched routes (handled by AngularController::angularIndexAction()) could be accessible by anonymous user.
Why? I want to open some API routes (via frontend proxy) to be accessible by non-users (for example confirmation URLs sent by email, with some message to user).
I don't want to hardcode access control list for every anonymous "Angular" route, I would like to do it only for API routes. At the end, those unmatched routes should open Angular's index which should know if user is logged in (for displaying full or simplified layout) and should handle Angular routes and display some kind of "Access denied" message if request failed (there is Symfony listener and Angular's $provide interceptor for that).
Any suggestions?
Edit: #Security annotation on AngularController::angularIndexAction() does not work, it still redirects to firewall entry point.
Edit2: Here is fragment of security.yml
firewalls:
unsecured:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
anonymous: true
secured:
pattern: '^.*$'
form_login:
login_path: /our-provider/login
check_path: /our-provider/callback/
anonymous: true
entry_point: our_provider.entry_point
access_control:
- { path: '^/our-provider/(login(/[a-zA-Z]+)?|logout|redirect|callback)', roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: '^/', roles: ROLE_USER }
I know that { path: '^/', roles: ROLE_USER } will redirect all routes to login page if user is not logged in. I assumed it's obvious and did not mentioned it. What I want is force ROLE_USER for matched routes and let IS_AUTHENTICATED_ANONYMOUSLY for those unmatched, without explicitely defining each frontend "proxy-route". In my case there is not 404 Symfony page, because everything goes to angular_index_all_unmatched_routes route and there Angular routing definition decides if there is something to handle or not.
I haven't tried this, and I cannot begin to guess your existing security/route setup in security.yml but I guess you could whitelist the method with IS_AUTHENTICATED_ANONYMOUSLY. From the Symfony docs:
All users (even anonymous ones) have this - this is useful when whitelisting URLs to guarantee access - some details are in How Does the Security access_control Work?.
So, for example, if you were using the #Security annotation you could do something like (not tested):
class AngularController extends Controller {
/**
* #Route("/{route}", name="route", requirements={"route" = ".*"})
* #Template("FrontendBundle::index.html.twig")
* #Security("has_role('IS_AUTHENTICATED_ANONYMOUSLY')")
*/
public function angularIndexAction($route) {
return ['route' => $route];
}
}
More on the #Security annotation here.
Hope this helps :)
Edit
All that said, when you define/restrict your routes under access_control in security.yml, the matching process stops on the first match. I assume that you have some role-restricted paths, which you should define explicitly - and put them first, so if they match the process stops.
Otherwise, you should be able to add a catch-all route, enforced by role IS_AUTHENTICATED_ANONYMOUSLY. Since the path definition of a route is a regex, something like ^/ should catch anything that is not explicitly defined. Just make sure and place it after your restricted route definitions.
You would not need for the #Security annotation in this case.
Edit 2
I tried mocking this out using a clean instance and HTTP BasicAuth but what I was trying to achieve was the following, which I understand as similar to your use case:
Create a backend controller with routes / and /api/ and trigger a HTTP BasicAuth authentication popup
Create a frontend controller with route /{route} that would match everything else and authenticate anonymously.
My firewall and access_control configuration looks like this:
security:
encoders:
# encoder config here
providers:
# provider config here
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured:
anonymous: ~
http_basic: ~
access_control:
- { path: ^/$, roles: ROLE_USER }
- { path: ^/api/, roles: ROLE_USER }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Access control paths are regexes, so ^/$ and ^/ are not the same. The former will only match exactly to route /. The latter will match any route that begins with /; e.g: /home, /products, /contact etc.
Indeed, the latter will match and anonymously authenticate /api, but it will not match /api/, or /api/1 etc. as these are explicitly defined and restricted to ROLE_USER.
So the general idea is to explicitly and (if possible) exactly match the routes you want to restrict, and declare those first. The last declaration ^/ should openly catch any other route that falls through.

What's the best practice to use named Route in AppEngine?

In the app.yaml file, I have put 2 lines to specify the url mapping:
url: /blog/.*
script: blog.app
url: /
script: home.app
the problem is I can't use the "uri_for" function to generate a url for blog module in home.py, case there's no Route added in home moudle:
here is the code in home module:
app = webapp2.WSGIApplication([
webapp2.Route(r'/', handler=HomeHandler, name='home')
], debug = SITE_CONFIG['is_debug'], config=SITE_CONFIG)
and code in blog.py:
app = webapp2.WSGIApplication([
webapp2.Route(r'/blog/<blog_id:\d+>', handler=BlogHandler, name="blog")
], debug = SITE_CONFIG['is_debug'], config=SITE_CONFIG)
so, if I have code like this: {{ uri_for('blog', blog_id=blabla) }} in home.html, it can't work.
You should consolidate those routes into one app.
app = webapp2.WSGIApplication([
webapp2.Route(r'/', handler=HomeHandler, name='home'),
webapp2.Route(r'/blog/<blog_id:\d+>', handler=BlogHandler, name="blog")
], debug = SITE_CONFIG['is_debug'], config=SITE_CONFIG)
and actually those are only the view blog post routes.
If you wanted to do a full CRUD app, you might need to add some more.
app = webapp2.WSGIApplication([
webapp2.Route(r'/admin/blog', handler='admin.AdminBlogHandler:list, name="admin.blog.list"),
webapp2.Route(r'/admin/blog/new', handler='admin.AdminBlogHandler:new', name='admin.blog.edit'),
webapp2.Route(r'/admin/blog/<id:[^/]+>/edit', handler='admin.AdminBlogHandler:edit', name='admin.blog.edit'),
webapp2.Route(r'/admin/blog/<id:[^/]+>', handler='admin.AdminBlogHandler:view', name='admin.blog.view')
], debug = SITE_CONFIG['is_debug'], config=SITE_CONFIG)
Note for these examples:
1) you prefix a name to load the handlers from a different file (admin.AdminBlogHandler will look in 'admin.py' for 'class AdminBlogHandler'
2) you specify the method to run after the handler name, after the colon.
3) in each method I am creating functionality for get and post, so there are not discrete RESTful URLs for edit and update.

Resources