Author: Andy Green Date: Fri Apr 24 14:20:09 2026 +0100 integ-harden diff --git a/assets/sai.js b/assets/sai.js index 9b82cb0..2410dd3 100644 --- a/assets/sai.js +++ b/assets/sai.js @@ -784,9 +784,14 @@ function createTaskRow(task, now_ut) { qc++; } + let prefix = ""; + if (task.git_hash && task.repo_name) { + prefix = `${hsanitize(task.git_hash.substring(0, 4))} ${hsanitize(task.repo_name)} `; + } + tr.innerHTML = `${s1}` + `${agify(now_ut, task.started)} ago` + - `${hsanitize(task.task_name)}`; + `${prefix}${hsanitize(task.task_name)}`; return tr; } @@ -799,9 +804,14 @@ function updateTaskRow(tr, task, now_ut) { s1 += "□"; qc++; } + let prefix = ""; + if (task.git_hash && task.repo_name) { + prefix = `${hsanitize(task.git_hash.substring(0, 4))} ${hsanitize(task.repo_name)} `; + } + const newHTML = `${s1}` + `${agify(now_ut, task.started)} ago` + - `${hsanitize(task.task_name)}`; + `${prefix}${hsanitize(task.task_name)}`; if (tr.innerHTML !== newHTML) { tr.innerHTML = newHTML; @@ -883,6 +893,7 @@ function updateSpreadsheetDOM(container, tasks) { var pos = 0, lli = 1, lines = "", times = "", locked = 1, tfirst = 0, cont = [ 0, 0, 0, 0, 0]; +var deleted_events_cache = new Set(); function get_appropriate_ws_url() { @@ -1359,9 +1370,7 @@ function refresh_state(task_uuid, task_state) } } -function after_delete() { - location.reload(); -} + function createContextMenu(event, menuItems) { event.preventDefault(); @@ -1894,6 +1903,12 @@ function ws_open_sai() } var s1 = get_appropriate_ws_url() + "/sai/browse" + s; + if (typeof gitohashi_integ === 'undefined' || !gitohashi_integ) { + if (s1.indexOf('?') !== -1) + s1 += "&client=sai"; + else + s1 += "?client=sai"; + } // if (s1.split("?")) // s1 = s1.split("?")[0]; console.log(s1); @@ -2162,6 +2177,13 @@ function ws_open_sai() * can send a single e[] if it just changed * state */ + if (jso.overview) { + jso.overview = jso.overview.filter(o => !deleted_events_cache.has(o.e.uuid)); + if (jso.overview.length === 0 && typeof jso.total_events === 'undefined') { + break; + } + } + s = ""; /* @@ -2193,18 +2215,15 @@ function ws_open_sai() var pagination_html_top = ""; var pagination_html_bottom = ""; if (!gitohashi_integ && jso.total_events > 6) { - var info_span = "Showing " + (jso.offset + 1) + " - " + Math.min(jso.offset + 6, jso.total_events) + " of " + jso.total_events + ""; - pagination_html_top += "
"; if (jso.offset > 0) { - pagination_html_top += " "; + pagination_html_top += ""; } - pagination_html_top += info_span + "
"; + pagination_html_top += ""; pagination_html_bottom += "
"; - pagination_html_bottom += info_span; if (jso.offset + 6 < jso.total_events) { - pagination_html_bottom += " "; + pagination_html_bottom += ""; } pagination_html_bottom += "
"; } @@ -2276,14 +2295,27 @@ function ws_open_sai() document.getElementById("delete-ev-" + san(jso.overview[n].e.uuid)). addEventListener("click", function(e) { console.log(e); + var uuid = san(e.srcElement.id.substring(10)); + deleted_events_cache.add(uuid); var rs= "{\"schema\":" + "\"com.warmcat.sai.eventdelete\"," + "\"uuid\": " + - JSON.stringify(san(e.srcElement.id.substring(10))) + "}"; + JSON.stringify(uuid) + "}"; console.log(rs); sai.send(rs); - setTimeout(after_delete, 750); + // Remove the event row immediately + var td = e.srcElement.closest(".waiting"); + if (td && td.parentNode) { + var tr = td.parentNode; + tr.style.transition = 'opacity 0.3s'; + tr.style.opacity = '0'; + setTimeout(function() { + if (tr && tr.parentNode) { + tr.parentNode.removeChild(tr); + } + }, 300); + } }); } break; @@ -2324,10 +2356,12 @@ function ws_open_sai() if (document.getElementById("taskinfo-" + jso.t.uuid)) { console.log("FOUND taskinfo-" + jso.t.uuid); document.getElementById("taskinfo-" + jso.t.uuid).innerHTML = sai_taskinfo_render(jso); - if (document.getElementById("esr-" + jso.e.uuid)) - document.getElementById("esr-" + jso.e.uuid).innerHTML = - sai_event_summary_render(jso, now_ut, 1); - update_summary_and_progress(jso.e.uuid); + if (jso.e) { + if (document.getElementById("esr-" + jso.e.uuid)) + document.getElementById("esr-" + jso.e.uuid).innerHTML = + sai_event_summary_render(jso, now_ut, 1); + update_summary_and_progress(jso.e.uuid); + } } else { @@ -2367,13 +2401,14 @@ function ws_open_sai() document.getElementById("sai_overview").innerHTML = s; logs_pending = times_pending = lines_pending = ""; - if (document.getElementById("esr-" + jso.e.uuid)) + if (jso.e && document.getElementById("esr-" + jso.e.uuid)) document.getElementById("esr-" + jso.e.uuid).innerHTML = sai_event_summary_render(jso, now_ut, 1); } } - update_summary_and_progress(jso.e.uuid); + if (jso.e) + update_summary_and_progress(jso.e.uuid); if (document.getElementById("rebuild-" + san(jso.t.uuid))) { document.getElementById("rebuild-" + san(jso.t.uuid)). diff --git a/src/builder/b-ws-server.c b/src/builder/b-ws-server.c index 272e84e..31ef5ff 100644 --- a/src/builder/b-ws-server.c +++ b/src/builder/b-ws-server.c @@ -519,6 +519,10 @@ saib_sul_load_report_cb(struct lws_sorted_usec_list *sul) ati->est_peak_mem_kib = ns->task->est_peak_mem_kib; ati->est_disk_kib = ns->task->est_disk_kib; ati->started = ns->task->started; + if (ns->task->repo_name) + lws_strncpy(ati->repo_name, ns->task->repo_name, sizeof(ati->repo_name)); + if (ns->task->git_hash) + lws_strncpy(ati->git_hash, ns->task->git_hash, sizeof(ati->git_hash)); lws_dll2_add_tail(&ati->list, &lr.active_tasks); lr.active_steps++; diff --git a/src/common/include/private.h b/src/common/include/private.h index d316703..eb4f709 100644 --- a/src/common/include/private.h +++ b/src/common/include/private.h @@ -83,6 +83,8 @@ typedef struct sai_active_task_info { unsigned int est_peak_mem_kib; unsigned int est_disk_kib; uint64_t started; + char repo_name[64]; + char git_hash[65]; } sai_active_task_info_t; typedef struct sai_load_report { diff --git a/src/common/struct-metadata.c b/src/common/struct-metadata.c index 1c9dd09..a310e10 100644 --- a/src/common/struct-metadata.c +++ b/src/common/struct-metadata.c @@ -36,6 +36,8 @@ const lws_struct_map_t lsm_active_task_info[] = { LSM_UNSIGNED (sai_active_task_info_t, est_peak_mem_kib, "est_peak_mem_kib"), LSM_UNSIGNED (sai_active_task_info_t, est_disk_kib, "est_disk_kib"), LSM_UNSIGNED (sai_active_task_info_t, started, "started"), + LSM_CARRAY (sai_active_task_info_t, repo_name, "repo_name"), + LSM_CARRAY (sai_active_task_info_t, git_hash, "git_hash"), }; const lws_struct_map_t lsm_load_report_members[] = { diff --git a/src/web/w-comms.c b/src/web/w-comms.c index acf5a42..c156d0f 100644 --- a/src/web/w-comms.c +++ b/src/web/w-comms.c @@ -448,6 +448,16 @@ http_resp: pss->wsi = wsi; pss->vhd = vhd; + pss->is_gitohashi = 1; + { + int r = 0; + char tbuf[96]; + while (lws_hdr_copy_fragment(wsi, tbuf, sizeof(tbuf), WSI_TOKEN_HTTP_URI_ARGS, r++) >= 0) { + if (!strncmp(tbuf, "client=sai", 10)) { + pss->is_gitohashi = 0; + } + } + } pss->alang[0] = '\0'; lws_hdr_copy(wsi, pss->alang, sizeof(pss->alang), WSI_TOKEN_HTTP_ACCEPT_LANGUAGE); diff --git a/src/web/w-private.h b/src/web/w-private.h index bdecaab..d4d16a0 100644 --- a/src/web/w-private.h +++ b/src/web/w-private.h @@ -62,6 +62,7 @@ enum { struct pss { struct vhd *vhd; struct lws *wsi; + uint8_t is_gitohashi:1; struct lws_spa *spa; struct lejp_ctx ctx; @@ -226,6 +227,10 @@ void saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(struct vhd *vhd, const void *buf, size_t len, enum lws_write_protocol flags); +int +saiw_ws_browser_queue_REQUIRES_LWS_PRE(struct pss *pss, const void *buf, + size_t len, enum lws_write_protocol flags); + void saiw_browser_state_changed(struct pss *pss, int established); diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c index 30c9a36..417a07c 100644 --- a/src/web/w-ws-browser.c +++ b/src/web/w-ws-browser.c @@ -520,7 +520,8 @@ saiw_browsers_task_state_change(struct vhd *vhd, const char *task_uuid) lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) { struct pss *pss = lws_container_of(p, struct pss, same); - saiw_pss_schedule_taskinfo(pss, task_uuid, 0); + if (!pss->is_gitohashi) + saiw_pss_schedule_taskinfo(pss, task_uuid, 0); } lws_end_foreach_dll(p); return 0; @@ -533,7 +534,8 @@ saiw_event_state_change(struct vhd *vhd, const char *event_uuid) lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) { struct pss *pss = lws_container_of(p, struct pss, same); - saiw_pss_schedule_eventinfo(pss, event_uuid); + if (!pss->is_gitohashi) + saiw_pss_schedule_eventinfo(pss, event_uuid); } lws_end_foreach_dll(p); return 0; @@ -1026,84 +1028,75 @@ saiw_browser_queue_overview(struct vhd *vhd, struct pss *pss) e = lws_container_of(walk, sai_event_t, list); lws_dll2_owner_clear(&task_owner); - do { - task_ac = NULL; - - if (sai_event_db_ensure_open(vhd->context, &vhd->sqlite3_cache, - vhd->sqlite3_path_lhs, e->uuid, 0, &pdb)) { - lwsl_err("%s: unable to open event-specific database\n", - __func__); - - break; - } + task_index = 0; + if (sai_event_db_ensure_open(vhd->context, &vhd->sqlite3_cache, + vhd->sqlite3_path_lhs, e->uuid, 0, &pdb)) { + lwsl_err("%s: unable to open event-specific database\n", + __func__); + } else { + task_ac = NULL; lws_dll2_owner_clear(&task_owner); if (lws_struct_sq3_deserialize(pdb, NULL, NULL, lsm_schema_sq3_map_task, &task_owner, - &task_ac, (int)task_index, 1)) { + &task_ac, 0, 999)) { lwsl_err("%s: OVERVIEW 1 failed\n", __func__); - sai_event_db_close(&vhd->sqlite3_cache, &pdb); - - break; + } else { + lws_start_foreach_dll(struct lws_dll2 *, pt, task_owner.head) { + t = lws_container_of(pt, sai_task_t, list); + + if (task_index) + *p++ = ','; + + /* + * We don't want to send everyone the artifact nonces... + * the up nonce is a key for uploading artifacts on to + * this task, it should only be stored in the server db + * and sent to the builder to use. + * + * The down nonce is used in generated links, but still + * you should have to acquire such a link via whatever + * auth rather than be able to cook them up yourself + * from knowing the task uuid. + */ + + t->art_up_nonce[0] = '\0'; + t->art_down_nonce[0] = '\0'; + + t->rebuildable = (t->state == SAIES_FAIL || t->state == SAIES_CANCELLED) && + (lws_now_secs() - (t->started + t->duration / 1000000) < 24 * 3600); + + js = lws_struct_json_serialize_create( + lsm_schema_json_map_task, + LWS_ARRAY_SIZE(lsm_schema_json_map_task), 0, t); + + t->build[0] = '\0'; + + do { + n = (int)lws_struct_json_serialize(js, (uint8_t *)p, lws_ptr_diff_size_t(end, p), &w); + switch (n) { + case LSJS_RESULT_FINISH: + lws_struct_json_serialize_destroy(&js); + p += w; + break; + + case LSJS_RESULT_CONTINUE: + p += w; + saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, + lws_ptr_diff_size_t(p, start), + lws_write_ws_flags(LWS_WRITE_TEXT, 0, 0)); + p = start; + break; + } + } while (n == LSJS_RESULT_CONTINUE); + + task_index++; + } lws_end_foreach_dll(pt); } - sai_event_db_close(&vhd->sqlite3_cache, &pdb); - - if (!task_owner.count) - break; - - if (task_index) - *p++ = ','; - - /* - * We don't want to send everyone the artifact nonces... - * the up nonce is a key for uploading artifacts on to - * this task, it should only be stored in the server db - * and sent to the builder to use. - * - * The down nonce is used in generated links, but still - * you should have to acquire such a link via whatever - * auth rather than be able to cook them up yourself - * from knowing the task uuid. - */ - - t = (sai_task_t *)task_owner.head; - t->art_up_nonce[0] = '\0'; - t->art_down_nonce[0] = '\0'; - - t->rebuildable = (t->state == SAIES_FAIL || t->state == SAIES_CANCELLED) && - (lws_now_secs() - (t->started + t->duration / 1000000) < 24 * 3600); - - /* only one in it at a time */ - t = lws_container_of(task_owner.head, sai_task_t, list); - - js = lws_struct_json_serialize_create( - lsm_schema_json_map_task, - LWS_ARRAY_SIZE(lsm_schema_json_map_task), 0, t); - - t->build[0] = '\0'; - - do { - n = (int)lws_struct_json_serialize(js, (uint8_t *)p, lws_ptr_diff_size_t(end, p), &w); - switch (n) { - case LSJS_RESULT_FINISH: - lws_struct_json_serialize_destroy(&js); - p += w; - break; - - case LSJS_RESULT_CONTINUE: - p += w; - saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, - lws_ptr_diff_size_t(p, start), - lws_write_ws_flags(LWS_WRITE_TEXT, 0, 0)); - p = start; - break; - } - } while (n == LSJS_RESULT_CONTINUE); lwsac_free(&task_ac); - - task_index++; - } while (1); + sai_event_db_close(&vhd->sqlite3_cache, &pdb); + } /* none left to do, go back up a level */ diff --git a/src/web/w-ws-server.c b/src/web/w-ws-server.c index e31a7e4..a994150 100644 --- a/src/web/w-ws-server.c +++ b/src/web/w-ws-server.c @@ -125,13 +125,20 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags) * "schema" member is parsed, even on the first fragment. */ switch (m->a.top_schema_index) { - case SAIS_WS_WEBSRV_RX_LOADREPORT: case SAIS_WS_WEBSRV_RX_TASKACTIVITY: saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, p, rem, lws_write_ws_flags(LWS_WRITE_TEXT, is_start, 0)); /* Not EOM */ break; + case SAIS_WS_WEBSRV_RX_LOADREPORT: + lws_start_foreach_dll(struct lws_dll2 *, pt, vhd->browsers.head) { + struct pss *pss = lws_container_of(pt, struct pss, same); + if (!pss->is_gitohashi) + saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, p, rem, + lws_write_ws_flags(LWS_WRITE_TEXT, is_start, 0)); + } lws_end_foreach_dll(pt); + break; default: // lwsl_err("%s: SWALLOWING %.*s\n", __func__, (int)len, buf); break; @@ -148,13 +155,20 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags) switch (m->a.top_schema_index) { case SAIS_WS_WEBSRV_RX_TASKCHANGE: case SAIS_WS_WEBSRV_RX_EVENTCHANGE: - case SAIS_WS_WEBSRV_RX_LOADREPORT: case SAIS_WS_WEBSRV_RX_TASKACTIVITY: saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, p, consumed, lws_write_ws_flags(LWS_WRITE_TEXT, is_start, 1)); /* Force EOM */ break; + case SAIS_WS_WEBSRV_RX_LOADREPORT: + lws_start_foreach_dll(struct lws_dll2 *, pt, vhd->browsers.head) { + struct pss *pss = lws_container_of(pt, struct pss, same); + if (!pss->is_gitohashi) + saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, p, consumed, + lws_write_ws_flags(LWS_WRITE_TEXT, is_start, 1)); + } lws_end_foreach_dll(pt); + break; } /* @@ -199,7 +213,8 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags) lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) { struct pss *pss = lws_container_of(p, struct pss, same); - saiw_browser_broadcast_queue_builders(pss->vhd, pss); + if (!pss->is_gitohashi) + saiw_browser_broadcast_queue_builders(pss->vhd, pss); } lws_end_foreach_dll(p); break; @@ -222,7 +237,8 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags) lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) { struct pss *pss = lws_container_of(p, struct pss, same); - saiw_browser_broadcast_queue_builders(pss->vhd, pss); + if (!pss->is_gitohashi) + saiw_browser_broadcast_queue_pcons(pss->vhd, pss); } lws_end_foreach_dll(p); break; @@ -231,8 +247,10 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags) lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) { struct pss *pss = lws_container_of(p, struct pss, same); - saiw_browser_broadcast_queue_power_history(pss->vhd, pss); - saiw_browser_broadcast_queue_pcon_energy(pss->vhd, pss, (sai_pcon_energy_report_t *)m->a.dest); + if (!pss->is_gitohashi) { + saiw_browser_broadcast_queue_power_history(pss->vhd, pss); + saiw_browser_broadcast_queue_pcon_energy(pss->vhd, pss, (sai_pcon_energy_report_t *)m->a.dest); + } } lws_end_foreach_dll(p); break;