Skip to content

Commit

Permalink
feat: Uptime Webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
allysonsilva committed Feb 27, 2024
1 parent 5843d82 commit 7840e1f
Show file tree
Hide file tree
Showing 14 changed files with 297 additions and 19 deletions.
78 changes: 69 additions & 9 deletions app/Http/Controllers/FrontController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,43 @@

namespace App\Http\Controllers;

use App\Models\Tag;
use App\Models\Guest;
use App\Models\Article;
use App\Models\Category;
use Illuminate\View\View;
use App\View\Shared\HomeSeo;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Cache;
use App\Notifications\UptimeMonitor;
use Illuminate\View\AnonymousComponent;
use App\Http\Requests\FrontSearchRequest;
use GrahamCampbell\GitHub\Facades\GitHub;
use App\DataObjects\Front\GithubRepositoryData;
use App\Support\Http\Controllers\BaseController;
use App\Http\Requests\FrontSubscribeNotificationRequest;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;

use Illuminate\Support\Facades\{
DB,
Cache,
Blade,
Notification,
};

use App\Models\{
Tag,
User,
Guest,
Article,
Category,
UptimeWebhookCall,
};

use App\Http\Requests\{
FrontSearchRequest,
FrontUptimeWebhookRequest,
FrontSubscribeNotificationRequest,
};

class FrontController extends BaseController
{
private const UPTIME_DOWN_STATUS = 0;

/**
* Create a new controller instance.
*
Expand All @@ -43,7 +60,7 @@ public function subscribeNotification(FrontSubscribeNotificationRequest $request
$guest = Guest::firstOrCreate(['endpoint' => $endpoint = $request->input('endpoint')]);

$subscription = $guest->updatePushSubscription(
$request->input('endpoint'),
$endpoint,
$request->input('public_key'),
$request->input('auth_token'),
$request->input('encoding'),
Expand All @@ -54,6 +71,49 @@ public function subscribeNotification(FrontSubscribeNotificationRequest $request
return response()->json([], 200);
}

/**
* Register the logged-in user so that they can receive notifications.
*
* @param \App\Http\Requests\FrontSubscribeNotificationRequest $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function addUserToBeNotifiedToUptimeMonitor(FrontSubscribeNotificationRequest $request): JsonResponse
{
/** @var \App\Models\User */
$user = auth()->user();

DB::transaction(fn () =>
$user->updateShouldBeNotified()
->updatePushSubscription(
$request->input('endpoint'),
$request->input('public_key'),
$request->input('auth_token'),
$request->input('encoding'),
)
);

return response()->json(status: JsonResponse::HTTP_CREATED);
}

/**
* @param \App\Http\Requests\FrontUptimeWebhookRequest $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function uptimeWebhook(FrontUptimeWebhookRequest $request): JsonResponse
{
UptimeWebhookCall::create($request->validated());

if ($request->input('heartbeat')['status'] === self::UPTIME_DOWN_STATUS) {
report($message = trim($request->input('msg'), '"'));
}

Notification::send(User::onlyShouldBeNotified()->get(), new UptimeMonitor($message));

return response()->json(status: JsonResponse::HTTP_NO_CONTENT);
}

/**
* Blog home page.
*
Expand Down
3 changes: 2 additions & 1 deletion app/Http/Middleware/VerifyCsrfToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class VerifyCsrfToken extends Middleware
* @var array<int, string>
*/
protected $except = [
//
'/github-webhooks',
'/uptime/webhook',
];
}
22 changes: 22 additions & 0 deletions app/Http/Requests/FrontUptimeWebhookRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Http\Requests;

use App\Support\Http\Requests\BaseRequest;

class FrontUptimeWebhookRequest extends BaseRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'heartbeat' => ['sometimes', 'nullable', 'required', 'array'],
'monitor' => ['sometimes', 'nullable', 'required', 'array'],
'msg' => ['required',],
];
}
}
30 changes: 30 additions & 0 deletions app/Models/UptimeWebhookCall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Models;

