Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: HTML5 Push Notifications #10884

Closed
wants to merge 23 commits into from

Conversation

jamesorlakin
Copy link
Contributor

@jamesorlakin jamesorlakin commented Mar 29, 2020

Edit: This PR is currently being rewritten. I'll force push it when I'm done but feel free to see my pushNotificationsNew branch in the meantime.


Fixes #10804, #9327.

This is a 'mostly there but unpolished' PR to implement push notifications using the Web Push and Notification API, powered by the webpush-go library. I feel this is a very useful piece of functionality to be productive, especially if emails aren't configured.


To give an overview:

  1. Web Push servers need VAPID keys, a Base64'd asymmetric keypair. Gitea will generate a keypair on startup and add to app.ini if not set - similar to JWT secrets.
  2. The user pushes a button to opt-in to notifications.
  3. The JS requests the notification permission from the user. This permission also grants access to the service worker's PushManager to enable Web Push. For Firefox at least it needs to be triggered by a user event to reduce spam.
  4. If granted, the JS subscribes the service worker for Web Push using the VAPID public key embedded in the JS config object. This will then return a URL, auth token, and a signing key from the browser's vendor. (Further requests to subscribe will return the same details) Currently this includes Chrome, Firefox, and Edge. Safari has its own proprietary system I haven't looked into.
  5. The URL, auth token, and signing key are POSTed to Gitea under a new endpoint. The endpoint will send a test notification in order to verify the provided details before storing it against the user in the Web Push subscription table.
  6. Done! 🎉

Whenever a notification model is created or updated we send a push notification:

  1. Fetch all the subscribed pushes for a user.
  2. Loop through each one and send a JSON object containing a message about the issue/PR and a URL.
  3. If the HTTP code isn't 201 (created), delete the subscription. Under the spec 410 (gone) is sent for expired subscriptions but I think there's other codes if the auth/signing is wrong, etc. (Edit: This is a bit dumb in retrospect. We should ignore 5xx errors as they shouldn't be our fault and are likely temporary.)
  4. The service worker receives the JSON object and uses the Notification API to show a corresponding notification.

As a result the service worker is required to be enabled - it's quite key! Having this lays the foundation for live messaging outside of notifications later on: updating the bell icon, informing the user of new activity while viewing a PR, etc.

This is WIP because I have some things outstanding/discussing:

  • Decouple the sending of pushes from the actual web request that triggered it. Would a simple goroutine be sufficient? Let's say a push service took 10s... ouch!
  • Refactor web push to be a module using the notification service entrypoint, rather than hijacking events on the notification model
  • Figure out the Notifier queue system and use that instead of hijacking the notification model
  • Localisation. I can do most of the server-side bits but not sure about the JS button. It won't show until it passes feature discovery. I guess I should put it in the template and add all required localised messages as custom attributes.
  • Add the repo name to the notification - oops!
  • Only remove subscription when encountering a 4xx error.
  • Rename .ini config names
  • More error handling on the JS side
  • Figure out a nicer/more obvious UI to opt-in.
  • Allow the user to opt-out
  • Testing?!

And as always, even minor nits are welcome! A lot of the logic is in the models, but I need it to automatically delete old hooks and react to notifications. It was a module but I caused an import cycle... 🤦‍♂


image
Gitea notifications new issue

@hilariocoelho
Copy link
Contributor

Wow! 👏
@6543 @zeripath come have a look at this!

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Mar 30, 2020
@lunny lunny added the type/feature Completely new functionality. Can only be merged if feature freeze is not active. label Mar 30, 2020
@6543
Copy link
Member

6543 commented Apr 10, 2020

Can you resolve the conflict - Ill look at it afterwards :)

@@ -131,7 +131,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `REACTIONS`: All available reactions. Allow users react with different emoji's.
- `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
- `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page.
- `USE_SERVICE_WORKER`: **true**: Whether to enable a Service Worker to cache frontend assets.
- `USE_SERVICE_WORKER`: **true**: Whether to enable a Service Worker to cache frontend assets and enable push notifications on supported browsers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't piggy-back on the service-worker pref, these are two distinct features. Add a new one, like USE_PUSH_NOTIFICATIONS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True - I'll pop a note saying the service worker is required as well.

