nikoksr / notify

A dead simple Go library for sending notifications to various messaging services.
MIT License
3.11k stars 218 forks source link

feat(service): Add Web Push service #112

Closed nikoksr closed 1 year ago

nikoksr commented 3 years ago

Is your feature request related to a problem? Please describe.

Missing support for Web Push Notifications.

Describe the solution you'd like

Implement a notification service and use existing services such as WhatsApp as a guide for implementation. At the time writing this, SherClockHolmes/webpush-go seems to be the best client library for our service.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

KaviiSuri commented 2 years ago

Hey, I'd love to pick this up if it's still available?

nikoksr commented 2 years ago

@KaviiSuri it indeed is!

KaviiSuri commented 2 years ago

Great, I'll get a PR ready!

KaviiSuri commented 2 years ago

Hey @nikoksr

I had a doubt regarding the implementation of this.

Web push notifications require the server to store subscription objects (in some db or memory) which are sent to it typically using an api end point.

How should I incorporate that into the implementation while exposing a generalized api that can help the user customize it?

Should we just ask for a pointer to a store interface? And leave the store and api implementation upto the user?

nikoksr commented 2 years ago

@KaviiSuri thanks for making sure about this! Very good point indeed and I agree with you, in general I think it's desireable to give the user as much freedom of choice as possible while providing sane defaults.

So either a setable Store interface or struct pointer would be an acceptable solution. Is there a way that you could provide like a no-op default store? Or a very simple in memory solution? I want this service to be usuable without the user having to bring their own store as a requirement. We should have a super basic default store while allowing the user to bring their own.

Let me known if there's more. Appreciate your efforts!

KaviiSuri commented 2 years ago

Hey @nikoksr , thanks, that clears it up! I'll try to get the PR done this weekend.

Just one more thing about actually accepting the subscription objects to store it.

I think having a default API route will be a little overkill?

What if we leave the API part to the user? we could expose a method to store the subscription object which uses the store inside?

So no matter what store is used, the user can just call service.AddSubscription()in their preferred way of getting the subscription from user (REST API, GraphQL, WebSockets etc)

nikoksr commented 2 years ago

Hi @KaviiSuri, really like your attitude about this. I'm already excited for your contribution.

I'm not entirely sure, what exactly you mean tho. I guess, I'd have to look into the functionalities of web push myself.

What do you mean by having a default API route exactly? Like, storing the address to a default Webpush endpoint as a constant and using that by default for our requests? In that case, I'd be more than fine with it. Many other services actually already do a pretty similar thing. I always prefer it myself, when libraries and tools come with sane defaults but also provide me the flexibility to configure.

In the case of an API route that would mean, that it's totally fine if you'd provide the default web push endpoint, however, please make sure that the user has the ability to provide his own address to an endpoint.

gedw99 commented 2 years ago

I would also like to help on this .

one easy way that’s pure golang is to just use Nats Jetstream. It can be embedded or be an external service . That’s just my own bias though .

It has a router and security. It can also generate.

I def think an abstract API like @KaviiSuri is talking about is best though . I think you’re talking about it being abstract on both the publishing API side ?

nats can run in memory or have a store. It can also store images and other data with a message . You can google for NATS kv a d object store

gedw99 commented 2 years ago

Also there are wrappers of NATS in case you want a really generic way, so that others can use their own message routers and store.

For example https://github.com/bots-garden/capsule supports html, mqtt or NATS.

KaviiSuri commented 2 years ago

@gedw99 this seems very interesting, especially when the subscription objects need to be persisted in memory across multiple servers (maybe we could also look into redis for this).

I'll get the basic version done (with in memory store for subscriptions as a default), and we can figure out a better default store (maybe using nats) after that?

gedw99 commented 1 year ago

@KaviiSuri Nats can persis in memory or durable.

It is perfect for web push because it have various retention flags and one of them is to store the message only as long as all subscribers have not seen the message. So you can push the message to nats and nats will ensure the message is only deleted after all subscribers have had it delivered .

Nats can also store images and video in the same way only deleting it only all users have def gotten the message.

Sys does 7 million messages per sec in in memory mode on a m1 Mac book. In durable mode it’s about 700 k / sec. It’s very fast

about Redis. You could also use Redis. I only suggested nats because it golang and because the nats server can be embedded in the server binary itself . So there is nothing to setup elsewhere.

It can scale out to multiple regions even when embedded too.

you will also need accounts in order to do multi tenant. Nats has that built in. Uses jwt and so can be integrated easily with other things. Soon it will support oAuth and oidc.

I think nats would be useful for all push services , not just web push. It will make it resilient and scalable without needing any other dependencies.

nats is 100% Open source. 12 years old. matrix dendrite is built on top of NATS.

Some good links :

https://natsbyexample.com/

https://natsbyexample.com/examples/jetstream/interest-stream/go

https://github.com/ConnectEverything/nats-by-example

Event store using only nats :

https://github.com/bruth/rita

Full app on top of Rita 👍

https://github.com/bruth/kmm

https://synadia.com/newsletter

Message retention options: https://docs.nats.io/using-nats/developer/develop_jetstream/model_deep_dive