diff --git a/assets/sai.js b/assets/sai.js
index 734d886..325f3f1 100644
--- a/assets/sai.js
+++ b/assets/sai.js
@@ -395,7 +395,7 @@ var lang_zhs = "{" +
"}}";
var logs = "", redpend = 0, gitohashi_integ = 0, authd = 0, exptimer, auth_user = "",
- ongoing_task_activities = {};
+ ongoing_task_activities = {}, last_log_timestamp = 0;
function update_task_activities() {
for (const uuid in ongoing_task_activities) {
@@ -799,7 +799,8 @@ function sai_event_render(o, now_ut, reset_all_icon)
if (t.state === 4 || t.state == 6)
has_bad = 1;
- s1 += "<div id=\"taskstate_" + t.uuid + "\" class=\"taskstate taskstate" + t.state + "\">";
+ s1 += "<div id=\"taskstate_" + t.uuid + "\" class=\"taskstate taskstate" + t.state +
+ "\" data-event-uuid=\"" + san(e.uuid) + "\" data-platform=\"" + san(t.platform) + "\">";
if (t.state === 0)
wai = 1;
@@ -1080,6 +1081,7 @@ function ws_open_sai()
"\"com.warmcat.sai.taskinfo\"," +
"\"js_api_version\": " + SAI_JS_API_VERSION + "," +
"\"logs\": 1," +
+ "\"last_log_ts\":" + last_log_timestamp + "," +
"\"task_hash\":" +
JSON.stringify(tid) + "}");
@@ -1523,6 +1525,7 @@ function ws_open_sai()
lines = times = logs = "";
tfirst = 0;
lli = 1;
+ last_log_timestamp = 0;
});
}
@@ -1639,12 +1642,18 @@ function ws_open_sai()
}
break;
+ case "com.warmcat.sai.unauthorized":
+ location.reload();
+ break;
+
case "com-warmcat-sai-logs":
var s1 = atob(jso.log), s = hsanitize(s1), li,
en = "", yo, dh, ce, tn = "";
if (!tfirst)
- tfirst = jso.timestamp;
+ tfirst = jso.timestamp;
+
+ last_log_timestamp = jso.timestamp;
li = (s1.match(/\n/g)||[]).length;
@@ -1813,6 +1822,55 @@ window.addEventListener("load", function() {
document.body.clientHeight;
}, 500)
+
+ const stickyEl = document.getElementById("sai_sticky");
+ if (stickyEl) {
+ stickyEl.addEventListener("contextmenu", function(event) {
+ let target = event.target;
+ let taskDiv = null;
+
+ // find the taskstate div parent
+ while (target && target.id !== "sai_sticky") {
+ if (target.classList && target.classList.contains("taskstate")) {
+ taskDiv = target;
+ break;
+ }
+ target = target.parentElement;
+ }
+
+ if (taskDiv && authd) {
+ event.preventDefault();
+
+ const taskUuid = taskDiv.id.substring(10);
+ const eventUuid = taskDiv.dataset.eventUuid;
+ const platform = taskDiv.dataset.platform;
+
+ const menuItems = [
+ {
+ label: "Rebuild this task",
+ callback: () => {
+ sai.send(JSON.stringify({
+ schema: "com.warmcat.sai.taskreset",
+ uuid: taskUuid
+ }));
+ }
+ },
+ {
+ label: `Rebuild all <b>${hsanitize(platform)}</b>`,
+ callback: () => {
+ sai.send(JSON.stringify({
+ schema: "com.warmcat.sai.platreset",
+ event_uuid: eventUuid,
+ platform: platform
+ }));
+ }
+ }
+ ];
+
+ createContextMenu(event, menuItems);
+ }
+ });
+ }
}, false);
diff --git a/src/common/include/private.h b/src/common/include/private.h
index 24d5325..f497e31 100644
--- a/src/common/include/private.h
+++ b/src/common/include/private.h
@@ -230,6 +230,11 @@ typedef struct sai_rebuild {
char builder_name[96];
} sai_rebuild_t;
+typedef struct sai_platreset {
+ lws_dll2_t list;
+ char event_uuid[65];
+ char platform[65];
+} sai_browse_rx_platreset_t;
struct sai_event;
@@ -451,6 +456,7 @@ typedef struct sai_browse_rx_evinfo {
typedef struct sai_browse_rx_taskinfo {
char task_hash[65];
+ uint64_t last_log_ts;
unsigned int log_start;
unsigned int js_api_version;
uint8_t logs;
diff --git a/src/server/s-websrv.c b/src/server/s-websrv.c
index f12c519..419c917 100644
--- a/src/server/s-websrv.c
+++ b/src/server/s-websrv.c
@@ -54,6 +54,11 @@ static lws_struct_map_t lsm_browser_taskreset[] = {
LSM_CARRAY (sai_browse_rx_evinfo_t, event_hash, "uuid"),
};
+static lws_struct_map_t lsm_browser_platreset[] = {
+ LSM_CARRAY (sai_browse_rx_platreset_t, event_uuid, "event_uuid"),
+ LSM_CARRAY (sai_browse_rx_platreset_t, platform, "platform"),
+};
+
static const lws_struct_map_t lsm_viewercount_members[] = {
LSM_UNSIGNED(sai_viewer_state_t, viewers, "count"),
};
@@ -71,6 +76,8 @@ static const lws_struct_map_t lsm_schema_json_map[] = {
"com.warmcat.sai.viewercount"),
LSM_SCHEMA (sai_rebuild_t, NULL, lsm_rebuild,
"com.warmcat.sai.rebuild"),
+ LSM_SCHEMA (sai_browse_rx_platreset_t, NULL, lsm_browser_platreset,
+ "com.warmcat.sai.platreset"),
};
enum {
@@ -80,6 +87,7 @@ enum {
SAIS_WS_WEBSRV_RX_TASKCANCEL,
SAIS_WS_WEBSRV_RX_VIEWERCOUNT,
SAIS_WS_WEBSRV_RX_REBUILD,
+ SAIS_WS_WEBSRV_RX_PLATRESET,
};
void
@@ -459,6 +467,56 @@ websrvss_ws_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
return 0;
+ case SAIS_WS_WEBSRV_RX_PLATRESET: {
+ sai_browse_rx_platreset_t *pr = (sai_browse_rx_platreset_t *)a.dest;
+ char filt[256];
+
+ if (sais_validate_id(pr->event_uuid, SAI_EVENTID_LEN))
+ goto soft_error;
+
+ /* open the event-specific database object */
+
+ if (sais_event_db_ensure_open(m->vhd, pr->event_uuid, 0, &pdb)) {
+ lwsl_err("%s: unable to open event-specific database\n",
+ __func__);
+ goto soft_error;
+ }
+
+ /*
+ * Retreive all the related structs into a dll2 list
+ */
+ lws_sql_purify(esc, pr->platform, sizeof(esc));
+ lws_snprintf(filt, sizeof(filt), " and platform='%s'", esc);
+
+ if (lws_struct_sq3_deserialize(pdb, filt, NULL,
+ lsm_schema_sq3_map_task,
+ &o, &a.ac, 0, 999) >= 0) {
+
+ sqlite3_exec(pdb, "BEGIN TRANSACTION",
+ NULL, NULL, &err);
+ sqlite3_free(err);
+
+ /*
+ * Walk the results list resetting all the tasks
+ */
+
+ lws_start_foreach_dll(struct lws_dll2 *, p, o.head) {
+ sai_task_t *t = lws_container_of(p, sai_task_t,
+ list);
+ sais_task_reset(m->vhd, t->uuid);
+ } lws_end_foreach_dll(p);
+
+ sqlite3_exec(pdb, "END TRANSACTION",
+ NULL, NULL, &err);
+ sqlite3_free(err);
+ }
+
+ sais_event_db_close(m->vhd, &pdb);
+ lwsac_free(&a.ac);
+
+ return 0;
+ }
+
case SAIS_WS_WEBSRV_RX_EVENTDELETE:
ei = (sai_browse_rx_evinfo_t *)a.dest;
diff --git a/src/web/w-private.h b/src/web/w-private.h
index 2cbc1c7..344396a 100644
--- a/src/web/w-private.h
+++ b/src/web/w-private.h
@@ -180,6 +180,7 @@ struct pss {
char login_form;
uint64_t first_log_timestamp;
+ uint64_t initial_log_timestamp;
uint64_t artifact_offset;
uint64_t artifact_length;
diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c
index 955a22d..8767cb6 100644
--- a/src/web/w-ws-browser.c
+++ b/src/web/w-ws-browser.c
@@ -71,10 +71,16 @@ static lws_struct_map_t lsm_browser_taskreset[] = {
LSM_CARRAY (sai_browse_rx_evinfo_t, event_hash, "uuid"),
};
+static lws_struct_map_t lsm_browser_platreset[] = {
+ LSM_CARRAY (sai_browse_rx_platreset_t, event_uuid, "event_uuid"),
+ LSM_CARRAY (sai_browse_rx_platreset_t, platform, "platform"),
+};
+
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_UNSIGNED (sai_browse_rx_taskinfo_t, js_api_version, "js_api_version"),
+ LSM_UNSIGNED (sai_browse_rx_taskinfo_t, last_log_ts, "last_log_ts"),
};
/*
@@ -99,6 +105,8 @@ static const lws_struct_map_t lsm_schema_json_map_bwsrx[] = {
"com.warmcat.sai.loadreport"),
LSM_SCHEMA (sai_rebuild_t, NULL, lsm_rebuild,
"com.warmcat.sai.rebuild"),
+ LSM_SCHEMA (sai_browse_rx_platreset_t, NULL, lsm_browser_platreset,
+ "com.warmcat.sai.platreset"),
};
enum {
@@ -110,6 +118,7 @@ enum {
SAIM_WS_BROWSER_RX_TASKCANCEL,
SAIM_WS_BROWSER_RX_JS_HELLO,
SAIM_WS_BROWSER_RX_REBUILD,
+ SAIM_WS_BROWSER_RX_PLATRESET,
};
@@ -492,6 +501,11 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
* as long as needed to send it out
*/
+ if (ti->logs)
+ pss->initial_log_timestamp = ti->last_log_ts;
+ else
+ pss->initial_log_timestamp = 0;
+
if (saiw_pss_schedule_taskinfo(pss, ti->task_hash, !!ti->logs))
goto soft_error;
@@ -509,7 +523,7 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
case SAIM_WS_BROWSER_RX_TASKRESET:
if (!sais_conn_auth(pss))
- goto soft_error;
+ goto auth_error;
/*
* User is asking us to reset / rebuild this task
@@ -523,7 +537,7 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
case SAIM_WS_BROWSER_RX_EVENTRESET:
if (!sais_conn_auth(pss))
- goto soft_error;
+ goto auth_error;
/*
* User is asking us to reset / rebuild every task in the event
@@ -543,7 +557,7 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
*/
if (!sais_conn_auth(pss))
- goto soft_error;
+ goto auth_error;
ei = (sai_browse_rx_evinfo_t *)a.dest;
@@ -558,7 +572,7 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
case SAIM_WS_BROWSER_RX_TASKCANCEL:
if (!sais_conn_auth(pss))
- goto soft_error;
+ goto auth_error;
/*
* Browser is informing us of task's STOP button clicked, we
@@ -574,7 +588,7 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
case SAIM_WS_BROWSER_RX_REBUILD:
if (!sais_conn_auth(pss))
- goto soft_error;
+ goto auth_error;
/*
* User is asking us to rebuild a builder
@@ -583,6 +597,17 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
saiw_websrv_queue_tx(vhd->h_ss_websrv, buf, bl);
break;
+ case SAIM_WS_BROWSER_RX_PLATRESET:
+ if (!sais_conn_auth(pss))
+ goto auth_error;
+
+ /*
+ * User is asking us to reset / rebuild a whole platform
+ */
+
+ saiw_websrv_queue_tx(vhd->h_ss_websrv, buf, bl);
+ break;
+
default:
assert(0);
break;
@@ -595,6 +620,16 @@ bail:
return ret;
+auth_error:
+ {
+ uint8_t buf[LWS_PRE + 128];
+ int n;
+
+ n = lws_snprintf((char *)buf + LWS_PRE, sizeof(buf) - LWS_PRE,
+ "{\"schema\":\"com.warmcat.sai.unauthorized\"}");
+ lws_write(pss->wsi, buf + LWS_PRE, (size_t)n, LWS_WRITE_TEXT);
+ }
+
soft_error:
lwsac_free(&a.ac);
@@ -1287,7 +1322,7 @@ send_it:
if (sch && sch->logsub && sch->one_task) {
strcpy(pss->sub_task_uuid, sch->one_task->uuid);
lws_dll2_add_head(&pss->subs_list, &pss->vhd->subs_owner);
- pss->sub_timestamp = 0; /* where we got up to */
+ pss->sub_timestamp = pss->initial_log_timestamp; /* where we got up to */
lws_callback_on_writable(pss->wsi);
lwsl_info("%s: subscribed to logs for %s\n", __func__,