Skip to content

Commit 7c969ba

Browse files
committed
Add support for VAPID
- Add support for VAPID - Add dedicated config file - Closes #18 #19
1 parent 37c3b26 commit 7c969ba

10 files changed

+308
-26
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Changelog
22

3-
All Notable changes to `laravel-notification-channels/webpus` will be documented in this file
3+
All Notable changes to `laravel-notification-channels/webpush` will be documented in this file
4+
5+
## 1.0.0 - 2017-03-25
6+
7+
- Added support for VAPID.
8+
- Added dedicated config file.
49

510
## 0.2.0 - 2017-01-26
611

README.md

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
This package makes it easy to send web push notifications with Laravel.
1313

14-
1514
## Installation
1615

1716
You can install the package via composer:
@@ -30,17 +29,7 @@ First you must install the service provider:
3029
],
3130
```
3231

33-
Then configure [Google Cloud Messaging](https://console.cloud.google.com) by setting your `key` and `sender_id`:
34-
35-
``` php
36-
// config/services.php
37-
'gcm' => [
38-
'key' => '',
39-
'sender_id' => ',
40-
],
41-
```
42-
43-
You need to add the `NotificationChannels\WebPush\HasPushSubscriptions` in your `User` model:
32+
Add the `NotificationChannels\WebPush\HasPushSubscriptions` trait to your `User` model:
4433

4534
``` php
4635
use NotificationChannels\WebPush\HasPushSubscriptions;
@@ -57,12 +46,30 @@ Next publish the migration with:
5746
php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="migrations"
5847
```
5948

60-
After the migration has been published you can create the `push_subscriptions` table by running the migrations:
49+
Run the migrate command to create the necessary table:
6150

6251
``` bash
6352
php artisan migrate
6453
```
6554

55+
You can also publish the config file with:
56+
57+
``` bash
58+
php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider" --tag="config"
59+
```
60+
61+
Generate the VAPID keys with (required for browser authentication) with:
62+
63+
``` bash
64+
php artisan webpush:vapid
65+
```
66+
67+
This command will set `VAPID_PUBLIC_KEY` and `VAPID_PRIVATE_KEY`in your `.env` file.
68+
69+
__These keys must be safely stored and should not change.__
70+
71+
If you still want support [Google Cloud Messaging](https://console.cloud.google.com) set the `GCM_KEY` and `GCM_SENDER_ID` in your `.env` file.
72+
6673
## Usage
6774

6875
Now you can use the channel in your `via()` method inside the notification as well as send a web push notification:

composer.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
],
1414
"require": {
1515
"php": ">=5.6.4",
16-
"illuminate/notifications": "5.3.*|5.4.*",
17-
"illuminate/support": "5.1.*|5.2.*|5.3.*|5.4.*",
18-
"minishlink/web-push": "1.1.*"
16+
"illuminate/notifications": "^5.3",
17+
"illuminate/support": "^5.1",
18+
"minishlink/web-push": "^1.4.1"
1919
},
2020
"require-dev": {
2121
"mockery/mockery": "^0.9.5",
22-
"phpunit/phpunit": "5.*",
23-
"orchestra/testbench": "3.4.*"
22+
"phpunit/phpunit": "^5.7",
23+
"orchestra/testbench": "^3.4.6"
2424
},
2525
"autoload": {
2626
"psr-4": {

config/webpush.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
return [
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| VAPID Authentication
8+
|--------------------------------------------------------------------------
9+
|
10+
| You'll need to create a public and private key for your server.
11+
| These keys must be safely stored and should not change.
12+
|
13+
*/
14+
15+
'vapid' => [
16+
'subject' => env('VAPID_SUBJECT'),
17+
'public_key' => env('VAPID_PUBLIC_KEY'),
18+
'private_key' => env('VAPID_PRIVATE_KEY'),
19+
'pem_file' => env('VAPID_PEM_FILE'),
20+
],
21+
22+
/*
23+
|--------------------------------------------------------------------------
24+
| Google Cloud Messaging
25+
|--------------------------------------------------------------------------
26+
|
27+
| Deprecated and optional. It's here only for compatibility reasons.
28+
|
29+
*/
30+
31+
'gcm' => [
32+
'key' => env('GCM_KEY'),
33+
'sender_id' => env('GCM_SENDER_ID'),
34+
],
35+
36+
];

src/VapidKeysGenerateCommand.php

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
namespace NotificationChannels\WebPush;
4+
5+
use Illuminate\Support\Str;
6+
use Minishlink\WebPush\VAPID;
7+
use Illuminate\Console\Command;
8+
use Illuminate\Console\ConfirmableTrait;
9+
10+
class VapidKeysGenerateCommand extends Command
11+
{
12+
use ConfirmableTrait;
13+
14+
/**
15+
* @var string
16+
*/
17+
protected $signature = 'webpush:vapid
18+
{--show : Display the keys instead of modifying files}
19+
{--force : Force the operation to run when in production}';
20+
21+
/**
22+
* @var string
23+
*/
24+
protected $description = 'Generate VAPID keys.';
25+
26+
/**
27+
* Execute the console command.
28+
*
29+
* @return mixed
30+
*/
31+
public function handle()
32+
{
33+
$keys = VAPID::createVapidKeys();
34+
35+
if ($this->option('show')) {
36+
$this->line('<comment>VAPID_PUBLIC_KEY='.$keys['publicKey'].'</comment>');
37+
$this->line('<comment>VAPID_PRIVATE_KEY='.$keys['privateKey'].'</comment>');
38+
return;
39+
}
40+
41+
if (! $this->setKeysInEnvironmentFile($keys)) {
42+
return;
43+
}
44+
45+
$this->info('VAPID keys set successfully.');
46+
}
47+
48+
/**
49+
* Set the keys in the environment file.
50+
*
51+
* @param array $keys
52+
* @return bool
53+
*/
54+
protected function setKeysInEnvironmentFile($keys)
55+
{
56+
$currentKeys = $this->laravel['config']['webpush.vapid'];
57+
58+
if (strlen($currentKeys['public_key']) !== 0 && (! $this->confirmToProceed())) {
59+
return false;
60+
}
61+
62+
$this->writeNewEnvironmentFileWith($keys);
63+
64+
return true;
65+
}
66+
67+
/**
68+
* Write a new environment file with the given keys.
69+
*
70+
* @param array $keys
71+
* @return void
72+
*/
73+
protected function writeNewEnvironmentFileWith($keys)
74+
{
75+
$contents = file_get_contents($this->laravel->environmentFilePath());
76+
77+
if (! Str::contains($contents, 'VAPID_PUBLIC_KEY')) {
78+
$contents .= PHP_EOL.'VAPID_PUBLIC_KEY=';
79+
}
80+
81+
if (! Str::contains($contents, 'VAPID_PRIVATE_KEY')) {
82+
$contents .= PHP_EOL.'VAPID_PRIVATE_KEY=';
83+
}
84+
85+
$contents = preg_replace(
86+
[
87+
$this->keyReplacementPattern('VAPID_PUBLIC_KEY'),
88+
$this->keyReplacementPattern('VAPID_PRIVATE_KEY'),
89+
],
90+
[
91+
'VAPID_PUBLIC_KEY='.$keys['publicKey'],
92+
'VAPID_PRIVATE_KEY='.$keys['privateKey'],
93+
],
94+
$contents
95+
);
96+
97+
file_put_contents($this->laravel->environmentFilePath(), $contents);
98+
}
99+
100+
/**
101+
* Get a regex pattern that will match env $keyName with any key.
102+
*
103+
* @param string $keyName
104+
* @return string
105+
*/
106+
protected function keyReplacementPattern($keyName)
107+
{
108+
$key = $this->laravel['config']['webpush.vapid'];
109+
110+
if ($keyName === 'VAPID_PUBLIC_KEY') {
111+
$key = $key['public_key'];
112+
} else {
113+
$key = $key['private_key'];
114+
}
115+
116+
$escaped = preg_quote('='.$key, '/');
117+
118+
return "/^{$keyName}{$escaped}/m";
119+
}
120+
}

src/WebPushChannel.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ public function send($notifiable, Notification $notification)
5151
}
5252

5353
/**
54-
* @param array|bool $response
55-
* @param \Illuminate\Database\Eloquent\Collection $subscriptions
54+
* @param array|bool $response
55+
* @param \Illuminate\Database\Eloquent\Collection $subscriptions
56+
* @return void
5657
*/
5758
protected function deleteInvalidSubscriptions($response, $subscriptions)
5859
{

src/WebPushServiceProvider.php

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,24 @@
22

33
namespace NotificationChannels\WebPush;
44

5+
use Illuminate\Support\Str;
56
use Minishlink\WebPush\WebPush;
67
use Illuminate\Support\ServiceProvider;
78

89
class WebPushServiceProvider extends ServiceProvider
910
{
11+
/**
12+
* Register the application services.
13+
*
14+
* @return void
15+
*/
16+
public function register()
17+
{
18+
$this->commands([VapidKeysGenerateCommand::class]);
19+
20+
$this->mergeConfigFrom(__DIR__.'/../config/webpush.php', 'webpush');
21+
}
22+
1023
/**
1124
* Bootstrap the application services.
1225
*
@@ -17,23 +30,61 @@ public function boot()
1730
$this->app->when(WebPushChannel::class)
1831
->needs(WebPush::class)
1932
->give(function () {
20-
return new WebPush([
21-
'GCM' => config('services.gcm.key'),
22-
]);
33+
return new WebPush($this->webPushConfig());
2334
});
2435

2536
if ($this->app->runningInConsole()) {
2637
$this->definePublishing();
2738
}
2839
}
2940

41+
/**
42+
* @return array
43+
*/
44+
protected function webPushConfig()
45+
{
46+
$config = [];
47+
$webpush = config('webpush');
48+
$publicKey = $webpush['vapid']['public_key'];
49+
$privateKey = $webpush['vapid']['private_key'];
50+
51+
if (! empty($webpush['gcm']['key'])) {
52+
$config['GCM'] = $webpush['gcm']['key'];
53+
}
54+
55+
if (empty($publicKey) || empty($privateKey)) {
56+
return $config;
57+
}
58+
59+
$config['VAPID'] = compact('publicKey', 'privateKey');
60+
$config['VAPID']['subject'] = $webpush['vapid']['subject'];
61+
62+
if (empty($config['VAPID']['subject'])) {
63+
$config['VAPID']['subject'] = url('/');
64+
}
65+
66+
if (! empty($webpush['vapid']['pem_file'])) {
67+
$config['VAPID']['pemFile'] = $webpush['vapid']['pem_file'];
68+
69+
if (Str::startsWith($config['VAPID']['pemFile'], 'storage')) {
70+
$config['VAPID']['pemFile'] = base_path($config['VAPID']['pemFile']);
71+
}
72+
}
73+
74+
return $config;
75+
}
76+
3077
/**
3178
* Define the publishable migrations and resources.
3279
*
3380
* @return void
3481
*/
3582
protected function definePublishing()
3683
{
84+
$this->publishes([
85+
__DIR__.'/../config/webpush.php' => config_path('webpush.php'),
86+
], 'config');
87+
3788
if (! class_exists('CreatePushSubscriptionsTable')) {
3889
$timestamp = date('Y_m_d_His', time());
3990

tests/ChannelTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Mockery;
66
use Minishlink\WebPush\WebPush;
7-
use NotificationChannels\WebPush\WebPushChannel;
7+
use NotificationChannels\WebPush\WebPushChannel;
88

99
class ChannelTest extends TestCase
1010
{

0 commit comments

Comments
 (0)