diff --git a/assets/sai.js b/assets/sai.js
index 3331b95..9248896 100644
--- a/assets/sai.js
+++ b/assets/sai.js
@@ -1,3 +1,5 @@
+const SAI_JS_API_VERSION = 2;
+
(function() {
/*
@@ -1033,6 +1035,7 @@ function ws_open_sai()
sai.send("{\"schema\":" +
"\"com.warmcat.sai.taskinfo\"," +
+ "\"js_api_version\": " + SAI_JS_API_VERSION + "," +
"\"logs\": 1," +
"\"task_hash\":" +
JSON.stringify(tid) + "}");
@@ -1051,7 +1054,8 @@ function ws_open_sai()
sai.send("{\"schema\":" +
"\"com.warmcat.sai.eventinfo\"," +
- "\"event_hash\":" +
+ "\"js_api_version\": " + SAI_JS_API_VERSION + "," +
+ "\"event_hash\":" +
JSON.stringify(eid) + "}");
return;
@@ -1062,7 +1066,7 @@ function ws_open_sai()
*/
sai.send("{\"schema\":" +
- "\"com.warmcat.sai.taskinfo\"}");
+ "\"com.warmcat.sai.taskinfo\", \"js_api_version\": " + SAI_JS_API_VERSION + "}");
};
sai.onmessage = function got_packet(msg) {
@@ -1113,6 +1117,12 @@ function ws_open_sai()
}
}
+ if (jso.api_version && jso.api_version !== SAI_JS_API_VERSION) {
+ console.warn(`Sai JS API version mismatch. Client: ${SAI_JS_API_VERSION}, Server: ${jso.api_version}. Reloading page.`);
+ location.reload(true); // Force a hard reload
+ return; // Stop processing this old message
+ }
+
switch (jso.schema) {
case "com.warmcat.sai.builders":
diff --git a/src/common/include/private.h b/src/common/include/private.h
index 5291eed..d6a75f2 100644
--- a/src/common/include/private.h
+++ b/src/common/include/private.h
@@ -421,6 +421,7 @@ typedef struct sai_browse_rx_evinfo {
typedef struct sai_browse_rx_taskinfo {
char task_hash[65];
unsigned int log_start;
+ unsigned int js_api_version;
uint8_t logs;
} sai_browse_rx_taskinfo_t;
diff --git a/src/web/w-private.h b/src/web/w-private.h
index 9fbef99..4c88487 100644
--- a/src/web/w-private.h
+++ b/src/web/w-private.h
@@ -23,6 +23,8 @@
#include <sqlite3.h>
#include <sys/stat.h>
+#define SAIW_API_VERSION 2
+
struct sai_plat;
typedef struct sai_platm {
@@ -167,6 +169,7 @@ struct pss {
int log_cache_size;
int authorized;
int specificity;
+ unsigned int js_api_version;
unsigned long expiry_unix_time;
/* notification hmac information */
@@ -318,7 +321,7 @@ saiw_sched_destroy(struct lws_dll2 *d, void *user);
void
-saiw_ws_broadcast_raw(struct vhd *vhd, const void *buf, size_t len);
+saiw_ws_broadcast_raw(struct vhd *vhd, const void *buf, size_t len, unsigned int min_api_version);
void
saiw_browser_state_changed(struct pss *pss, int established);
diff --git a/src/web/w-websrv.c b/src/web/w-websrv.c
index ced0742..9c75049 100644
--- a/src/web/w-websrv.c
+++ b/src/web/w-websrv.c
@@ -117,7 +117,7 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
m->a.ac = NULL;
vhd->builders_owner =
&((sai_plat_owner_t *)m->a.dest)->plat_owner;
- saiw_ws_broadcast_raw(vhd, buf, len);
+ saiw_ws_broadcast_raw(vhd, buf, len, 0);
break;
case SAIS_WS_WEBSRV_RX_OVERVIEW:
@@ -150,7 +150,7 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
/* A builder sent a load report, forward to all browsers */
lwsl_notice("%s: ===== Received load report, broadcasting to %d browsers\n",
__func__, (int)vhd->browsers.count);
- saiw_ws_broadcast_raw(vhd, buf, len);
+ saiw_ws_broadcast_raw(vhd, buf, len, 2);
break;
}
diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c
index 18995f9..9a44614 100644
--- a/src/web/w-ws-browser.c
+++ b/src/web/w-ws-browser.c
@@ -35,17 +35,25 @@
* all connected browsers, eg, for load reports.
*/
void
-saiw_ws_broadcast_raw(struct vhd *vhd, const void *buf, size_t len)
+saiw_ws_broadcast_raw(struct vhd *vhd, const void *buf, size_t len, unsigned int api_ver_min)
{
+ int eff = 0;
// lwsl_err("%s: sai-web broadcasting to browsers\n", __func__);
// lwsl_hexdump_err(buf, len);
lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) {
struct pss *pss = lws_container_of(p, struct pss, same);
- if (lws_buflist_append_segment(&pss->raw_tx, buf, len) >= 0)
- lws_callback_on_writable(pss->wsi);
+ if (pss->js_api_version >= api_ver_min) {
+ eff++;
+ if (lws_buflist_append_segment(&pss->raw_tx, buf, len) >= 0)
+ lws_callback_on_writable(pss->wsi);
+ }
+
} lws_end_foreach_dll(p);
+
+ lwsl_notice("%s: broadcast to %d / %d browsers\n", __func__,
+ eff, (int)vhd->browsers.count);
}
extern const lws_struct_map_t lsm_load_report_members[2];
@@ -63,11 +71,11 @@ static lws_struct_map_t lsm_browser_taskreset[] = {
};
static lws_struct_map_t lsm_browser_taskinfo[] = {
- LSM_CARRAY (sai_browse_rx_taskinfo_t, task_hash, "task_hash"),
- LSM_UNSIGNED (sai_browse_rx_taskinfo_t, logs, "logs"),
+ LSM_CARRAY (sai_browse_rx_taskinfo_t, task_hash, "task_hash"),
+ LSM_UNSIGNED (sai_browse_rx_taskinfo_t, logs, "logs"),
+ LSM_UNSIGNED (sai_browse_rx_taskinfo_t, js_api_version, "js_api_version"),
};
-
/*
* Schema list so lws_struct can pick the right object to create based on the
* incoming schema name
@@ -97,6 +105,7 @@ enum {
SAIM_WS_BROWSER_RX_EVENTRESET,
SAIM_WS_BROWSER_RX_EVENTDELETE,
SAIM_WS_BROWSER_RX_TASKCANCEL,
+ SAIM_WS_BROWSER_RX_JS_HELLO,
};
@@ -425,6 +434,10 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
int m, ret = -1;
memset(&a, 0, sizeof(a));
+ /*
+ * pss->js_api_version defaults to 1 (from ESTABLISHED callback).
+ * A new client will update it by sending a js-hello message.
+ */
a.map_st[0] = lsm_schema_json_map_bwsrx;
a.map_entries_st[0] = LWS_ARRAY_SIZE(lsm_schema_json_map_bwsrx);
a.map_entries_st[1] = LWS_ARRAY_SIZE(lsm_schema_json_map_bwsrx);
@@ -460,6 +473,9 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
*/
// lwsl_warn("%s: SAIM_WS_BROWSER_RX_TASKINFO: doing WSS_PREPARE_BUILDER_SUMMARY\n", __func__);
+ if (ti->js_api_version)
+ pss->js_api_version = ti->js_api_version;
+
saiw_alloc_sched(pss, WSS_PREPARE_OVERVIEW);
saiw_alloc_sched(pss, WSS_PREPARE_BUILDER_SUMMARY);
ret = 0;
@@ -790,11 +806,13 @@ again:
p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
"{\"schema\":\"sai.warmcat.com.overview\","
+ " \"api_version\":%u,"
" \"alang\":\"%s\","
" \"authorized\": %d,"
" \"auth_secs\": %ld,"
" \"auth_user\": \"%s\","
"\"overview\":[",
+ SAIW_API_VERSION,
lws_json_purify(esc, pss->alang, sizeof(esc) - 1, &iu),
pss->authorized, pss->authorized ? pss->expiry_unix_time - lws_now_secs() : 0,
lws_json_purify(esc1, pss->auth_user, sizeof(esc1) - 1, &iu)