@@ -289,6 +289,8 @@ set name for unique queues. Individual queues will default to

- `INSTALL_LOCK`: **false**: Disallow access to the install page.
- `SECRET_KEY`: **\<random at every install\>**: Global secret key. This should be changed.
- `WEB_PUSH_PUBLIC_KEY`: **\<random at every install\>**: VAPID key pair used for Web Push notifications
- `WEB_PUSH_PRIVATE_KEY`: **\<random at every install\>**: VAPID key pair used for Web Push notifications
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PUSH_NOTIFICATIONS_PUBLIC_KEY and PUSH_NOTIFICATIONS_PRIVATE_KEY would be more better imho.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the codebase I've tried to be consistent with 'Web Push', but I could change the user-facing config quite easily

@jamesorlakin
Copy link
Contributor Author

Go's modules confuse me a fair bit!

So CI is failing because my machine adds go 1.13 to the vendored package's go.mod and CI seemingly doesn't:
image

I'm using Go 1.14.1 locally, but if I try and do make vendor with 1.13 the vendored modules.txt goes ...haywire:
image

I'm using Windows if that makes any difference.

@jamesorlakin
Copy link
Contributor Author

jamesorlakin commented Apr 13, 2020

Cheers, but it's insistent on putting the go 1.13 back into that file even with make fmt && make vendor on Go 1.14.1 for me. 😕

@6543
Copy link
Member

6543 commented Apr 13, 2020

Cheers, but it's insistent on putting the go 1.13 back into that file even with make fmt && make vendor on Go 1.14.1 for me.

this could be indeed a windows only issue

@stale
Copy link

stale bot commented Jun 12, 2020

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs during the next 2 months. Thank you for your contributions.

@stale stale bot added the issue/stale label Jun 12, 2020
@6543
Copy link
Member

6543 commented Jun 12, 2020

Pleace resolve conflicts

@stale stale bot removed the issue/stale label Jun 12, 2020
@silverwind
Copy link
Member

silverwind commented Jun 18, 2020

Serviceworker implementation has changed significantly since this PR as it's now handled by workbox so this will need quite a few updates. Also I think I'm not generally against adding push notifications but it needs to be integrated with our existing EventSource-based notifications, probably sharing much code with that implementation.

@jamesorlakin
Copy link
Contributor Author

Hi all,

Yep, this one certainly needs updating! I was testing the water with how push notification implementations work for sure. It's been a while since I've had time to dig through Gitea's codebase but I'll try and block some time this weekend to have a play.

I might restart this from the latest commit from master. I haven't really touched Workbox before, but I'm guessing I'll be able to add push events handlers to the generated serviceworker? I discovered very late on about the queue-based event system so I'd be keen to try and utilise that as well.

