]> jfr.im git - irc/unrealircd/unrealircd-webpanel.git/commitdiff
Add api/common_api.php and use Server-Sent Events, streaming JSON
authorBram Matthys <redacted>
Wed, 12 Apr 2023 10:02:09 +0000 (12:02 +0200)
committerBram Matthys <redacted>
Wed, 12 Apr 2023 10:04:10 +0000 (12:04 +0200)
data on like the overview page to update the User/Channel/.. counts
instead of doing a HTTP request every second like we had before.

The use of api/common_api.php from api/ is quite easy:
require_once('common_api.php');
which takes care of validating the session, closing the session,
disabling output buffering, etc.

And then you use either:
* api_log_loop($sources)
  like: api_log_loop(["connect"]);
Or:
* api_timer_loop((int $every_msec, string $method, array|null $params = null)
  like: api_timer_loop(1000, "stats.get");

And then all the (streaming) JSON-RPC response data from the server is
streamed back to the client via SSE.

api/common_api.php [new file with mode: 0644]
api/overview.php
index.php

diff --git a/api/common_api.php b/api/common_api.php
new file mode 100644 (file)
index 0000000..7da12c9
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+include "../common.php";
+
+if (!isset($_SESSION['id']))
+       die("Access denied");
+
+// Close the session now, otherwise other pages block
+session_write_close();
+
+// Only now make the connection (this can take a short while)
+include "../connection.php";
+
+// Server Side Events
+header('Content-Type: text/event-stream');
+
+// Explicitly disable caching so Varnish and other upstreams won't cache.
+header("Cache-Control: no-cache, must-revalidate");
+
+// Setting this header instructs Nginx to disable fastcgi_buffering and disable
+// gzip for this request.
+header('X-Accel-Buffering: no');
+
+// No time limit
+set_time_limit(0);
+
+// Send content immediately
+ob_implicit_flush(1);
+
+// Eh.. yeah...
+ob_end_flush();
+
+// If we use fastcgi, then finish the request now (UNTESTED)
+if (function_exists('fastcgi_finish_request'))
+       fastcgi_finish_request();
+
+/* Send server-sent events (SSE) message */
+function send_sse($json)
+{
+       echo "data: ".json_encode($json)."\n\n";
+}
+
+function api_log_loop($sources)
+{
+       GLOBAL $rpc;
+
+       $rpc->log()->subscribe($sources);
+       if ($rpc->error)
+       {
+               echo $rpc->error;
+               die;
+       }
+
+       for(;;)
+       {
+               $res = $rpc->eventloop();
+               if (!$res)
+                       continue;
+               send_sse($res);
+       }
+}
+
+function api_timer_loop(int $every_msec, string $method, array|null $params = null)
+{
+       GLOBAL $rpc;
+
+       $rpc->rpc()->add_timer("timer", $every_msec, $method, $params);
+       if ($rpc->error)
+       {
+               echo $rpc->error;
+               die;
+       }
+
+       for(;;)
+       {
+               $res = $rpc->eventloop();
+               if (!$res)
+                       continue;
+               send_sse($res);
+       }
+}
index 55f5db128167acd0c0a168e745becff1b4292e8d..a4bf424b798039f8ebca2eb2e74f0bf889d66b0e 100644 (file)
@@ -1,17 +1,4 @@
 <?php
+require_once('common_api.php');
 
-session_start();
-if (!isset($_SESSION["id"]))
-    die("{\"error\": \"Access denied\"}");
-
-include "../common.php";
-
-// Close the session now, otherwise other pages block too long
-session_write_close();
-
-include "../connection.php";
-
-header("Content-type: application/json");
-
-$stats = $rpc->stats()->get();
-echo json_encode($stats);
+api_timer_loop(1000, "stats.get");
index 27246870af231b2f9789d3c46d824c4d94cb1c7b..397cf17fab2ee05a88996e7c5051fe7d0032313a 100644 (file)
--- a/index.php
+++ b/index.php
@@ -229,26 +229,33 @@ $num_of_panel_admins = count($userlist);
 
 
 <script>
-    function updateStats() {
-        var xhttp = new XMLHttpRequest();
-        xhttp.onreadystatechange = function() {
-            if (this.readyState == 4 && this.status == 200) {
-                var data = JSON.parse(this.responseText);
-                document.getElementById("stats_user_total").innerHTML = data.user.total;
-                document.getElementById("stats_channel_total").innerHTML = data.channel.total;
-                               document.getElementById("stats_oper_total").innerHTML = data.user.oper;
-                               document.getElementById("stats_server_total").innerHTML = data.server.total;
-                               document.getElementById("num_server_bans").innerHTML = data.server_ban.server_ban;
-                               document.getElementById("num_spamfilter_entries").innerHTML = data.server_ban.spamfilter;
-                               document.getElementById("num_ban_exceptions").innerHTML = data.server_ban.server_ban_exception;
-                               document.getElementById("stats_uline_total").innerHTML = data.user.ulined + "/" + data.server.ulined;
-            }
-        };
-        xhttp.open("GET", "api/overview.php", true);
-        xhttp.send();
-    }
-    updateStats();
-    setInterval(updateStats, 1000); // Update stats every second
+       function updateStats(e)
+       {
+               var data;
+               try {
+                       data = JSON.parse(e.data);
+               } catch(e) {
+                       return;
+               }
+               document.getElementById("stats_user_total").innerHTML = data.user.total;
+               document.getElementById("stats_channel_total").innerHTML = data.channel.total;
+               document.getElementById("stats_oper_total").innerHTML = data.user.oper;
+               document.getElementById("stats_server_total").innerHTML = data.server.total;
+               document.getElementById("num_server_bans").innerHTML = data.server_ban.server_ban;
+               document.getElementById("num_spamfilter_entries").innerHTML = data.server_ban.spamfilter;
+               document.getElementById("num_ban_exceptions").innerHTML = data.server_ban.server_ban_exception;
+               document.getElementById("stats_uline_total").innerHTML = data.user.ulined + "/" + data.server.ulined;
+       }
+       function initStats()
+       {
+               if (!!window.EventSource) {
+                       var source = new EventSource('api/overview.php');
+                       source.addEventListener('message', updateStats, false);
+               }
+       }
+       initStats();
+       //setInterval(updateStats, 1000); // Update stats every second
+       // ^ commented out but may want to restart initStats() when connection is lost.
 </script>
 
 <div class="container mt-3">