Skip to content

Instantly share code, notes, and snippets.

@lanches-kurashita
Last active May 8, 2017 12:13
Show Gist options
  • Save lanches-kurashita/443c42982a6543a8b846d9a554d83408 to your computer and use it in GitHub Desktop.
Save lanches-kurashita/443c42982a6543a8b846d9a554d83408 to your computer and use it in GitHub Desktop.
Webpush on rails

a piece of Code for webpush on rails

# app/views/layout/application.html.slim
doctype html
html
head
link rel='manifest' href='/manifest.json'
= stylesheet_link_tag 'application', media: 'all'
= javascript_tag do
= raw "window.vapidPublicKey = new Uint8Array(#{vapid_public_key});\n"
...
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :vapid_public_key
def vapid_public_key
@decoded_vapid_public_key ||= Base64.urlsafe_decode64(ENV['VAPID_PUBLIC_KEY']).bytes
end
end
class Device < ApplicationRecord
belongs_to :user
validates :user_id, presence: true
validates :endpoint, presence: true, uniqueness: { scope: :user_id }
validates :p256dh, presence: true
validates :auth, presence: true
end
class DevicesController < ApplicationController
def create
device = current_user.devices.find_or_initialize_by endpoint: params[:subscription][:endpoint]
device.attributes = device_params
device.save! if device.changed?
head :ok
end
private
def device_params
params.require(:subscription).permit(%i(endpoint p256dh auth))
end
end
ENV['VAPID_PUBLIC_KEY'] #=> "BDIzN5ppEokkmLdcwc5hlQoZavhXA0PmFdLWicfiD-oyg_b4n42rQ08LQfPlNtkG9P6FgmuqPPCic2bzqnDistA="
ENV['VAPID_PRIVATE_KEY'] #=> "A7Aok-dAKnj19Jmb8r-ujb5L5CVdjgRc_WyBwa0_Lw4="
gem 'webpush'
gem 'serviceworker-rails'
<!-- index.html -->
<link rel="manifest" href="/manifest.json">
# public/manifest.json
{
"name": "My App",
"short_name": "my-app",
"start_url": "/",
"icons": [
{
"src": "/icon.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
# config/routes.rb
resources :devices, only: [:create]
// app/assets/javascripts/serviceworker-companion.js
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/serviceworker.js', { scope: './' })
.then(function(reg) {
console.log('[Page] Service worker registered!');
// プッシュ通知の購読
reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: window.vapidPublicKey,
}).then(subscribeSuccess);
});
}
var subscribeSuccess = function(subscription){
params = {
subscription: subscription.toJSON();
}
$.ajax({
type: 'POST',
url: '/devices',
data: params
});
}
// app/assets/javascripts/serviceworker.js.erb
var CACHE_VERSION = 'v1';
var CACHE_NAME = CACHE_VERSION + ':sw-cache-';
function onInstall(event) {
console.log('[Serviceworker]', "Installing!", event);
event.waitUntil(self.skipWaiting());
}
function onActivate(event) {
console.log('[Serviceworker]', "Activating!", event);
event.waitUntil(self.clients.claim());
}
// Borrowed from https://github.com/TalAter/UpUp
function onFetch(event) {
event.respondWith(
// try to return untouched request from network first
fetch(event.request).catch(function() {
// if it fails, try to return request from the cache
return caches.match(event.request).then(function(response) {
if (response) {
return response;
}
// if not found in cache, return default offline content for navigate requests
if (event.request.mode === 'navigate' ||
(event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
console.log('[Serviceworker]', "Fetching offline content", event);
return caches.match('/offline.html');
}
})
})
);
}
self.addEventListener('install', onInstall);
self.addEventListener('activate', onActivate);
self.addEventListener('fetch', onFetch);
// プッシュ通知を受け取った時の処理
self.addEventListener('push', function(event){
var title = (event.data && event.data.text()) || 'テストプッシュ';
var data = {
body: "",
tag: "push-simple-demo-notification-tag",
icon: '/icon.jpg',
vibrate: [400,100,400], // ミリ秒単位で振動と休止の時間を交互に任意の回数だけ配列に格納
data: {
link_to: '/'
}
};
event.waitUntil(self.registration.showNotification(title, data));
});
// 通知をクリックした時の処理
self.addEventListener("notificationclick", function(event) {
event.notification.close();
var link_to = event.notification.data.link_to;
event.waitUntil(clients.matchAll({
type: "window"
}).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
var regexp = new RegExp(link_to + '$');
var match = client.url.match(regexp);
if (match != null && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
return clients.openWindow(link_to);
}
}));
});
class User < ApplicationRecord
has_many :devices, dependent: :destroy
end
# One-time, on the server
vapid_key = Webpush.generate_key
# Save these in your application server settings
vapid_key.public_key
# => "BDIzN5ppEokkmLdcwc5hlQoZavhXA0PmFdLWicfiD-oyg_b4n42rQ08LQfPlNtkG9P6FgmuqPPCic2bzqnDistA="
vapid_key.private_key
# => "A7Aok-dAKnj19Jmb8r-ujb5L5CVdjgRc_WyBwa0_Lw4="
# app/services/webpush_service.rb
class WebpushService
def initialize(user_id: nil)
@user_id = user_id
end
def webpush_clients(message)
devices.each do |device|
webpush device, message
end
end
def webpush(device, message)
Webpush.payload_send(
message: message,
endpoint: device.endpoint,
p256dh: device.p256dh,
auth: device.auth,
ttl: 24 * 60 * 60,
vapid: {
public_key: ENV['VAPID_PUBLIC_KEY'],
private_key: ENV['VAPID_PRIVATE_KEY']
}
)
end
private
def devices
@user_id.present? ? Device.where(user_id: @user_id) : Device.all
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment