can we unregister old service worker by its name and register new service worker - push-notification

I am facing some problem related to service worker before some time i am using gcm and service worker file name was service-worker.js after releasing fcm i changed my code and now my service worker file name is firebase-messaging-sw.js but in some my client browser calling old service-worker.js file which is generating an error(service-worker.js not found 500). I already used following code before gettoken().
const messaging = firebase.messaging();
navigator.serviceWorker.register('/firebase-messaging-sw.js')
.then((registration) => {
messaging.useServiceWorker(registration);
// Request permission and get token.....
});
but its still showing this error.

In general, if you have multiple service workers registered with different scopes, and you want to get a list of them from a client page (and potentially unregister some of them, based on either matching scope or SW URL), you can do the following:
async unregisterSWs({matchingScope, matchingUrl}) {
const registrations = await navigator.serviceWorker.getRegistrations();
const matchingRegistrations = registrations.filter(registration => {
if (matchingScope) {
return registration.scope === matchingScope;
}
if (matchingUrl) {
return registration.active.scriptURL === matchingUrl;
}
});
for (const registration of matchingRegistrations) {
await registration.unregister();
console.log('Unregistered ', registration);
}
}
and then call it passing in either a scope or SW script URL that you want to use to unregister:
unregisterSWs({matchingScope: 'https://example.com/push'});
unregisterSWs({matchingUrl: 'https://example.com/my-push-sw.js'});

Related

Getting data from supabase in nuxt 3 middleware

const client = useSupabaseClient()
useAsyncData('profiles', async () => {
const { data } = await client.from('profiles').select('id, username, description').eq('user_id', user.value.id).single()
return data
}).then(resp => {
if (resp.data.value.username == null) {
navigateTo('/createprofile')
} else {
store.username = resp.data.value.username
}
})
I have this piece of code inside of a middleware and it works, but it gives two errors
[nitro] [dev] [unhandledRejection] Error: nuxt instance unavailable
[Vue warn]: onServerPrefetch is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup(). If you are using async setup(), make sure to register lifecycle hooks before the first await statement.
I am using NuxtSupabase if that wasn't clear
This is middleware for fetching user's profile data and if that profile does not exist then redirect them to profile creation
What I am wondering is how I do this without getting the errors

Uncaught TypeError: with Redux Thunk and node.js express wwwhisper middleware on MERN stack application

I am building a MERN stack application and trying to use the connect-wwwhisper package to protect access to an application (testing beta version) that I am hosting. I am using passport authentication on the Node js backend for user authentication but I want to layer wwwhisper package on entire app so that only people with approved email may access the entire app without disturbing the passport authentication that I set up. I have set up wwwhisper per the documentation: https://devcenter.heroku.com/articles/wwwhisper but there is a conflict with the redux thunk middleware that is causing a type error within the redux js file below:
function compose() {
for (var _len = arguments.length, funcs = new Array(_len), _key =
0;
_key < _len; _key++) {
funcs[_key] = arguments[_key];
}
if (funcs.length === 0) {
return function (arg) {
return arg;
};
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce(function (a, b) {
return function () {
return a(b.apply(void 0, arguments));
The error message is: Uncaught TypeError: Cannot read property 'apply' of undefined
In my server js file I am using the following to direct requests to the index.html file of the react side of the application. All other requests to the backend api are using the
app.use("routename");
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build",
"index.html"));
});
}
The wwwhisper middleware does protect the application and sends out the tokenized link to access the application but when I try to access the application I get the above error message along with a message saying the token is unauthorized. The author of the wwwhisper middleware is not familiar with how the wwwhisper middleware may be interacting with the Redux thunk middleware. How can I get this to work? I've been programming for about a year so any help is appreciated.

Google Calendar API watch channels not stopping

Stopping a watch channel is not working, though it's not responding with an error, even after allowing for propagation overnight.  I'm still receiving 5 notifications for one calendarlist change.  Sometimes 6.  Sometimes 3.  It's sporadic. We're also receiving a second round of notifications for the same action after 8 seconds.  Sometimes 6 seconds.  Sometimes a third set with a random count.  Also sporadic. Received a total of 10 unique messages for a single calendar created via web browser.
You can perform infinite amount of watch requests on specific calendar resource, Google will always return the same calendar resource Id for the same calendar, but the uuid you generate in the request will be different, and because of that, you will receive multiple notifications for each watch request that you've made. One way to stop all notifications from specific calendar resource, is to listen for notifications, pull out "x-goog-channel-id" and "x-goog-resource-id" from notification headers, and use them in Channels.stop request.
{
"id": string,
"resourceId": string
}
Every time you perform a watch request, you should persist the data from the response, and check if the uuid or resource id already exist, if yes don't perform watch request for that resource id again (if you don't want to receive multiple notifications).
e.g.
app.post("/calendar/listen", async function (req, res) {
var pushNotification = req.headers;
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.end("Post recieved");
var userData = await dynamoDB.getSignInData(pushNotification["x-goog-channel-token"]).catch(function (err) {
console.log("Promise rejected: " + err);
});
if (!userData) {
console.log("User data not found in the database");
} else {
if (!userData.calendar) {
console.log("Calendar token not found in the user data object, can't perform Calendar API calls");
} else {
oauth2client.credentials = userData.calendar;
await calendarManager.stopWatching(oauth2client, pushNotification["x-goog-channel-id"], pushNotification["x-goog-resource-id"])
}
}
};
calendarManager.js
module.exports.stopWatching = function (oauth2client, channelId, resourceId) {
return new Promise(function (resolve, reject) {
calendar.channels.stop({
auth: oauth2client,
resource: {
id: channelId,
resourceId: resourceId
}
}, async function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return reject(err);
} else {
console.log("Stopped watching channel " + channelId);
await dynamoDB.deleteWatchData(channelId)
resolve(response);
}
})
})
}
Not a google expert but I recently implement it in my application,
I am trying to answer some of your questions for future readers:
It's sporadic
Tha's because you have create more than 1 channels for watching events.
We're also receiving a second round of notifications for the same action after 8 seconds
Google doesn't say anything about the maximum delay for sending a push notification.
Suggestions:
CREATE:
When you create a new channel, always save the channel_id and channel_resource in your database.
DELETE:
When you want to delete a channel just use stop API endpoint with the channel data saved in your database
RENEW:
As you have noticed the channels do expire, so you need to update them once in a while. To do that create a crone in your server that is going to STOP all previous channels and it will create new one.
Comment: Whenever something is going wrong please read the error message sent from the Google API calendar. Most of the time, it tells you what is wrong.
Use Channels.stop which is mentioned in the docs. Supply the following data in your request body:
{
"id": string,
"resourceId": string
}
id is the channel ID when you created your watch request. Same goes with resource ID.
Read this SO thread and this github forum for additional reference.

Web Push - Placing service worker

From here https://developers.google.com/web/fundamentals/getting-started/primers/service-workers
If the service worker is at the root of the domain, this means
that the service worker's scope will be the entire origin.
But If we register the service worker file at /example/sw.js, then
the service worker would only see fetch events for pages whose
URL starts with /example/ (i.e. /example/page1/, /example/page2/).
Second point mentions only fetch won't work at / (root or other than example) if I place the service worker at /example/.
But subscription (generation of sub object) itself not getting generated if the service worker is at /example/ and if the web page is at / (root or other than example), which the doc clearly doesn't explain.
Please let me know, if even the generation of subscription (pushManager.getSubscription) in the service worker itself won't happen.
PS: I have tried it on Chrome 54.0.2840.100 Ubuntu 16.04 LTS
Generation of subscription object does not depend on service worker scope. You can do it any where
Eg.
permission.js
export function allowNotifications(scope){
if (navigator.serviceWorker && Notification){
if( Notification.permission !== "granted") {
navigator.serviceWorker.ready.then(function(reg) {
subscribe(reg);
});
}
}
}
function subscribe(reg) {
reg.pushManager.subscribe({userVisibleOnly: true}).then(function(pushSubscription) {
bindUserToDevice(JSON.stringify(pushSubscription));
}, function (err) {
console.log('error');
});
}
export function bindUserToDevice(subscriptionObj) {
// received subsciption object should be send to backend to bind the device for pushes
var data = {
type: 'POST',
url : '/bind',
body: subscriptionObj,
};
fetch('/bind', data);
}
allowNotifications function can be called from anywhere. Only the service worker file should be present on root which should have push event
global.addEventListener('push', function(event) {
var pushObj = event.data.json();
var pushData = pushObj.data;
var title = pushData && pushData.title;
var body = pushData && pushData.body;
var icon = '/img/logo.png';
event.waitUntil(global.registration.showNotification(title, {
body: body,
icon: icon,
data:pushData
}));
});

