I created my app with npx create-nuxt-app, then added npm install #nuxtjs/pwa --save. I'm including a google font in index.html with:
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" rel="stylesheet" data-n-head="true">
I tested my app in offline mode in Chrome by clicking the "Offline" checkbox in the devtools/application tab, and reloading. Everything is cached except for the font.
I then added:
workbox: {
runtimeCaching: [
{
urlPattern: 'https://fonts.googleapis.com/.*',
handler: 'cacheFirst',
method: 'GET'
},
]
}
to the nuxt.config.js file but I can't get the font to be cached. I've tried a number of variations on the urlPattern.
Nuxt is generating a service worker for me, and it looks like this:
importScripts('/_nuxt/workbox.3de3418b.js')
const workboxSW = new self.WorkboxSW({
"cacheId": "my-app",
"clientsClaim": true,
"directoryIndex": "/"
})
workboxSW.precache([
{
"url": "/_nuxt/app.bb74329360a7ee70c2af.js",
"revision": "8477c51cbf9d3188f34f1d61ec1ae6bc"
},
{
"url": "/_nuxt/layouts/default.ce9446c7c3fffa50cfd2.js",
"revision": "504d33b2d46614e60d919e01ec59bbc8"
},
{
"url": "/_nuxt/manifest.912c22076a54259e047d.js",
"revision": "a51a74b56987961c8d34afdcf4efa85c"
},
{
"url": "/_nuxt/pages/index.6bfd6741c6dfd79fd94d.js",
"revision": "1a80379a5d35d5d4084d4c2b85e1ee10"
},
{
"url": "/_nuxt/vendor.f681eb653617896fcd64.js",
"revision": "59c58901fd5142fdaac57cbee8c1aeb4"
}
])
workboxSW.router.registerRoute(new RegExp('/_nuxt/.*'), workboxSW.strategies.cacheFirst({}), 'GET')
workboxSW.router.registerRoute(new RegExp('/.*'), workboxSW.strategies.networkFirst({}), 'GET')
workboxSW.router.registerRoute(new RegExp('https://fonts.googleapis.com/.*'), workboxSW.strategies.cacheFirst({}), 'GET')
Why is the font not getting cached?
EDIT #1:
Thanks to Jeff Posnick, I understand what's happening. I haven't figured out the right syntax to pass in the nuxt.config.js file, but as an experiment, I hacked the sw.js file directly and added these two lines:
workboxSW.router.registerRoute(new RegExp('https://fonts.googleapis.com/.*'),
workboxSW.strategies.cacheFirst({cacheableResponse: {statuses: [0, 200]}}),
'GET')
workboxSW.router.registerRoute(new RegExp('https://fonts.gstatic.com/.*'),
workboxSW.strategies.cacheFirst({cacheableResponse: {statuses: [0, 200]}}),
'GET')
That worked!
For version 2 of workbox and version 2 of #nuxt/pwa, this is the syntax that is needed in the nuxt.config.js file:
workbox: {
runtimeCaching: [
{
urlPattern: 'https://fonts.googleapis.com/.*',
handler: 'cacheFirst',
method: 'GET',
strategyOptions: {cacheableResponse: {statuses: [0, 200]}}
},
{
urlPattern: 'https://fonts.gstatic.com/.*',
handler: 'cacheFirst',
method: 'GET',
strategyOptions: {cacheableResponse: {statuses: [0, 200]}}
},
]
}
This is due to the fact that Workbox won't cache opaque responses using a cacheFirst strategy, unless you specifically tell it to.
This was a common source of confusion with Workbox v2, and we've improved the JavaScript console logs and documentation for the upcoming v3 release. The "Handle Third Party Requests" guide goes into more detail.
You can change your config to
runtimeCaching: [{
urlPattern: 'https://fonts.googleapis.com/.*',
handler: 'cacheFirst',
method: 'GET',
cacheableResponse: {statuses: [0, 200]}
}]
to get that behavior in the current v2 release of Workbox.
tested with #nuxtjs/pwa#3.0.0-beta.20 and nuxt#2.11.0.
pwa property below in nuxt.config.js created a new cache as expected:
pwa: {
workbox: {
runtimeCaching: [
{
urlPattern: 'https://my-api-url/.*',
handler: 'networkFirst',
method: 'GET',
strategyOptions: {
cacheName: 'my-api-cache',
cacheableResponse: {statuses: [0, 200]}
}
}
]
}
},
could confirm cache content in Cache Storage section on Chrome DevTools, Appliation tab.
Related
I have a nextjs project with a :client param which represents a client, like this:
domain.com/:client
And I have multiple clients... so I need to do this rewrite:
:client.domain.com -> domain.com/:client
For example for clients:
google.domain.com -> domain.com/google
netflix.domain.com -> domain.com/netflix
...
Inside the same project.
Any way to do that?
You can use the redirects option in the vercel.json, as Maxime mentioned.
However, it requires 1 extra key.
For example, if your app is available at company.com:
{
...
redirects: [
{
"source": "/",
"has": [
{
"type": "host",
"value": "app.company.com"
}
],
"destination": "/app"
}
]
}
More info:
Example Guide
vercel.json docs
Create a config in your project root with next.config.js
If this file exists add the following snippet to it, Mind you, we used example.com in place of domain .com as Body cannot contain "http://domain. com" in stackoverflow
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/:path*",
has: [
{
type: "host",
value: "client.example.com",
},
],
destination: "http://example.com/client/:path*",
permanent: false,
},
];
},
};
To confirm it's also working in development, try with localhost
module.exports = {
reactStrictMode: true,
// async rewrites() {
async redirects() {
return [
{
source: "/:path*",
has: [
{
type: "host",
value: "client.localhost",
},
],
destination: "http://localhost:3000/client/:path*",
permanent: false,
},
];
},
};
Dynamic Redirect
To make it dynamic, we'll create an array of subdomains
const subdomains = ["google", "netflix"];
module.exports = {
async redirects() {
return [
...subdomains.map((subdomain) => ({
source: "/:path*",
has: [
{
type: "host",
value: `${subdomain}.example.com`,
},
],
destination: `https://example.com/${subdomain}/:path*`,
permanent: false,
})),
];
},
}
You can read more from the official next.js doc redirects or rewrite
I've created a C# .net5.0 console application and during testing Serilog has been working without incident, logging to Console and File (same folder; path="log.txt"). However, when I run on the application on our server, neither Console nor File logging sinks are working! I assume now that the issue is not the sinks themselves but Serilog not actually working.
I've tried enabling the self log:
Serilog.Debugging.SelfLog.Enable(msg =>
Console.WriteLine(msg)
);
but even running in the debugger in my dev environment, the Console.WriteLine(msg) line is never called!
My appsettings.json is as follows:
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Information",
"System": "Information"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "log.txt",
"rollingInterval": "Infinite",
"outputTemplate": "{Timestamp:HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
"shared": false
}
}
],
"Enrich": [ "FromLogContext" ]
},
"Database": {
"Server": "(local)",
"Database": "ActivbaseLive"
},
"Email": {
"SmtpHost": "localhost",
"SmtpPort": 25,
"SmtpSecurityOption": "None",
"SmtpUsername": null,
"SmtpPassword": null,
"SmtpSender": "\"Activbase Learning Platform\" <noreply#activbase.net>"
}
}
I've tried absolute paths (using double backslashes in appsettings.json).
I've tried pre-creating the log file (e.g. log.txt and log200428.txt) and setting permissions to Everyone Full Control but neither of these changes fix the problem and they don't explain why the Console sink doesn't write either.
Here is how Serilog is being configured during start-up which is where I suspect the problem is (even through it works in dev environment):
return Host.CreateDefaultBuilder()
.ConfigureLogging(logging =>
{
logging.ClearProviders();
})
.UseSerilog((hostContext, loggerConfiguration) =>
{
loggerConfiguration.ReadFrom.Configuration(hostContext.Configuration);
})
.ConfigureAppConfiguration((hostContext, builder) =>
{
builder.AddEnvironmentVariables();
})
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
...
});
}
Any ideas why Serilog isn't working in production?
The Path you provide should be absolute.
Some thing like this:
"path": "E:/wwwroot/QA/BackgroundWorkerService/Logs/logFile_.log"
Even I had the same issue, the above fix worked fine for me...
For my api application running in IIS: I had to assign the following permissions to the log folder for the IIS_IUSRS. I didn't need an absolute path!
I am creating a website using django and nuxtjs. I have installed service worker to improve speed. When I unregister service worker in inspect/Application tab, timing of my website in inspect/Network tab looks like this:
When I use service worker opening website at the first time is fine, But when I reload the page or open another page of the website, the request to the server is queued long time. The timing of my website looks like this:
which is queued 1.91 seconds. here describes reasons of queuing a request and all of them is in browser control.
I installed service worker with pwa module which has workbox in its dependencies. My nuxt.config.js file is here:
import pkg from './package'
import {modify_html} from './services/amp/hook';
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: 'example',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: pkg.description }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Global CSS
*/
css: [
'~/static/css/base.css',
'~/static/css/hooper.css',
'~/static/css/font-awesome/css/all.min.css',
],
/*
** Plugins to load before mounting the App
*/
plugins: [
{ src: '~plugins/ga.js', ssr: false }
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://axios.nuxtjs.org/usage
'#nuxtjs/axios',
// Doc: https://bootstrap-vue.js.org/docs/
'bootstrap-vue/nuxt',
'#nuxtjs/pwa',
'#nuxtjs/device',
],
manifest:{
"short_name": "example",
"name": "example",
"icons": [
{
"src": "/static/icon.png",
"type": "image/png",
},
],
"start_url": "/",
"background_color": "white",
"display": "standalone",
"scope": "/",
"theme_color": "white"
},
workbox:{
},
/*
** Axios module configuration
*/
axios: {
// See https://github.com/nuxt-community/axios-module#options
},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {
}
},
hooks:{
// This hook is called before generatic static html files for SPA mode
'generate:page': (page) => {
if(page.path.includes('/amp/')){
page.html = modify_html(page.html)
}
},
// This hook is called before rendering the html to the browser
'render:route': (url, page, { req, res }) => {
if(url.includes('/amp/')){
page.html = modify_html(page.html)
}
}
}
}
How can I fix service worker to do not stall new requests?
I have MVC as my backend. MVC controllers/views have _StartPage.cshtml, _Layout.cshtml (They are combined to form _index.cshtml).
So, I need to use webpack dev server proxy to fetch the index.html from backeend server. Is there any way to do it?
So far this is what I've come up with
devServer: {
historyApiFallback: true,
index: '',
contentBase: "./",
port: 8000,
proxy: [{
//Only works for api, index.html does not
context: ['/index.html', '/api'],
target: 'http://localhost',
pathRewrite: { '^/api': '/TestApp/api' }
}]
},
I'm open to use any other client server beside webpack-dev-server if that solves the problem?
Please note that this will be used only in Development Not in production.
I think you want a different entry for each path. Something like this maybe?
devServer: {
contentBase: './',
port: 8000
proxy: {
'/index.html': {
target: 'http://localhost/',
pathRewrite: { '^/': 'TestApp/index.html' },
},
'/api': {
target: 'http://localhost/',
pathRewrite: { '^/api': '/TestApp/api' }
},
},
}
How about your project structure?
The webpack-dev-server must be used in development mode, after build and deployed, you need to change the api url for production mode.
I'm a front-end and I meet similar problem, I solve it by DefinePlugin that could change api in different mode.
My webpack like this:
├webpack.common.js
├webpack.dev.js
├webpack.prod.js
webpack.common.js
devServer: {
// contentBase: 'dist/',
historyApiFallback: true,
watchOptions: { aggregateTimeout: 300, poll: 1000 },
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
},
proxy: {
'/': {
target: 'xxxxxxxxxxxx/website/',//url
changeOrigin: true,
secure: false,
}
}
},
webpack.dev.js
plugins:[
new webpack.DefinePlugin({
'SERVICE_URL': JSON.stringify('')
})
]
webpack.prod.js
plugins:[
new webpack.DefinePlugin({
'SERVICE_URL': JSON.stringify('https://xxx.api.com/') //real api
})
]
fetch/ajax code
console.log(SERVICE_URL)
$.ajax({
url: SERVICE_URL+'api/%E6%AD%A6%E6%BC%A2%E8%82%BA%E7%82%8E_%E5%8F%B0%E5%95%86_simple.json',
...
This is my solution, I hope it could give some inspiration.
Using loopback, I have created a connection to an existing API using the REST connector, which is working well. I would however like to pass through the oAuth token coming from the client.
I can get hold of the oAuth token by grabbing ctx.req.headers.authorization from the Model.beforeRemote method, but can't seem to figure out a way of passing it to the REST connector as a new header.
I've tried a couple of things:
Adding a hook using Model.observe (but this doesn't seem to fire with the REST connector).
Using a template with an authorization field - but have not been able to get this working correctly.
Any ideas appreciated.
With the connector below you should be able to pass the OAuth token into the function (as first parameter in the example). Does something like this not work for you?
{
connector: 'rest',
debug: false,
options: {
"headers": {
"accept": "application/json",
"content-type": "application/json",
"authorization": "{oauth}"
},
strictSSL: false,
},
operations: [
{
template: {
"method": "GET",
"url": "http://maps.googleapis.com/maps/api/geocode/{format=json}",
"query": {
"address": "{street},{city},{zipcode}",
"sensor": "{sensor=false}"
},
"options": {
"strictSSL": true,
"useQuerystring": true
},
"responsePath": "$.results[0].geometry.location"
},
functions: {
"geocode": ["oauth", "street", "city", "zipcode"]
}
}
]}
Wanted to answer this, and build on Bryan's comments. Firstly, in datasources.json, you'll want to setup the REST connector:
{
"name": "connect",
"connector": "rest",
"debug": "true",
"operations": [
{
"template": {
"method": "GET",
"url": "http://server/api",
"headers":{
"authorization": "Bearer {token}"
}
},
"functions": {
"get": ["token"]
}
}
]
}
As Bryan covered, it possible to put the auth header in each call, or at the root of the connector.
Secondly, and this is the bit I was stuck on, in order to pass the token to the API call from a model, it's required to generate a remote method that passes the token as a query parameter. This is what it looks like in this example:
module.exports = function (Model) {
Model.disableRemoteMethod('invoke', true);
Model.disableRemoteMethod('get', true);
Model.call = function (req, cb) {
var token = req.token;
Model.get(token, function (err, result) {
cb(null, result);
});
};
Model.remoteMethod(
'call',
{
http: {path: '/', verb: 'get'},
accepts: [
{arg: 'req', type: 'object', http: {source: 'req'}}
],
returns: {
root: true
}
}
);
};
Notice how the req argument is required in order to provide the request to the model. You also notice that I've disabled the original get and invoke methods (replacing it with a more REST-friendly resource).
Finally, you'll need to get the token into the request. For this, it's easy enough to use some middleware. Here's an example from server.js:
app.use('/api', function (req, res, next) {
oidc.authenticate(req, function (err, token) {
if (err) {
return res.send({status: 401, message: err});
}
req.token = token;
next();
});
});
In the above example, I'm using an internal OIDC provider to validate the token, but of course, you can use anything.