thecore_backend_commons 3.2.10 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +139 -5
- data/app/channels/push_notification_channel.rb +18 -0
- data/app/models/push_message.rb +5 -0
- data/app/models/push_subscriber.rb +21 -0
- data/db/migrate/20260616000001_create_push_subscribers.rb +15 -0
- data/db/migrate/20260616000002_create_push_messages.rb +16 -0
- data/db/seeds.rb +12 -1
- data/lib/tasks/thecore_backend_commons_tasks.rake +11 -4
- data/lib/thecore_backend_commons/push_notification_service.rb +58 -0
- data/lib/thecore_backend_commons/smtp_tester.rb +79 -0
- data/lib/thecore_backend_commons/version.rb +1 -1
- data/lib/thecore_backend_commons.rb +3 -0
- metadata +22 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 00717650bd9fe40c41bdf1c30e6372a2abbd095f461b3d1271048e9e408cb8ea
|
|
4
|
+
data.tar.gz: b613ac4217fb65df2b934dc289b6d4167eb19d0f0605ab44914bb9996385467c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c6e95d2f7f3ad718c6a1722cc679be17761179f5dac5c0dbabe9590403f3c297a66f67223366871fe35a78d5786ad73699feb30a1028d76603310526fa20cccf
|
|
7
|
+
data.tar.gz: b7d31a43dbc06cb21846674ba6d3f74bed4d2061b7f70c21c823612a13bc5af14d350d0881bd21f9750b0417d53679008405ec9dac204c3c1134dc2e6b6ff041
|
data/README.md
CHANGED
|
@@ -1,4 +1,130 @@
|
|
|
1
|
-
This is part of Thecore framework
|
|
1
|
+
This is part of [Thecore framework](https://github.com/gabrieletassoni/thecore/tree/release/3).
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Web Push notifications (VAPID)
|
|
6
|
+
|
|
7
|
+
Self-contained browser push notifications — no Firebase, no APNs, no third-party account required. The gem provides the server-side half: models, dispatch service, and ActionCable channel. The client integration guide (React + service worker) lives in the [`model_driven_api` README](../model_driven_api/README.md#web-push-vapid-from-a-react-client).
|
|
8
|
+
|
|
9
|
+
### How it works
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Browser Rails backend
|
|
13
|
+
│ │
|
|
14
|
+
│── POST subscribe ─────────►│ PushSubscriber.subscribe_for(user, endpoint:, p256dh:, auth:)
|
|
15
|
+
│ │
|
|
16
|
+
│◄─ ActionCable stream ──────│ PushNotificationChannel streams push_notifications_subscriber_N
|
|
17
|
+
│ │
|
|
18
|
+
│ [backend sends push] │ PushNotificationService.dispatch(subscriber, message)
|
|
19
|
+
│◄─ Web Push payload ────────│ → Webpush.payload_send (VAPID)
|
|
20
|
+
│ │ → message.sent_at = now
|
|
21
|
+
│── POST acknowledge ────────►│ message.update!(received_at: / read_at:)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Server configuration
|
|
25
|
+
|
|
26
|
+
VAPID keys are generated automatically the first time `rails db:seed` runs. You only need to set the contact email via RailsAdmin → Settings:
|
|
27
|
+
|
|
28
|
+
| ThecoreSettings key (`ns: :vapid`) | Purpose | Default |
|
|
29
|
+
|------------------------------------|---------|---------|
|
|
30
|
+
| `public_key` | VAPID public key (base64url) — send to browsers | generated at seed |
|
|
31
|
+
| `private_key` | VAPID private key — never expose to clients | generated at seed |
|
|
32
|
+
| `contact_email` | `mailto:` URI in VAPID `sub` claim | `""` (set this) |
|
|
33
|
+
| `max_messages_per_subscriber` | PushMessage retention cap per subscriber | `"500"` |
|
|
34
|
+
|
|
35
|
+
> **Regenerating VAPID keys invalidates all existing `PushSubscriber` records.** Every registered browser must re-subscribe.
|
|
36
|
+
|
|
37
|
+
### Models
|
|
38
|
+
|
|
39
|
+
#### `PushSubscriber`
|
|
40
|
+
|
|
41
|
+
Represents one browser/device subscription registered by a `User`.
|
|
42
|
+
|
|
43
|
+
| Column | Type | Notes |
|
|
44
|
+
|--------|------|-------|
|
|
45
|
+
| `user_id` | bigint | FK to users |
|
|
46
|
+
| `endpoint` | text | Unique; push service URL provided by the browser |
|
|
47
|
+
| `p256dh` | string | ECDH public key (base64url) |
|
|
48
|
+
| `auth` | string | Auth secret (base64url) |
|
|
49
|
+
| `user_agent` | string | Browser/OS identifier |
|
|
50
|
+
| `expired_at` | datetime | `nil` = active; set when the push service returns 410 |
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
# Upsert by endpoint (re-registering an existing browser updates the record)
|
|
54
|
+
PushSubscriber.subscribe_for(user, endpoint:, p256dh:, auth:, user_agent:)
|
|
55
|
+
|
|
56
|
+
# Scope: only active (not expired)
|
|
57
|
+
PushSubscriber.active
|
|
58
|
+
|
|
59
|
+
# Expire a subscriber (called automatically on 410 from push service)
|
|
60
|
+
subscriber.expire!
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### `PushMessage`
|
|
64
|
+
|
|
65
|
+
Records a notification payload and its lifecycle.
|
|
66
|
+
|
|
67
|
+
| Column | Type | Notes |
|
|
68
|
+
|--------|------|-------|
|
|
69
|
+
| `push_subscriber_id` | bigint | FK |
|
|
70
|
+
| `title` | string | Required |
|
|
71
|
+
| `body` | text | Required |
|
|
72
|
+
| `url` | string | URL to open on notification click (optional) |
|
|
73
|
+
| `icon` | string | Notification icon URL (optional) |
|
|
74
|
+
| `sent_at` | datetime | Populated by `PushNotificationService` on successful dispatch |
|
|
75
|
+
| `received_at` | datetime | Set by client via `acknowledge` endpoint |
|
|
76
|
+
| `read_at` | datetime | Set by client via `acknowledge` endpoint |
|
|
77
|
+
|
|
78
|
+
Old messages beyond `vapid.max_messages_per_subscriber` are pruned automatically after each dispatch (oldest first).
|
|
79
|
+
|
|
80
|
+
### `PushNotificationService`
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
ThecoreBackendCommons::PushNotificationService.dispatch(subscriber, message)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
1. Calls `Webpush.payload_send` with the subscriber's keys and the VAPID credentials from `ThecoreSettings`.
|
|
87
|
+
2. On success: sets `message.sent_at = Time.current`.
|
|
88
|
+
3. On `Webpush::ExpiredSubscription` or `Webpush::InvalidSubscription` (HTTP 410/404 from push service): calls `subscriber.expire!`.
|
|
89
|
+
4. Prunes oldest `PushMessage` records if the subscriber exceeds the retention cap.
|
|
90
|
+
5. Always returns `message` — errors are rescued and logged, never propagated.
|
|
91
|
+
|
|
92
|
+
### `PushNotificationChannel` (ActionCable)
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
# In your connection.rb — current_user must be set
|
|
96
|
+
class ApplicationCable::Connection < ActionCable::Connection::Base
|
|
97
|
+
identified_by :current_user
|
|
98
|
+
# ... authenticate and set current_user
|
|
99
|
+
end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Subscribe from the frontend:
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
// Stream for one subscriber (most common)
|
|
106
|
+
consumer.subscriptions.create(
|
|
107
|
+
{ channel: "PushNotificationChannel", subscriber_id: subscriberId },
|
|
108
|
+
{ received(data) { /* handle message */ } }
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Stream for all active subscribers of the current user
|
|
112
|
+
consumer.subscriptions.create(
|
|
113
|
+
{ channel: "PushNotificationChannel", user_id: currentUserId },
|
|
114
|
+
{ received(data) { /* handle message */ } }
|
|
115
|
+
);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The channel only streams to `subscriber_id` values that belong to `current_user` — unauthorized subscriber IDs are silently ignored.
|
|
119
|
+
|
|
120
|
+
Broadcast from anywhere in the backend:
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
PushNotificationChannel.broadcast_to(subscriber, message)
|
|
124
|
+
# Sends message.as_json to "push_notifications_subscriber_#{subscriber.id}"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
2
128
|
|
|
3
129
|
## Invio email e configurazione SMTP
|
|
4
130
|
|
|
@@ -55,12 +181,20 @@ ThecoreBackendCommons::SmtpConfig.setting(:from)
|
|
|
55
181
|
|
|
56
182
|
### Testare la configurazione SMTP
|
|
57
183
|
|
|
58
|
-
|
|
184
|
+
Il gem espone `ThecoreBackendCommons::SmtpTester` e un rake task, disponibili automaticamente in qualsiasi app che include questo gem.
|
|
185
|
+
|
|
186
|
+
**Da rake task (shell):**
|
|
59
187
|
|
|
60
188
|
```bash
|
|
61
|
-
|
|
189
|
+
rails thecore_backend_commons:smtp:test[destinatario@example.com]
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Da Rails console:**
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
ThecoreBackendCommons::SmtpTester.call("destinatario@example.com")
|
|
62
196
|
```
|
|
63
197
|
|
|
64
|
-
Se non si passa un indirizzo,
|
|
198
|
+
Se non si passa un indirizzo, in entrambi i casi viene usato `ThecoreSettings mytask.default_email`.
|
|
65
199
|
|
|
66
|
-
Lo
|
|
200
|
+
Lo tester stampa i parametri SMTP effettivamente usati (inclusi `tls` e `enable_starttls_auto`) prima di tentare la consegna. Restituisce `true`/`false` dal console e exit code `1` dal rake task in caso di errore.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class PushNotificationChannel < ApplicationCable::Channel
|
|
2
|
+
def subscribed
|
|
3
|
+
if params[:subscriber_id].present?
|
|
4
|
+
subscriber = PushSubscriber.active.find_by(id: params[:subscriber_id], user_id: current_user.id)
|
|
5
|
+
stream_from "push_notifications_subscriber_#{subscriber.id}" if subscriber
|
|
6
|
+
elsif params[:user_id].present? && params[:user_id].to_i == current_user.id
|
|
7
|
+
PushSubscriber.active.where(user_id: current_user.id).each do |sub|
|
|
8
|
+
stream_from "push_notifications_subscriber_#{sub.id}"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def unsubscribed; end
|
|
14
|
+
|
|
15
|
+
def self.broadcast_to(subscriber, message)
|
|
16
|
+
ActionCable.server.broadcast("push_notifications_subscriber_#{subscriber.id}", message.as_json)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class PushSubscriber < ApplicationRecord
|
|
2
|
+
belongs_to :user
|
|
3
|
+
has_many :push_messages, dependent: :destroy
|
|
4
|
+
validates :endpoint, presence: true, uniqueness: true
|
|
5
|
+
scope :active, -> { where(expired_at: nil) }
|
|
6
|
+
|
|
7
|
+
def expire!
|
|
8
|
+
update!(expired_at: Time.current)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.subscribe_for(user, endpoint:, p256dh: nil, auth: nil, user_agent: nil)
|
|
12
|
+
record = find_or_initialize_by(endpoint: endpoint)
|
|
13
|
+
record.user = user
|
|
14
|
+
record.p256dh = p256dh
|
|
15
|
+
record.auth = auth
|
|
16
|
+
record.user_agent = user_agent
|
|
17
|
+
record.expired_at = nil
|
|
18
|
+
record.save!
|
|
19
|
+
record
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreatePushSubscribers < ActiveRecord::Migration[7.2]
|
|
2
|
+
def change
|
|
3
|
+
create_table :push_subscribers do |t|
|
|
4
|
+
t.bigint :user_id, null: false
|
|
5
|
+
t.text :endpoint, null: false
|
|
6
|
+
t.string :p256dh
|
|
7
|
+
t.string :auth
|
|
8
|
+
t.string :user_agent
|
|
9
|
+
t.datetime :expired_at
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
add_index :push_subscribers, :endpoint, unique: true
|
|
13
|
+
add_index :push_subscribers, :user_id
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreatePushMessages < ActiveRecord::Migration[7.2]
|
|
2
|
+
def change
|
|
3
|
+
create_table :push_messages do |t|
|
|
4
|
+
t.bigint :push_subscriber_id, null: false
|
|
5
|
+
t.string :title, null: false
|
|
6
|
+
t.text :body, null: false
|
|
7
|
+
t.string :url
|
|
8
|
+
t.string :icon
|
|
9
|
+
t.datetime :sent_at
|
|
10
|
+
t.datetime :received_at
|
|
11
|
+
t.datetime :read_at
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
add_index :push_messages, :push_subscriber_id
|
|
15
|
+
end
|
|
16
|
+
end
|
data/db/seeds.rb
CHANGED
|
@@ -10,4 +10,15 @@ Thecore::Seed.save_setting :smtp, :domain, ""
|
|
|
10
10
|
Thecore::Seed.save_setting :smtp, :user_name, ""
|
|
11
11
|
Thecore::Seed.save_setting :smtp, :password, ""
|
|
12
12
|
Thecore::Seed.save_setting :smtp, :authentication, ""
|
|
13
|
-
Thecore::Seed.save_setting :smtp, :enable_starttls_auto, ""
|
|
13
|
+
Thecore::Seed.save_setting :smtp, :enable_starttls_auto, ""
|
|
14
|
+
|
|
15
|
+
puts "Loading ThecoreBackendCommons VAPID config"
|
|
16
|
+
require "web-push"
|
|
17
|
+
unless ThecoreSettings::Setting.where(ns: :vapid, key: :public_key).where.not(raw: [nil, ""]).exists?
|
|
18
|
+
vapid_key = WebPush.generate_key
|
|
19
|
+
Thecore::Seed.save_setting :vapid, :public_key, vapid_key.public_key
|
|
20
|
+
Thecore::Seed.save_setting :vapid, :private_key, vapid_key.private_key
|
|
21
|
+
puts " Generated new VAPID key pair"
|
|
22
|
+
end
|
|
23
|
+
Thecore::Seed.save_setting :vapid, :contact_email, "" unless ThecoreSettings::Setting.where(ns: :vapid, key: :contact_email).exists?
|
|
24
|
+
Thecore::Seed.save_setting :vapid, :max_messages_per_subscriber, "500" unless ThecoreSettings::Setting.where(ns: :vapid, key: :max_messages_per_subscriber).exists?
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
namespace :thecore_backend_commons do
|
|
2
|
+
namespace :smtp do
|
|
3
|
+
desc "Send a test email using ThecoreSettings SMTP config. " \
|
|
4
|
+
"Usage: rails thecore_backend_commons:smtp:test[recipient@example.com] " \
|
|
5
|
+
"(omit argument to use mytask.default_email)"
|
|
6
|
+
task :test, [:recipient] => :environment do |_t, args|
|
|
7
|
+
success = ThecoreBackendCommons::SmtpTester.call(args[:recipient])
|
|
8
|
+
exit 1 unless success
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module ThecoreBackendCommons
|
|
2
|
+
class PushNotificationService
|
|
3
|
+
MAX_MESSAGES_DEFAULT = 500
|
|
4
|
+
|
|
5
|
+
def self.dispatch(subscriber, message)
|
|
6
|
+
new(subscriber, message).dispatch
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def initialize(subscriber, message)
|
|
10
|
+
@subscriber = subscriber
|
|
11
|
+
@message = message
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def dispatch
|
|
15
|
+
send_push
|
|
16
|
+
prune_old_messages
|
|
17
|
+
@message
|
|
18
|
+
rescue => e
|
|
19
|
+
Rails.logger.error("[PushNotificationService] dispatch failed: #{e.message}")
|
|
20
|
+
@message
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def send_push
|
|
26
|
+
WebPush.payload_send(
|
|
27
|
+
message: JSON.generate(payload),
|
|
28
|
+
endpoint: @subscriber.endpoint,
|
|
29
|
+
p256dh: @subscriber.p256dh,
|
|
30
|
+
auth: @subscriber.auth,
|
|
31
|
+
vapid: vapid_options
|
|
32
|
+
)
|
|
33
|
+
@message.update!(sent_at: Time.current)
|
|
34
|
+
rescue WebPush::ExpiredSubscription, WebPush::InvalidSubscription
|
|
35
|
+
@subscriber.expire!
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def payload
|
|
39
|
+
{ title: @message.title, body: @message.body, url: @message.url, icon: @message.icon }.compact
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def vapid_options
|
|
43
|
+
{
|
|
44
|
+
public_key: ThecoreSettings::Setting.where(ns: :vapid, key: :public_key).pluck(:raw).first,
|
|
45
|
+
private_key: ThecoreSettings::Setting.where(ns: :vapid, key: :private_key).pluck(:raw).first,
|
|
46
|
+
subject: "mailto:#{ThecoreSettings::Setting.where(ns: :vapid, key: :contact_email).pluck(:raw).first.presence || 'admin@example.com'}"
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def prune_old_messages
|
|
51
|
+
limit = ThecoreSettings::Setting.where(ns: :vapid, key: :max_messages_per_subscriber).pluck(:raw).first&.to_i || MAX_MESSAGES_DEFAULT
|
|
52
|
+
count = @subscriber.push_messages.count
|
|
53
|
+
return unless count > limit
|
|
54
|
+
oldest_ids = @subscriber.push_messages.order(created_at: :asc).limit(count - limit).pluck(:id)
|
|
55
|
+
PushMessage.where(id: oldest_ids).delete_all
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ThecoreBackendCommons
|
|
4
|
+
# Sends a test email using the SMTP settings from ThecoreSettings.
|
|
5
|
+
# Usable from the Rails console or via the rake task.
|
|
6
|
+
#
|
|
7
|
+
# From rails console:
|
|
8
|
+
# ThecoreBackendCommons::SmtpTester.call("you@example.com")
|
|
9
|
+
#
|
|
10
|
+
# From the shell:
|
|
11
|
+
# rails thecore_backend_commons:smtp:test[you@example.com]
|
|
12
|
+
class SmtpTester
|
|
13
|
+
def self.call(recipient = nil)
|
|
14
|
+
new(recipient).call
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(recipient = nil)
|
|
18
|
+
@recipient = recipient.presence ||
|
|
19
|
+
ThecoreSettings::Setting.find_by(ns: :mytask, key: :default_email)&.raw
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
validate!
|
|
24
|
+
print_settings
|
|
25
|
+
send_mail
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def validate!
|
|
31
|
+
raise ArgumentError, "No recipient given and mytask.default_email is not configured." if @recipient.blank?
|
|
32
|
+
raise ArgumentError, "smtp.address is not configured in ThecoreSettings." if opts[:address].blank?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def print_settings
|
|
36
|
+
puts "SMTP settings:"
|
|
37
|
+
puts " address: #{opts[:address]}"
|
|
38
|
+
puts " port: #{opts[:port]}"
|
|
39
|
+
puts " domain: #{opts[:domain].inspect}"
|
|
40
|
+
puts " user_name: #{opts[:user_name].inspect}"
|
|
41
|
+
puts " authentication: #{opts[:authentication].inspect}"
|
|
42
|
+
puts " tls: #{opts[:tls]}"
|
|
43
|
+
puts " enable_starttls_auto:#{opts[:enable_starttls_auto]}"
|
|
44
|
+
puts " from: #{SmtpConfig.setting(:from).inspect}"
|
|
45
|
+
puts ""
|
|
46
|
+
puts "Sending test email to: #{@recipient}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def send_mail
|
|
50
|
+
from_address = SmtpConfig.setting(:from).presence || "noreply@mytask.local"
|
|
51
|
+
delivery_opts = opts
|
|
52
|
+
|
|
53
|
+
mail = Mail.new do
|
|
54
|
+
from from_address
|
|
55
|
+
to delivery_opts[:address] # placeholder; overridden below
|
|
56
|
+
subject "[MyTask] SMTP test — #{Time.current.strftime('%Y-%m-%d %H:%M:%S %Z')}"
|
|
57
|
+
body "This is an automated SMTP connectivity test sent from MyTask.\n\n" \
|
|
58
|
+
"If you received this message, the SMTP configuration is working correctly.\n\n" \
|
|
59
|
+
"Settings used:\n" \
|
|
60
|
+
" address: #{delivery_opts[:address]}\n" \
|
|
61
|
+
" port: #{delivery_opts[:port]}\n" \
|
|
62
|
+
" tls: #{delivery_opts[:tls]}\n" \
|
|
63
|
+
" auth: #{delivery_opts[:authentication].inspect}"
|
|
64
|
+
end
|
|
65
|
+
mail.to = @recipient
|
|
66
|
+
mail.delivery_method(:smtp, delivery_opts)
|
|
67
|
+
mail.deliver!
|
|
68
|
+
puts "OK: email delivered successfully."
|
|
69
|
+
true
|
|
70
|
+
rescue StandardError => e
|
|
71
|
+
puts "ERROR: #{e.class}: #{e.message}"
|
|
72
|
+
false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def opts
|
|
76
|
+
@opts ||= SmtpConfig.delivery_options
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "ostruct"
|
|
2
|
+
require "web-push" # gem for VAPID web push (pushpad/web-push, actively maintained)
|
|
2
3
|
require "thecore_auth_commons"
|
|
3
4
|
require "thecore_background_jobs"
|
|
4
5
|
require "rails-i18n"
|
|
@@ -14,6 +15,8 @@ require "seed_dump"
|
|
|
14
15
|
require "thecore_backend_commons/version"
|
|
15
16
|
require "thecore_backend_commons/engine"
|
|
16
17
|
require "thecore_backend_commons/smtp_config"
|
|
18
|
+
require "thecore_backend_commons/smtp_tester"
|
|
19
|
+
require "thecore_backend_commons/push_notification_service"
|
|
17
20
|
|
|
18
21
|
module ThecoreBackendCommons
|
|
19
22
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: thecore_backend_commons
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gabriele Tassoni
|
|
@@ -177,6 +177,20 @@ dependencies:
|
|
|
177
177
|
- - "~>"
|
|
178
178
|
- !ruby/object:Gem::Version
|
|
179
179
|
version: '3.4'
|
|
180
|
+
- !ruby/object:Gem::Dependency
|
|
181
|
+
name: web-push
|
|
182
|
+
requirement: !ruby/object:Gem::Requirement
|
|
183
|
+
requirements:
|
|
184
|
+
- - "~>"
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: '3.0'
|
|
187
|
+
type: :runtime
|
|
188
|
+
prerelease: false
|
|
189
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
190
|
+
requirements:
|
|
191
|
+
- - "~>"
|
|
192
|
+
- !ruby/object:Gem::Version
|
|
193
|
+
version: '3.0'
|
|
180
194
|
description: Wrapper to keep all the common libraries and setups needed by Thecore
|
|
181
195
|
UI Backend(s).
|
|
182
196
|
email:
|
|
@@ -188,7 +202,10 @@ files:
|
|
|
188
202
|
- README.md
|
|
189
203
|
- Rakefile
|
|
190
204
|
- app/channels/activity_log_channel.rb
|
|
205
|
+
- app/channels/push_notification_channel.rb
|
|
191
206
|
- app/mailers/concerns/smtp_deliverable.rb
|
|
207
|
+
- app/models/push_message.rb
|
|
208
|
+
- app/models/push_subscriber.rb
|
|
192
209
|
- config/initializers/abilities.rb
|
|
193
210
|
- config/initializers/add_to_db_migrations.rb
|
|
194
211
|
- config/initializers/after_initialize.rb
|
|
@@ -203,11 +220,15 @@ files:
|
|
|
203
220
|
- config/locales/en.yml
|
|
204
221
|
- config/locales/it.devise.custom.yml
|
|
205
222
|
- config/locales/it.yml
|
|
223
|
+
- db/migrate/20260616000001_create_push_subscribers.rb
|
|
224
|
+
- db/migrate/20260616000002_create_push_messages.rb
|
|
206
225
|
- db/seeds.rb
|
|
207
226
|
- lib/tasks/thecore_backend_commons_tasks.rake
|
|
208
227
|
- lib/thecore_backend_commons.rb
|
|
209
228
|
- lib/thecore_backend_commons/engine.rb
|
|
229
|
+
- lib/thecore_backend_commons/push_notification_service.rb
|
|
210
230
|
- lib/thecore_backend_commons/smtp_config.rb
|
|
231
|
+
- lib/thecore_backend_commons/smtp_tester.rb
|
|
211
232
|
- lib/thecore_backend_commons/version.rb
|
|
212
233
|
homepage: https://github.com/gabrieletassoni/thecore_backend_commons
|
|
213
234
|
licenses: []
|