How to emit data only to one client in Meteor streams

I am building a realtime game with Meteor streams. I need to update only one client - send a room ID from server. Users are not logged in so Meteor.userId() is null and therefore I can't use this: http://arunoda.github.io/meteor-streams/communication-patterns.html#streaming_private_page
There is only one URL (homepage) where all things happen. So I don't use any URL parameters for room. Everything is on the server.
I have tried to use Meteor.uuid() instead of Meteor.userId() but uuid is changed after each emit (which is strange).
In socket.io I would do this:
//clients is an array of connected socket ids
var clientIndex = clients.indexOf(socket.id);
io.sockets.socket(clients[clientIndex]).emit('message', 'hi client');
Is there any way to do this in Meteor streams or Meteor itself?
Well, this can be easily done if you decided to use database, but I guess it is not the best option if you have a large number of clients.
So another way to achieve this - without database - is to make a good use of the Meteor's publish/subscribe mechanism. Basically the way it could work is the following:
1. client asks server for a communication token (use Meteor.methods)
2. client subscribes to some (abstract) data set using that token
3. server publishes the required data based on the received token
So you will need to define a method - say getToken - on the server that generates tokens for new users (since you don't want to use accounts). This could be something more or less like this:
var clients = {}
Meteor.methods({
getToken: function () {
var token;
do {
token = Random.id();
} while (clients[token]);
clients[token] = {
dependency: new Deps.Dependency(),
messages: [],
};
return token;
},
});
A new client will need to ask for token and subscribe to the data stream:
Meteor.startup(function () {
Meteor.call('getToken', function (error, myToken) {
// possibly use local storage to save the token for further use
if (!error) {
Meteor.subscribe('messages', myToken);
}
});
});
On the server you will need to define a custom publish method:
Meteor.publish('messages', function (token) {
var self = this;
if (!clients[token]) {
throw new Meteor.Error(403, 'Access deniend.');
}
send(token, 'hello my new client');
var handle = Deps.autorun(function () {
clients[token].dependency.depend();
while (clients[token].messages.length) {
self.added('messages', Random.id(), {
message: clients[token].messages.shift()
});
}
});
self.ready();
self.onStop(function () {
handle.stop();
});
});
and the send function could defined as follows:
var send = function (token, message) {
if (clients[token]) {
clients[token].messages.push(message);
clients[token].dependency.changed();
}
}
That's a method I would use. Please check if it works for you.
I think using Meteor.onConnection() like a login would enable you to do what you want pretty easily in a publish function.
Something like this:
Messages = new Meteor.Collection( 'messages' );
if ( Meteor.isServer ){
var Connections = new Meteor.Collection( 'connections' );
Meteor.onConnection( function( connection ){
var connectionMongoId = Connections.insert( connection );
//example Message
Message.insert( {connectionId: connection.id, msg: "Welcome"});
//remove users when they disconnect
connection.onClose = function(){
Connections.remove( connectionMongoId );
};
});
Meteor.publish( 'messages', function(){
var self = this;
var connectionId = self.connection.id;
return Messages.find( {connectionId: connectionId});
});
}
if ( Meteor.isClient ){
Meteor.subscribe('messages');
Template.myTemplate.messages = function(){
//show all user messages in template
return Messages.find();
};
}
I have used database backed collections here since they are the default but the database is not necessary. Making Messages a collection makes the reactive publishing easy whenever a new message is inserted.
One way that this is different from streams is that all the messages sent to all clients will end up being kept in server memory as it tries to keeps track of all data sent. If that is really undesirable then you could use a Meteor.method so send data instead and just use publish to notify a user a new message is available so call the method and get it.
Anyway this is how I would start.

Resources