I can't get the offline.html page to display. I keep getting the The FetchEvent for "https://my-domain.com" resulted in a network error response: a redirected response was used for a request whose redirect mode is not "follow".
Here's the snippet of my service-worker.js which should return the offline.html when the network is unavailable.
self.addEventListener('fetch', function(event) {
if (event.request.mode === 'navigate' || (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
if(event.request.url.includes("my-domain.com")){
console.log(event.request);
event.respondWith(
caches.match(event.request).then(function(resp) {
return resp || fetch(event.request).then(function(response) {
let responseClone = response.clone();
caches.open(CACHE_NAME).then(function(cache) {
cache.put(event.request, responseClone);
});
return response;
});
}).catch(function() {
return caches.match("/offline.html");
})
);
}
}
});
Below is the console.log of my network request (page refresh when offline)
Request {method: "GET", url: "https://my-domain.com", headers: Headers, destination: "unknown", referrer: "", …}
bodyUsed:false
cache:"no-cache"
credentials:"include"
destination:"unknown"
headers:Headers {}
integrity:""
keepalive:false
method:"GET"
mode:"navigate"
redirect:"manual"
referrer:""
referrerPolicy:"no-referrer-when-downgrade"
signal:AbortSignal {aborted: false, onabort: null}
url:"https://my-domain.com"
__proto__:Request
I got this working / found the fix. It was related to a redirected response security issue in the browser. From the Chromium Bugs Blog, Response.redirected and a new security restriction.
Solution: To avoid this failure, you have 2 options.
You can either change the install event handler to store the response generated from res.body:
self.oninstall = evt => {
evt.waitUntil(
caches.open('cache_name')
.then(cache => {
return fetch('/')
.then(response => cache.put('/', new Response(response.body));
}));
};
Or change both handlers to store the non-redirected response by setting redirect mode to ‘manual’:
self.oninstall = function (evt) {
evt.waitUntil(caches.open('cache_name').then(function (cache) {
return Promise.all(['/', '/index.html'].map(function (url) {
return fetch(new Request(url, { redirect: 'manual' })).then(function (res) {
return cache.put(url, res);
});
}));
}));
};
self.onfetch = function (evt) {
var url = new URL(evt.request.url);
if (url.pathname != '/' && url.pathname != '/index.html') return;
evt.respondWith(caches.match(evt.request, { cacheName: 'cache_name' }));
};
Related
I have a site with a service worker and it registers every time a new sub-URL is visited. Is this the expected behaviour?
Example. My root URL is mysite.com. The SW registers OK when I visit that URL. When I visit mysite.com/subpage, it registers as a new service worker for that URL too.
Please tell me if I am doing something wrong or if this is the way the SWs work.
My service worker recache the resources everytime it installs, so I'm guessing it is recaching everything everytime the user visits a sub-URL for the first time. Isn't that not recommended?
This is my SW.
var staticCacheName = "bspev-v" + new Date().getTime();
var filesToCache = [
'/',
'/offline',
'/css/app.css',
'/js/app.js',
'/js/rutas.js',
// iconos app
'/images/icons/icon-72x72.png',
'/images/icons/icon-96x96.png',
'/images/icons/icon-128x128.png',
'/images/icons/icon-144x144.png',
'/images/icons/icon-152x152.png',
'/images/icons/icon-192x192.png',
'/images/icons/icon-384x384.png',
'/images/icons/icon-512x512.png',
'/favicon.ico',
// imagenes app
'/images/logotexto.png',
'/images/offline.png',
// ajax
'/api/prestamos/pendientes',
//fuentes app
'https://fonts.googleapis.com/css?family=Archivo+Narrow&display=swap',
'https://fonts.gstatic.com/s/archivonarrow/v11/tss0ApVBdCYD5Q7hcxTE1ArZ0bbwiXw.woff2',
// vue
'/js/vue.js',
'/js/vue.min.js',
// fontawesome
'/css/fa-all.css',
'/webfonts/fa-solid-900.woff2'
];
// Cache on install
self.addEventListener("install", event => {
this.skipWaiting();
event.waitUntil(
caches.open(staticCacheName)
.then(cache => {
console.log('Service Worker instalado.');
return cache.addAll(filesToCache);
})
)
});
// Clear cache on activate
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames
.filter(cacheName => (cacheName.startsWith("bspev-")))
.filter(cacheName => (cacheName !== staticCacheName))
.map(cacheName => caches.delete(cacheName))
);
})
);
});
// Serve from Cache
self.addEventListener("fetch", event => {
const requestURL = new URL(event.request.url);
//Handle api calls
if (/\/api\//.test(requestURL.pathname)) {
event.respondWith(
fetch(event.request).then(response => {
event.waitUntil(
caches.open(staticCacheName).then(cache => {
cache.put(event.request, response.clone());
})
);
return response.clone();
}).catch(function() {
return caches.match(event.request);
})
);
} else {
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request);
})
.catch(() => {
return caches.match('offline');
})
);
}
});
This is the SW registration
<script type="text/javascript">
// Initialize the service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/serviceworker.js', {
scope: '.'
}).then(function (registration) {
// Registration was successful
console.log('Laravel PWA: ServiceWorker registration successful with scope: ', registration.scope);
}, function (err) {
// registration failed :(
console.log('Laravel PWA: ServiceWorker registration failed: ', err);
});
} else { console.log('no serviceWorker in navigator') }
</script>
I have been trying to implement Paypal's IPN using AWS Api Gateway to get an IPN handler url. the api is integrated with a Lambda function as the "receiver".
I have tested the api gateway url using Paypal's IPN simulator.It works for the first step and I get this message "IPN was sent and the handshake was verified".
My problem is now with the next step,where I have to send the recived message back to Paypal using HTTPS post. I have tried a number of times and keep getting this error:
{
"code": "ECONNREFUSED",
"errno": "ECONNREFUSED",
"syscall": "connect",
"address": "127.0.0.1",
"port": 443
}
I really would appreciate some help in getting this to work.
I'm using node.js 8.10.Here's my Lambda function:
exports.handler = (event, context, callback) => {
console.log('Received event:', JSON.stringify(event, null, 2));
// Return 200 to caller
console.log('sending 200 back to paypal');
callback(null, {
statusCode: '200'
});
// Read the IPN message sent from PayPal and prepend 'cmd=_notify-validate'
console.log('modifying return body...');
var body = 'cmd=_notify-validate&' + event.body;
callHttps(body, context);};
function callHttps(body, context) {
console.log('in callHttp()....');
var https = require('https');
var options = {
url: 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr',
method: 'POST',
headers: {
"user-agent": "Nodejs-IPN-VerificationScript"
},
body: body
};
const req = https.request(options, (res) => {
res.on('data', (chunk) => {
// code to execute
console.log("on data - can execute code here....");
});
res.on('end', () => {
// code to execute
console.log("on end - can execute code here....");
});
});
req.on('error', (e) => {
console.log("Error has occured: ", JSON.stringify(e, null, 2));
});
req.end();}
managed to sort it out.i was using url instead of breaking it down to host and path.here's the full code that worked for me:
exports.handler = (event, context, callback) => {
console.log('Received event:', JSON.stringify(event, null, 2));
// Return 200 to caller
console.log('sending 200 back to paypal');
callback(null, {
statusCode: '200'
});
callHttps(event.body, context);};
function callHttps(body, context) {
console.log('in callHttp()....');
// Read the IPN message sent from PayPal and prepend 'cmd=_notify-validate'
console.log('modifying return body...');
var bodyModified = 'cmd=_notify-validate&' + body;
var https = require('https');
var options = {
host: "ipnpb.sandbox.paypal.com",
path: "/cgi-bin/webscr",
method: 'POST',
headers: {
'user-agent': 'Nodejs-IPN-VerificationScript',
'Content-Length': bodyModified.length,
}
};
const req = https.request(options, (res) => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
var result = '';
res.on('data', (d) => {
// get the result here
result += d;
});
res.on('end', (end) => {
// check the result
if (result === 'VERIFIED') {
// process the message
// split the message
var res = body.split("&");
// create an object
var paypalMessageObject = new Object();
// loop through split array
res.forEach(element => {
// split element
var temp = (element.toString()).split("=");
// add to the object
paypalMessageObject[temp[0]] = temp[1];
});
console.log('paypalMessageObject: ' + JSON.stringify(paypalMessageObject, null, 2));
var checkItems = {
payment_status: paypalMessageObject.payment_status,
mc_gross: paypalMessageObject.mc_gross,
mc_currency: paypalMessageObject.mc_currency,
txn_id: paypalMessageObject.txn_id,
receiver_email: paypalMessageObject.receiver_email,
item_number: paypalMessageObject.item_number,
item_name: paypalMessageObject.item_name
};
console.log('checkItems: ', JSON.stringify(checkItems, null, 2));
}
else { console.log('not verified, now what?'); }
});
});
req.on('error', (e) => {
console.log("Error has occured: ", JSON.stringify(e, null, 2));
});
req.write(bodyModified);
req.end();}
I am writing a Fetch request to post new users to an application. The fetch is integrated with a redux store. Response returns [object Object] and response.status returns undefined. I am new to Redux and am wondering if that is where the error is. Here is the code from my actions creator file:
export function createCustomerSuccess(values) {
return {
type: types.CREATE_CUSTOMER_SUCCESS,
values: values
};
}
export function createCustomer(values) {
return function (dispatch, getState) {
console.log('values passing to store', values);
return postIndividual(values).then( (response) => {
console.log('calling customer actions');
console.log(response);
if(response.status === 200){
console.log(response.status);
dispatch(createCustomerSuccess(values));
console.log('create customer success');
}
else {
console.log('not successful');
}
});
};
}
function postIndividual(values) {
console.log('test from post' + JSON.stringify(values));
const URLPOST = "http://myurlisworking/Add";
return fetch (URLPOST, {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Access-Control-Origin": "*"
},
body: JSON.stringify(values)
})
.then(response => response.json())
.then(response => {
console.log('response' + response.status)
});
}
Issue seems like with your fetch expectations. When your first .then gets called after fetch() then you get response.status available there to check.
You can rewrite your fetch like below and see if that resolves.
function postIndividual(values) {
console.log('test from post' + JSON.stringify(values));
const URLPOST = "http://myurlisworking/Add";
return fetch (URLPOST, {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Access-Control-Origin": "*"
},
body: JSON.stringify(values)
})
.then(response => {
console.log('response' + response.status)
return response.ok && response.json();
})
.catch(err => console.log('Error:', err));
}
You can check response.status here ^ and do what you want.
Alternatively you can just do the fetch in postIndividual and handle response in your createCustomer instead.
I'm currently working on integrating devise as an authentication backend with angular as its frontend.
I have faced a problem on when login and logout, the session data will be updated untill the page refresh.
What i will do get session data without page refresh..?
Thanks for your Answers...
AngularJs Controller :
function UsersCtrl($scope, Session) {"use strict";
$scope.CurrentUser = Session.requestCurrentUser();
$scope.login = function(user) {
$scope.authError = null;
Session.login(user.email, user.password)
.then(function(response) {
if (!response) {
$scope.authError = 'Credentials are not valid';
} else {
$scope.authError = 'Success!';
}
}, function(response) {
$scope.authError = 'Server offline, please try later';
});
};
$scope.logout = function() {
// alert("woow");
Session.logout();
};
$scope.register = function(user) {
$scope.authError = null;
console.log(user);
Session.register(user.email, user.password, user.confirm_password)
.then(function(response) {
}, function(response) {
var errors = '';
$.each(response.data.errors, function(index, value) {
errors += index.substr(0,1).toUpperCase()+index.substr(1) + ' ' + value + ''
});
$scope.authError = errors;
});
};
}
AngularJs Session Service:
angular.module('sessionService', ['ngResource'])
.factory('Session', function($location, $http, $q) {
// Redirect to the given url (defaults to '/')
function redirect(url) {
url = url || '/';
$location.path(url);
}
var service = {
login: function(email, password) {
return $http.post('/users/login', {user: {email: email, password: password} })
.then(function(response) {
service.currentUser = response.data.user;
if (service.isAuthenticated()) {
//$location.path(response.data.redirect);
$location.path('/store');
}
});
},
logout: function() {
$http.delete('/sessions').then(function(response) {
$http.defaults.headers.common['X-CSRF-Token'] = response.data.csrfToken;
service.currentUser = null;
redirect('/store');
});
},
register: function(email, password, confirm_password) {
return $http.post('/users', {user: {email: email, password: password, password_confirmation: confirm_password} })
.then(function(response) {
service.currentUser = response.data;
if (service.isAuthenticated()) {
console.log("authenticated");
$location.path('/');
}
});
},
requestCurrentUser: function() {
if (service.isAuthenticated()) {
return $q.when(service.currentUser);
} else {
return $http.get('/users').then(function(response) {
service.currentUser = response.data.user;
return service.currentUser;
});
}
},
currentUser: null,
isAuthenticated: function(){
return !!service.currentUser;
}
};
return service;
console.log(service);
});
One Thing about building applications like this (restful) is that understanding the the backend as an api and app as a front-end very well.
Then think about a story as such;
In the login screen of your app
Front-end: You Provided the credentials to your backend;
Back-end: Checked and authenticated then It will create a unique hash stored in db (JWT recommended to check expiration in frontend) to your Front-end.
Front-end:Save it in a cookie.
Also place it in your ajax setting header part as "Authorization: {token}"
Front-end: Then send each request with this header to your backend.
Back-end: Always check if the token is present and valid to provide resources.
http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/ this link has helped me understand the whole thing and misconceptions in the past.
use $window.location.reload(); before page redirect.
One way to achieve this could be overriding the devise sessions_controller destroy action and afrer doing sign_out #current_user return the session as json
Below is the code I am using to login with google. I have an element on login.php with id authorize-button. When clicked it logs in just fine.
I have a logout link in my header file. When I click the logout it calls gapi.auth.signOut(); then it destroys session and redirects back to login.php
This happens as far as I can tell but then it just logs the user right back into our site with google. This is a pain as some of our users switch from google to facebook logins.
Thanks in advance for any help.
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth, 1);
}
function checkAuth() {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
}
function handleAuthResult(authResult) {
var authorizeButton = document.getElementById('authorize-button');
if (authResult && !authResult.error) {
//authorizeButton.style.visibility = 'hidden';
makeApiCall();
} else {
//authorizeButton.style.visibility = '';
authorizeButton.onclick = handleAuthClick;
}
}
function handleAuthClick(event) {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
return false;
}
function signOut() {
gapi.auth.signOut();
}
function makeApiCall() {
gapi.client.load('oauth2', 'v2', function() {
var request = gapi.client.oauth2.userinfo.get();
request.execute(function(logResponse) {
var myJSON = {
"myFirstName": logResponse.given_name,
"myLastName": logResponse.family_name,
"name": logResponse.name,
"socialEmailAddress": logResponse.email
};
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get({
'userId': 'me'
});
request.execute(function(logResponse2) {
//alert(JSON.stringify(logResponse));
myJSON['profilePicture'] = logResponse2.image.url;
myJSON['socialId'] = logResponse2.id;
//alert(JSON.stringify(myJSON));
$.ajax({
type: "POST",
url: "includes/login-ajax.php",
data: "function=googleLogin&data=" + JSON.stringify(myJSON),
dataType: "html",
success: function(msg) {
if (msg == 1) {
//window.location = "settings.php";
}
}
});
});
});
});
});
}
Make sure you have set your cookie-policy to a value other than none in your sign-in button code. For example:
function handleAuthClick(event) {
gapi.auth.authorize(
{
client_id: clientId,
scope: scopes,
immediate: false,
cookie_policy: 'single_host_origin'
},
handleAuthResult);
return false;
}
Note that sign out will not work if you are running from localhost.
Weird issue, but solved my problem by rendering the signin button (hidden) even if the user is authenticated.
See full question/answer here https://stackoverflow.com/a/19356354/353985
I came across the same issue today. I have search for solution the whole. The only reliable solution that worked for me is through revoke as explained here
I stored access_token in session which is needed during revoke
Below is my code you may find it useful
function logout() {
var access_token = $('#<%=accessTok.ClientID %>').val();
var provider = $('#<%=provider.ClientID %>').val();
if (access_token && provider) {
if (provider == 'GPLUS') {
var revokeUrl = 'https://accounts.google.com/o/oauth2/revoke?token=' +
access_token;
// Perform an asynchronous GET request.
$.ajax({
type: 'GET',
url: revokeUrl,
async: false,
contentType: "application/json",
dataType: 'jsonp',
success: function (nullResponse) {
// Do something now that user is disconnected
// The response is always undefined.
},
error: function (e) {
// Handle the error
// console.log(e);
// You could point users to manually disconnect if unsuccessful
// https://plus.google.com/apps
}
});
}
else if (provider == 'FB') {
FB.getLoginStatus(function (response) {
if (response.status === 'connected') {
FB.logout();
}
});
}
} else {
}
}