This might take quite a while in the grand scheme of things, and we'd have to accept it wouldn't work (or do we try to fallback to AJAX as in #10961?) in IE or Safari.

Thanks.

@6543
Copy link
Member

6543 commented Jun 25, 2020

@jamesorlakin IE support was droped in v1.13

@jamesorlakin
Copy link
Contributor Author

Even better 👍

@silverwind
Copy link
Member

I imagine the new implementation can re-use the EventSource to add a new notification event and then push that to the serviceworker for display. These parts don't depend on workbox, but they do have this example which includes push notifications. Also, I wonder if desktop browsers would need a fallback to the older web notifications API.

@mohe2015
Copy link
Contributor

mohe2015 commented Jun 28, 2020

@silverwind

Also, I wonder if desktop browsers would need a fallback to the older web notifications API.

I'm pretty sure they don't as I have implemented push notifications for desktop and mobile a while ago using the standard APIs.

Edit: https://caniuse.com/#feat=push-api also shows support for desktop browsers.
Edit-Edit: Safari seems to not support the Push API but only the Web Notifications API so we would need to discuss whether we want to support that feature for this browser too.

@silverwind
Copy link
Member

silverwind commented Jun 29, 2020

I guess fallback to web notification is a requirement because there are scenarios where serviceworker will be disabled (either by configuration or when the user is in private browsing mode (which may disable serviceworker in some circumstances)). Maybe there is a third-party module that can handle such a fallback.

@mohe2015
Copy link
Contributor

mohe2015 commented Jun 29, 2020

@silverwind

I guess fallback to web notification is a requirement because there are scenarios where serviceworker will be disabled

There is still the default notification system (email, just the bell in the top right of the web interface)
Of course people may still want notifications but when using Web Notifications they need to keep the tab open and you would need to poll or use something like websockets to get updates.

(either by configuration

I think then a fallback is not intended either (or do you mean disabled in the browser?)

or when the user is in private browsing mode (which may disable serviceworker in some circumstances)).

I think then you can't even enable the push notifications and I don't think this is the way most people who want push notifications use their browser.

Maybe there is a third-party module that can handle such a fallback.

Unfortunately I don't think so as the technology is quite different (Web Notifications is just showing a Notification and Push Notification is also getting updates to the browser)

So in my personal opinion I would see Push Notifications as a progressive enhancement so I don't think a fallback is required. This would also simplify the implementation.

@silverwind
Copy link
Member

you would need to poll or use something like websockets to get updates.

We do have SSE/Eventsource implemented for server-side push things. Currently it's only used to update the notification count. We probably will want to switch this to websockets because SSE has some quirks and limitations.

What I mean is that web notifications would still be possible even without a service worker available while push notifications require it, but I guess we don't need to handle it as it would complicate the implementation (e.g. need to handle multiple tabs racing to push the same notification etc).

@jamesorlakin
Copy link
Contributor Author

jamesorlakin commented Jun 29, 2020

We do have SSE/Eventsource implemented for server-side push things.

Correct me if I'm wrong here, but I thought that was handled by an AJAX timer?

I think for now we should stick to feature discovery (does the browser support SWs, push, etc) and only show the option if the service worker is enabled in the config. Still showing notifications outside of Web Push is a nice idea, but we need to make it clear this fallback mode would not provide notifications if no Gitea tab is open.

@silverwind
Copy link
Member

See https://github.com/go-gitea/gitea/blob/master/web_src/js/features/notification.js#L30, it's using EventSource and falls back to ajax, at least that's the plan.

I think for now we should stick to feature discovery (does the browser support SWs, push, etc) and only show the option if the service worker is enabled in the config

I would make a second option and only enable push notifications if both are enabled, that way one can still benefit from SW caching while keeping notifications off if desired.

@stale
Copy link

stale bot commented Sep 5, 2020

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs during the next 2 months. Thank you for your contributions.

@stale stale bot added the issue/stale label Sep 5, 2020
@stale
Copy link

stale bot commented Nov 9, 2020

This pull request has been automatically closed because of inactivity. You can re-open it if needed.

@stale stale bot closed this Nov 9, 2020
@6543 6543 reopened this Nov 9, 2020
@stale stale bot removed the issue/stale label Nov 9, 2020
@6543 6543 added the issue/confirmed Issue has been reviewed and confirmed to be present or accepted to be implemented label Nov 9, 2020
@catdevnull
Copy link
Contributor

Is this ever gonna land? :#

@jamesorlakin
Copy link
Contributor Author

Afraid it's unlikely from me - apologies to everyone for leaving this in an unfinished state.
Circumstances have changed such that I'm unfortunately no longer an active Gitea user (instead being stuck with an old Bitbucket instance 😢) and thus won't have the time or motivation to really continue with changes.

I absolutely adore all the effort put into this project and wish for it to thrive for years to come. For anybody looking to take this on feel free to copy any of the code thus far as your own. If there's any questions or a later PR feel free to tag me if you'd like. Cheers!

@go-gitea go-gitea locked and limited conversation to collaborators Apr 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
issue/confirmed Issue has been reviewed and confirmed to be present or accepted to be implemented lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. type/feature Completely new functionality. Can only be merged if feature freeze is not active.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Notifications using Javascript Notification API
8 participants