use App\Support\ORM\BaseModel;

class UptimeWebhookCall extends BaseModel
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'msg',
'heartbeat',
'monitor',
];

/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'msg' => 'array',
'heartbeat' => 'array',
'monitor' => 'array',
];
}
21 changes: 20 additions & 1 deletion app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

use Laravel\Sanctum\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Builder;
use NotificationChannels\WebPush\HasPushSubscriptions;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
use HasApiTokens, HasFactory, Notifiable, HasPushSubscriptions;

/**
* The attributes that are mass assignable.
Expand All @@ -20,6 +22,7 @@ class User extends Authenticatable
'name',
'email',
'password',
'should_be_notified',
];

/**
Expand All @@ -40,5 +43,21 @@ class User extends Authenticatable
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'should_be_notified' => 'boolean',
];

public function updateShouldBeNotified(bool $shouldBeNotified = true): static
{
$this->fill(['should_be_notified' => $shouldBeNotified])->save();

return $this;
}

/**
* Scope a query to only include users who should be notified.
*/
public function scopeOnlyShouldBeNotified(Builder $query): Builder
{
return $query->where('should_be_notified', true);
}
}
6 changes: 0 additions & 6 deletions app/Notifications/NewArticle.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,6 @@ public function via($notifiable)
return [WebPushChannel::class];
}

/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toWebPush($notifiable, $notification)
{
return (new WebPushMessage)
Expand Down
40 changes: 40 additions & 0 deletions app/Notifications/UptimeMonitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use NotificationChannels\WebPush\WebPushChannel;
use NotificationChannels\WebPush\WebPushMessage;

class UptimeMonitor extends Notification implements ShouldQueue
{
use Queueable;

/**
* Create a new notification instance.
*/
public function __construct(public string $message)
{
//
}

/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return [WebPushChannel::class];
}

public function toWebPush($notifiable, $notification)
{
return (new WebPushMessage)
->title($this->message)
->icon(mix('/images/favicons/favicon-192x192.png'))
->body($this->message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('should_be_notified')->after('remember_token')->index()->nullable();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropIndex(['should_be_notified']);
$table->dropColumn('should_be_notified');
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('uptime_webhook_calls', function (Blueprint $table) {
$table->id();
$table->json('msg');
$table->json('heartbeat');
$table->json('monitor');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('uptime_webhook_calls');
}
};
41 changes: 41 additions & 0 deletions resources/js/install-sw-workbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,41 @@ try {
}
}

async function addUserToUptimeNotifications(pushManager) {
const subscription = await pushManager.getSubscription()

const key = subscription.getKey('p256dh')
const token = subscription.getKey('auth')
const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0]

const data = {
endpoint: subscription.endpoint,
public_key: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null,
auth_token: token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null,
encoding: contentEncoding,
};

axios.post('/notifications/add-user-to-uptime-monitor', data, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}).then(function (response) {
Swal.fire({
text: 'Você foi adicionado com sucesso para receber notificações do Health Check dos monitores do UPTIME',
icon: "success"
});
})
.catch(function (error) {
console.error(error.response);

Swal.fire({
text: JSON.stringify(error.response.data),
icon: "error"
});
});
}

const pushNotificationAccepted = () => window.localStorage.getItem('acceptPushNotifications');

(async () => {
Expand Down Expand Up @@ -266,6 +301,12 @@ try {

document.querySelector('.enable-notifications')
.addEventListener('click', () => enableNotifications(registration.pushManager));

let btnUserToUptimeNotifications = document.querySelector('.add-user-to-uptime-notifications');

if (btnUserToUptimeNotifications) {
btnUserToUptimeNotifications.addEventListener('click', () => addUserToUptimeNotifications(registration.pushManager));
}
});

} else {
Expand Down
Loading

0 comments on commit 7840e1f

Please sign in to comment.