Caching external resources with sw-precache - progressive-web-apps

I'm trying to get sw-precache to pre-cache external CDN resources, but the generated service-worker.js doesn't contain the CDN url's in the precacheConfig array.
This is what I have in my gulpfile:
staticFileGlobs: [
'http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css',
'client/assets/**/*.{js,html,css,png,jpg,gif,svg,eot,ttf,woff,ico}'
]
The files inside my local client/assets folder are added to the precacheConfig array, but the external font-awesome css isn't. Is there a way to achieve this?

sw-precache can only precache and keep up to date local assets, like those that match the client/assets/**/*... pattern you're using. It's not meant to work with remote assets that are accessed via CDN.
You have a couple of approaches:
Use npm (or the package manager or your choice) to download a local copy of the resource (i.e. font-awesome) and then deploy that third party resource alongside your first-party assets. If the third-party code is picked up by a pattern you pass to staticFileGlobs then it can be precached and versioned just like anything else local.
Use runtime caching to handle the resource on the CDN. Since the URL for your specific asset includes a 4.0.3 versioning string, it's safe to assume that the underlying contents will never change, and a cacheFirst strategy is probably safe.
You can modify your sw-precache configuration to look like the following:
{
staticFileGlobs: [
'client/assets/**/*.{js,html,css,png,jpg,gif,svg,eot,ttf,woff,ico}'
],
runtimeCaching: [{
urlPattern: /^https:\/\/netdna\.bootstrapcdn\.com\//,
handler: 'cacheFirst'
}],
// ...any other config options...
}
That configuration is broad enough to pick up anything served off that CDN, cache it, and then serve it cache-first once in subsequent visits.
Please note that your example used an http: protocol for your CDN's URL, and you'll need to use https: to obtain a response that plays nicely with service worker caching.

Related

Axios BaseURL not working on certain hosts

Below is how I configured Axios based on the example given on Nuxt.js' website:
.env:
BASE_URL=https://path.to.endpoint
nuxt.config.js:
publicRuntimeConfig: {
axios: {
baseURL: process.env.BASE_URL
}
},
On page load I make this call:
this.$axios.get(`/endpoint`)
Once I deploy my app as a static site it works both on my personal host and on GitHub pages. But on my employer's host, the path to endpoint specified in .env becomes https://localhost:3000 so the API call fails.
Why is the most likely cause of this behaviour?
Alright, from the comments, it looks like you configuration is totally fine from what you've provided and that the team on the other side does have an incorrect setup of the environment variables.
You need to ask where they do host your code and what are the actual values of their env variables. Actually, you will probably need to give it to them since they (usually) cannot guess it by themselves.
Human communication is the next step. ^^

Google CDN Connection to CDN create nosuch Key Errors

i uploaded images to google storage bucket and i am no trying to set the CDN using the load balancer to work.
Storage Status :
Bucket Permissions : Storage Object Viewer - Reader assign to allUsers ,
Storage Legacy Bucket Reader assign To allUsers
File Status :
Share Public is set and there is a public link
Load Balancer:
Set to path /creatives/* on the host name
but i always get this msg:
<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
</Error>
what i notice is as soon as i build the path of /creatives/* there is another path build /* direct to the backend service of the auto scale group
am i missing here any settings?
So as i discovered Google CDN + Google http Load balancer works different from other CDN.
with a regular CDN you can direct the origin to you bucket HTTP address and work on the / structure.
for example :
Google CDN Bucket URL:
googleapi.storage.com/my-bucket
Folder structure:
/1/1.jpg
Normal CDN origin will be pointing to googleapi.storage.com/my-bucket
and you will get a new service endpoint for the CDN like:
my-bucket.fastly.cdonservice.com
and this call will work:
my-bucket.fastly.cdonservice.com/1/1.jpg
but on google cloud what you are setting up is a path that is connected to the CDN service that you created on the backend part.
So this is the big difference, lets assume that you created this path rule.
host: www.googlecdnnonexplainedfeautres.com
path: /images/*
service: yourbackendservice (connected to the bucket you want to cache)
so you might assume this should work:
www.googlecdnnonexplainedfeautres.com/images/1/1.jpg.
but NO ..
after digging the logs you will find a 404 on the bucket because google will go and search this path on the bucket:
googleapi.storage.com/my-bucket/images/1/1.jpg.
wait , where did the images came ? i thought its a hook. no google take this as a static website root ( the thing you can check on and off on S3) so here its mandatory.
so how this should work ?
modify the folder structure to be like this :
Google CDN Bucket URL:
googleapi.storage.com/my-bucket
Folder structure:
images/1/1.jpg
and now you are good.
This link should work now :
www.googlecdnnonexplainedfeautres.com/images/1/1.jpg.
so before you commit a bucket to be used as CDN source for google just add another top folder that match with the path you set on the LB.
and of course .. permissions , allUsers , read and etc..
EnjoY!
You can use URL Rewrites to solve this problem.
This may not have existed back in 2017 but there's an option at least as of 2021.
Explanation:
The lb's default behavior is to pass the entire path after the host to Cloud Storage. This may seem incorrect, but it's a sane default (How is the load balancer supposed to know what part of the path you want to include or exclude?).
I was facing the same issue.
I connected mydomain.com/static/* to my cloud storage bucket static-assets.
Upon visiting mydomain.com/static/static-asset.jpg the load balancer would request cloud storage for an object with key static-assets/**static**/static-asset.jpg.
Since the object's actual key is static-assets/static-asset.jpg, this would return a NoSuchKey response.
The fix was the rewrite the path prefix of /static to /.
One way to configure this is through the Cloud Console Load Balancer UI--we can add an advanced path rule to rewrite the prefix.
Please note:
Important: The rewrite is prepended to the path as is. Full path
rewrites are not supported. HTTP(S) Load Balancing only implements
path prefix rewrites. For example, you can rewrite:
host.name/path1/resource1 to host.name/path2/resource1. You cannot
rewrite host.name/path1/resource1 to host.name/path1/resource2.
Read about URL Rewrites here.

