Skip to content

Commit

Permalink
fix(desktop-notifications): changed tab and service worker logic; no …
Browse files Browse the repository at this point in the history
…more event sources in the service worker

This is considered a very experimental push, use at own risk.
  • Loading branch information
ncosta-ic committed Feb 13, 2024
1 parent a16ee4e commit 73d685d
Show file tree
Hide file tree
Showing 10 changed files with 328 additions and 130 deletions.
8 changes: 5 additions & 3 deletions application/controllers/DaemonController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

final class DaemonController extends CompatController
{
protected $requiresAuthentication = false;

public function init(): void
{
/**
Expand Down Expand Up @@ -40,7 +42,7 @@ public function scriptAction(): void
->getBaseDir() . '/public/js';

$filePath = realpath(
$root . DIRECTORY_SEPARATOR . 'icinga-notifications-' . $this->_getParam(
$root . DIRECTORY_SEPARATOR . 'notifications-' . $this->_getParam(
'file',
'undefined'
) . $this->_getParam('extension', 'undefined')
Expand All @@ -50,7 +52,7 @@ public function scriptAction(): void
$this->httpNotFound("No file name submitted");
}
$this->httpNotFound(
"'icinga-notifications-"
"'notifications-"
. $this->_getParam('file')
. $this->_getParam('extension')
. " does not exist"
Expand Down Expand Up @@ -86,7 +88,7 @@ public function scriptAction(): void
}
} else {
$this->httpNotFound(
"'icinga-notifications-"
"'notifications-"
. $this->_getParam('file')
. $this->_getParam('extension')
. " could not be read"
Expand Down
2 changes: 1 addition & 1 deletion configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,4 @@
]
);

$this->provideJsFile('notification.js');
$this->provideJsFile('notifications.js');
30 changes: 16 additions & 14 deletions library/Notifications/Daemon/Daemon.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,21 +246,23 @@ private function processNotifications(): void
$time->setTimezone(new DateTimeZone('UTC'));
$time = $time->format(DateTimeInterface::RFC3339_EXTENDED);

$event = new Event(
EventIdentifier::ICINGA2_NOTIFICATION,
(object) [
'incident_id' => $incident->incident_id,
'event_id' => $incident->event_id,
'host' => $host,
'service' => $service,
'time' => $time,
'severity' => $incident->incident->severity
],
// minus one as it's usually expected as an auto-incrementing id, we just want to pass it
// the actual id in this case
intval($notification->id - 1)
);
// self::$logger::warning(self::PREFIX . @var_export($event, true));
$connections[$notification->contact_id]->sendEvent(
new Event(
EventIdentifier::ICINGA2_NOTIFICATION,
(object) [
'incident_id' => $incident->incident_id,
'event_id' => $incident->event_id,
'host' => $host,
'service' => $service,
'time' => $time,
'severity' => $incident->incident->severity
],
// minus one as it's usually expected as an auto-incrementing id, we just want to pass it
// the actual id in this case
intval($notification->id - 1)
)
$event
);
++$numOfNotifications;
}
Expand Down
1 change: 1 addition & 0 deletions library/Notifications/Daemon/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ private function handleRequest(ServerRequestInterface $request): Response
return new Response(
StatusCodeInterface::STATUS_OK,
[
"Connection" => "keep-alive",
"Content-Type" => "text/event-stream; charset=utf-8",
"Cache-Control" => "no-cache",
"X-Accel-Buffering" => "no"
Expand Down
1 change: 0 additions & 1 deletion public/js/icinga-notifications-worker.js

This file was deleted.

1 change: 0 additions & 1 deletion public/js/icinga-notifications-worker.js.map

This file was deleted.

109 changes: 0 additions & 109 deletions public/js/notification.js

This file was deleted.

106 changes: 106 additions & 0 deletions public/js/notifications-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const _PREFIX = '[notifications-worker] - ';
const _SERVER_CONNECTIONS = [];

if (!(self instanceof ServiceWorkerGlobalScope)) {
throw new Error("Tried loading 'notification-worker.js' in a context other than a Service Worker.");
}

/** @type {ServiceWorkerGlobalScope} */
const selfSW = self;
selfSW.addEventListener('message', (event) => {
self.console.log(_PREFIX + "received a message: ", event);
this.processMessage(event);
});
selfSW.addEventListener('activate', (event) => {
// claim all clients under own scope once the service worker gets activated
event.waitUntil(
selfSW.clients.claim().then(() => {
self.console.log(_PREFIX + "claimed all tabs.");
})
);
});
selfSW.addEventListener('fetch', (event) => {
// self.console.log(_PREFIX + 'fetch event triggered with: ', event);
const request = event.request;
const url = new URL(event.request.url);

// only check dedicated event stream requests towards the daemon
if (request.headers.get('accept').startsWith('text/event-stream') && url.pathname.trim() === '/icingaweb2/notifications/daemon') {
self.console.log(_PREFIX + `tab '${event.clientId}' requested event-stream.`);
event.respondWith(this.injectMiddleware(request, event.clientId));
}
});

function processMessage(event) {
if (event.data) {
let data = JSON.parse(event.data);
switch (data.command) {
case 'tab_force_reclaim':
/*
* trigger the claim process as there seems to be new clients in our scope which aren't under our
* control
*/
self.clients.claim().then(() => {
self.console.log(_PREFIX + "reclaimed all tabs.");
});
break;
}
}
}

async function injectMiddleware(request, clientId) {
let response = await fetch(request, {
keepalive: true
});
if (response.ok && response.body instanceof ReadableStream) {
self.console.log(_PREFIX + `injecting into data stream of tab '${clientId}'.`);

const controllers = {
writable: undefined,
readable: undefined,
signal: new AbortController()
};
let readStream = new ReadableStream({
start(controller) {
controllers.readable = controller;
},
cancel(reason) {
self.console.log(_PREFIX + `tab '${clientId}' closed event-stream (client-side).`);
// tab crashed or closed down connection to event-stream, stopping pipe through stream by
// triggering the abort signal
controllers.signal.abort();
}
}, new CountQueuingStrategy({ highWaterMark: 10 }));
let writeStream = new WritableStream({
start(controller) {
controllers.writable = controller;
},
write(chunk, controller) {
controllers.readable.enqueue(chunk);
},
close() {
// close was triggered by the server closing down the event-stream
self.console.log(_PREFIX + `tab '${clientId}' closed event-stream (server-side).`);
controllers.readable.close();
},
abort(reason) {
// close was triggered by an abort signal (most likely by the reader / client-side)
self.console.log(_PREFIX + `tab '${clientId}' closed event-stream (server-side).`);
controllers.readable.close();
}
}, new CountQueuingStrategy({ highWaterMark: 10 }));
// returning injected (piped) stream
return new Response(
response.body.pipeThrough({
writable: writeStream,
readable: readStream
}, { signal: controllers.signal.signal }),
{
headers: response.headers,
statusText: response.statusText,
status: response.status
}
)
}
return response;
}
Loading

0 comments on commit 73d685d

Please sign in to comment.