Setting :deploy_to from server config in Capistrano3

In my Capistrano 3 deployment, I would like to set the set :deploy_to, -> { "/srv/www/#{fetch(:application)}" } so the :deploy_to is different for each server it deploys to.
In my staging.rb file I have:
server 'dev.myserver.com', user: 'deploy', roles: %w{web app db}, install_path: 'mycustom/path'
server 'dev.myserver2.com', user: 'deploy', roles: %w{web app db}, install_path: 'mycustom/other/path'
My question is: would it possible to use the "install_path" I defined, in my :deploy_to? If that's possible, how would you do it?
Finally, after looking around, I came onto an issue from one of the developer of Capistrano, stating specifically that it can't be done
Quote from the Github issue:
Not possible, sorry. fetch() (as is documented widely) reads values
set by set(), the only reason to use set() and fetch() over regular
ruby variables is to provide a consistent API between plugins and
extensions, and because set() can take a Proc to be resolved later.
The variables you are setting in the host object via the server()
command belong to an individual host, some of them, user, roles, etc
have special meanings. For more information see
https://github.com/capistrano/sshkit/blob/master/EXAMPLES.md#do-something-different-on-one-host-or-another-depending-on-a-host-property.
If you specifically need to deploy to a different directory on each
machine you probably should not be using the built-in tasks (they
don't fit your needs), and rather copy the deploy.rake from the Gem
into your own project, and modify it as you need. Which in this case
might be to not take fetch(:deploy_to), but to read that from a host
property.
You could try to do something where before doing anything that relies
on calling fetch(:deploy_to), you set() it using the value from
host.someproperty but I'm pretty sure that'll break in exciting and
interesting ways.

Protecting click once web deployed installations

I have a link on my website to the standard publish page generated by Visual Studio. My concern is that if anybody finds out the URL to that page, they can download my software. Sure, I could password protect the page with the link, but it still would not be protecting the download URL. Are there any ways to secure the click once upload? I have looked around, and it seems like I am stuck in this sense.
Public URL is a security issue in ClickOnce Deployment. However, there is a solution for your problem if your web server has windows and .NET installed. Tell me if you have one ? I will have to come up with another workaround for Linux web server in case you have that.
Brief
Firstly, a bit of information about ClickOnce deployment. When you deploy the application, the GET requests on the server made are (assuming WebDir is the publish directory on the server)
G-1. GET /WebDir/setup.exe (Initial download)
G-2. GET /WebDir/MyApp.Application (setup.exe -url request)
G-3. GET /WebDir/MyApp.Application (.application deployment provider URL request)
G-4. GET /WebDir/Application Files/MyApp_1_0_0_0/MyApp.exe.manifest (Application manifest request)
G-5. GET /WebDir/Application Files/MyApp_1_0_0_0/MyApp.exe.deployand other .deploy files ... (Application file requests)
Implementation
Now, the solution is to intercept these file requests on the server. On IIS, you can attach a custom HTTPHandler and handle the request. On Apache, you can redirect requests to a PHP code using .htaccess files. Apart from this, you will have to generate unique identifier uid for client instances downloaded from the server (can be your license key) and put that in the deployment provider URL query parameters.
Directory Structure
Create an "Application" folder inside your WebDir and restrict access to /WebDir/Application/. Rest everything can be there inside /WebDir/
File Requests
So here's what you do on a Apache web server hosted on a windows machine:
Create a custom download page or use the one created from publishing the application using Visual Studio (but you will have to edit it manually!). Let's assume that page is /WebDir/Download.php
After authenticating user from Download.php, you have to send setup.exe from your code (can do it with readfile() in PHP) to the user. However, the catch is bootstrapper (setup.exe) after installing will do a GET request [G-2]. Don't forget now, that you have to validate this file request. So basically you change the "setup.exe -url" property to include uid before returning the file. For eg: change it to /WebDir/uid/MyApp.Application [G-2]. You can use MsiStuff.exe to change the URL property for the bootstrapper.
Using a .htaccess file, rewrite [G-2] to /WebDir/Handler.php?user=uid. From Handler.php, you can check if it is a valid uid. If it is valid, you will have to include the uid in the deployment provider URL and "Dependent Assemblies Path" in deployment manifest so that if an upgrade request comes (It essentially requests the deployment manifest), you can validate the user there too. Add uid to query string parameters. For eg: change it to /WebDir/MyApp.application?user=uid [G-3]. Don't forget that you will have to resign the manifests once you modify them. Use Mage or write your own code to do that.
So finally, the GET requests on the server will be (assuming uid=1f3rd)
G-1. GET /WebDir/Download.phpAction: return setup.exe with the -url changed
G-2. GET /WebDir/Application/setup.exe/1f3rd/MyApp.ApplicationAction: redirect, validate user, change URL, re-sign and return file
G-3. GET /WebDir/Application/setup.exe/MyApp.Application?user=1f3rdAction: redirect, validate user and return file
G-4. GET /WebDir/Application/1f3rd/Application Files/MyApp_1_0_0_0/MyApp.exe.manifestAction: redirect, validate user and return file
G-5. GET /WebDir/Application/1f3rd/Application Files/MyApp_1_0_0_0/MyApp.exe.deployand other .deploy files ...Action: redirect, validate user and return file
Pros
Application is successfully deployed and upgraded only if all the requests have a valid uid in the URL present.
You can now identify different instances of application on client systems. You can track the update history, do a selective version upgrade/downgrade and much more !
Cons
You will need a windows server to implement the above since you need mage.exe | your-own-.NET-code-signing-application and Msistuff.exe.
You may have minor performance issues since you are performing validation on every file request. You can choose to skip validation on .manifest and .deploy file requests.
You will have to ensure proper security for companies certificate which will be present on the web server for signing (You can store it on the server local file-system if you have the full server to yourself. In that case, it is fine unless somebody breaks into machine itself !)
If you want me to make something clear or explain in detail, feel free to ask. In case you have suggestions for modification to the above, post that too.
I will write a detailed CodeProject article if I have spare time someday.

CherryPy : Accessing Global config

I'm working on a CherryPy application based on what I found on that BitBucket repository.
As in this example, there is two config files, server.cfg (aka "global") and app.cfg.
Both config files are loaded in the serve.py file :
# Update the global settings for the HTTP server and engine
cherrypy.config.update(os.path.join(self.conf_path, "server.cfg"))
# ...
# Our application
from webapp.app import Twiseless
webapp = Twiseless()
# Let's mount the application so that CherryPy can serve it
app = cherrypy.tree.mount(webapp, '/', os.path.join(self.conf_path, "app.cfg"))
Now, I'd like to add the Database configuration.
My first thought was to add it in the server.cfg (is this the best place? or should it be located in app.cfg ?).
But if I add the Database configuration in the server.cfg, I don't know how to access it.
Using :
cherrypy.request.app.config['Database']
Works only if the [Database] parameter is in the app.cfg.
I tried to print cherrypy.request.app.config, and it shows me only the values defined in app.cfg, nothing in server.cfg.
So I have two related question :
Is it best to put the database connection in the server.cfg or app.cfg file
How to access server.cfg configuration (aka global) in my code
Thanks for your help! :)
Put it in the app config. A good question to help you decide where to put such things is, "if I mounted an unrelated blog app at /blogs on the same server, would I want it to share that config?" If so, put it in server config. If not, put it in app config.
Note also that the global config isn't sectioned, so you can't stick a [Database] section in there anyway. Only the app config allows sections. If you wanted to stick database settings in the global config anyway, you'd have to consider config entry names like "database_port" instead. You would then access it directly by that name: cherrypy.config.get("database_port").