diff --git a/assets/404.html b/assets/404.html
index 9d856c9..0da1a51 100644
--- a/assets/404.html
+++ b/assets/404.html
@@ -1,6 +1,8 @@
-<meta charset="UTF-8">
+<!DOCTYPE html>
<html lang="en">
- <body>
+<head>
+<meta charset="UTF-8">
+</head>
<img src="libwebsockets.org-logo.svg"><br>
<h1>404</h1>
Sorry, that file doesn't exist.
diff --git a/assets/failed-login.html b/assets/failed-login.html
index 66bfe9c..06f5d7a 100644
--- a/assets/failed-login.html
+++ b/assets/failed-login.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
This is an example destination that will appear after a failed login
</html>
diff --git a/assets/gs/admin-login.html b/assets/gs/admin-login.html
index fa36384..a6b2aef 100644
--- a/assets/gs/admin-login.html
+++ b/assets/gs/admin-login.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
This is an example destination that will appear after successful Admin login.
diff --git a/assets/gs/index.html b/assets/gs/index.html
index a0445a0..5d33a6c 100644
--- a/assets/gs/index.html
+++ b/assets/gs/index.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
diff --git a/assets/gs/post-forgot-fail.html b/assets/gs/post-forgot-fail.html
index ed575a6..7666c94 100644
--- a/assets/gs/post-forgot-fail.html
+++ b/assets/gs/post-forgot-fail.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
Sorry, something went wrong.
diff --git a/assets/gs/post-forgot-ok.html b/assets/gs/post-forgot-ok.html
index bfec1e0..27b0360 100644
--- a/assets/gs/post-forgot-ok.html
+++ b/assets/gs/post-forgot-ok.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
This is a one-time password recovery login.
diff --git a/assets/gs/post-verify-fail.html b/assets/gs/post-verify-fail.html
index d0e5b3c..66e2975 100644
--- a/assets/gs/post-verify-fail.html
+++ b/assets/gs/post-verify-fail.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
<head>
<script src="lwsgs.js"></script>
diff --git a/assets/gs/post-verify-ok.html b/assets/gs/post-verify-ok.html
index 918b713..3d704d4 100644
--- a/assets/gs/post-verify-ok.html
+++ b/assets/gs/post-verify-ok.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
<head>
<script src="lwsgs.js"></script>
diff --git a/assets/gs/sent-forgot-fail.html b/assets/gs/sent-forgot-fail.html
index ed575a6..7666c94 100644
--- a/assets/gs/sent-forgot-fail.html
+++ b/assets/gs/sent-forgot-fail.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
Sorry, something went wrong.
diff --git a/assets/gs/successful-login.html b/assets/gs/successful-login.html
index 39a47d5..f6ce3f2 100644
--- a/assets/gs/successful-login.html
+++ b/assets/gs/successful-login.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
This is an example destination that will appear after successful non-Admin login
</html>
diff --git a/assets/index.html b/assets/index.html
index 48ce7ea..ee8c0ec 100644
--- a/assets/index.html
+++ b/assets/index.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=utf-8 http-equiv="Content-Language" content="en"/>
diff --git a/assets/post-register-ok.html b/assets/post-register-ok.html
index b1bddd5..ba07895 100644
--- a/assets/post-register-ok.html
+++ b/assets/post-register-ok.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<html lang="en">
<body>
<table>
diff --git a/assets/sai.css b/assets/sai.css
index 9016e4b..ea28791 100644
--- a/assets/sai.css
+++ b/assets/sai.css
@@ -1179,10 +1179,10 @@ body.overlay-active {
}
.log-segment-wrapper {
- margin-top: 4px;
+ margin: 2px 0;
}
.log-segment-body .log-segment-wrapper {
- margin-left: 2em;
+ margin-left: 1em;
}
.log-segment-header {
background-color: #f8f9fa;
@@ -1192,8 +1192,7 @@ body.overlay-active {
font-weight: bold;
font-family: 'OpenSans', sans-serif;
font-size: 11px;
- margin-top: 1px;
- margin-bottom: 1px;
+ margin: 0;
user-select: none;
-webkit-user-select: none;
border-radius: 2px;
@@ -1241,6 +1240,11 @@ body.overlay-active {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
+ margin: 0;
+ padding: 0;
+}
+.seg-header-table td {
+ padding: 0;
}
.seg-td-icon {
@@ -1248,7 +1252,7 @@ body.overlay-active {
text-align: center;
}
-.seg-td-lines, .seg-td-errors {
+.seg-header-table .seg-td-lines, .seg-header-table .seg-td-errors {
width: 100px;
text-align: right;
padding-right: 10px;
@@ -1345,3 +1349,67 @@ canvas.power-graph {
background: #ffdc00;
color: black;
}
+.taskstate1.prog-0 { background: linear-gradient(90deg, #d0d0d0 0%, transparent 0%); }
+.taskstate1.prog-5 { background: linear-gradient(90deg, #d0d0d0 5%, transparent 5%); }
+.taskstate1.prog-10 { background: linear-gradient(90deg, #d0d0d0 10%, transparent 10%); }
+.taskstate1.prog-15 { background: linear-gradient(90deg, #d0d0d0 15%, transparent 15%); }
+.taskstate1.prog-20 { background: linear-gradient(90deg, #d0d0d0 20%, transparent 20%); }
+.taskstate1.prog-25 { background: linear-gradient(90deg, #d0d0d0 25%, transparent 25%); }
+.taskstate1.prog-30 { background: linear-gradient(90deg, #d0d0d0 30%, transparent 30%); }
+.taskstate1.prog-35 { background: linear-gradient(90deg, #d0d0d0 35%, transparent 35%); }
+.taskstate1.prog-40 { background: linear-gradient(90deg, #d0d0d0 40%, transparent 40%); }
+.taskstate1.prog-45 { background: linear-gradient(90deg, #d0d0d0 45%, transparent 45%); }
+.taskstate1.prog-50 { background: linear-gradient(90deg, #d0d0d0 50%, transparent 50%); }
+.taskstate1.prog-55 { background: linear-gradient(90deg, #d0d0d0 55%, transparent 55%); }
+.taskstate1.prog-60 { background: linear-gradient(90deg, #d0d0d0 60%, transparent 60%); }
+.taskstate1.prog-65 { background: linear-gradient(90deg, #d0d0d0 65%, transparent 65%); }
+.taskstate1.prog-70 { background: linear-gradient(90deg, #d0d0d0 70%, transparent 70%); }
+.taskstate1.prog-75 { background: linear-gradient(90deg, #d0d0d0 75%, transparent 75%); }
+.taskstate1.prog-80 { background: linear-gradient(90deg, #d0d0d0 80%, transparent 80%); }
+.taskstate1.prog-85 { background: linear-gradient(90deg, #d0d0d0 85%, transparent 85%); }
+.taskstate1.prog-90 { background: linear-gradient(90deg, #d0d0d0 90%, transparent 90%); }
+.taskstate1.prog-95 { background: linear-gradient(90deg, #d0d0d0 95%, transparent 95%); }
+.taskstate1.prog-100 { background: linear-gradient(90deg, #d0d0d0 100%, transparent 100%); }
+.taskstate2.prog-0 { background: linear-gradient(90deg, #a0a0a0 0%, transparent 0%); }
+.taskstate2.prog-5 { background: linear-gradient(90deg, #a0a0a0 5%, transparent 5%); }
+.taskstate2.prog-10 { background: linear-gradient(90deg, #a0a0a0 10%, transparent 10%); }
+.taskstate2.prog-15 { background: linear-gradient(90deg, #a0a0a0 15%, transparent 15%); }
+.taskstate2.prog-20 { background: linear-gradient(90deg, #a0a0a0 20%, transparent 20%); }
+.taskstate2.prog-25 { background: linear-gradient(90deg, #a0a0a0 25%, transparent 25%); }
+.taskstate2.prog-30 { background: linear-gradient(90deg, #a0a0a0 30%, transparent 30%); }
+.taskstate2.prog-35 { background: linear-gradient(90deg, #a0a0a0 35%, transparent 35%); }
+.taskstate2.prog-40 { background: linear-gradient(90deg, #a0a0a0 40%, transparent 40%); }
+.taskstate2.prog-45 { background: linear-gradient(90deg, #a0a0a0 45%, transparent 45%); }
+.taskstate2.prog-50 { background: linear-gradient(90deg, #a0a0a0 50%, transparent 50%); }
+.taskstate2.prog-55 { background: linear-gradient(90deg, #a0a0a0 55%, transparent 55%); }
+.taskstate2.prog-60 { background: linear-gradient(90deg, #a0a0a0 60%, transparent 60%); }
+.taskstate2.prog-65 { background: linear-gradient(90deg, #a0a0a0 65%, transparent 65%); }
+.taskstate2.prog-70 { background: linear-gradient(90deg, #a0a0a0 70%, transparent 70%); }
+.taskstate2.prog-75 { background: linear-gradient(90deg, #a0a0a0 75%, transparent 75%); }
+.taskstate2.prog-80 { background: linear-gradient(90deg, #a0a0a0 80%, transparent 80%); }
+.taskstate2.prog-85 { background: linear-gradient(90deg, #a0a0a0 85%, transparent 85%); }
+.taskstate2.prog-90 { background: linear-gradient(90deg, #a0a0a0 90%, transparent 90%); }
+.taskstate2.prog-95 { background: linear-gradient(90deg, #a0a0a0 95%, transparent 95%); }
+.taskstate2.prog-100 { background: linear-gradient(90deg, #a0a0a0 100%, transparent 100%); }
+.taskstate6.prog-0 { background: linear-gradient(90deg, #df3030 0%, transparent 0%); }
+.taskstate6.prog-5 { background: linear-gradient(90deg, #df3030 5%, transparent 5%); }
+.taskstate6.prog-10 { background: linear-gradient(90deg, #df3030 10%, transparent 10%); }
+.taskstate6.prog-15 { background: linear-gradient(90deg, #df3030 15%, transparent 15%); }
+.taskstate6.prog-20 { background: linear-gradient(90deg, #df3030 20%, transparent 20%); }
+.taskstate6.prog-25 { background: linear-gradient(90deg, #df3030 25%, transparent 25%); }
+.taskstate6.prog-30 { background: linear-gradient(90deg, #df3030 30%, transparent 30%); }
+.taskstate6.prog-35 { background: linear-gradient(90deg, #df3030 35%, transparent 35%); }
+.taskstate6.prog-40 { background: linear-gradient(90deg, #df3030 40%, transparent 40%); }
+.taskstate6.prog-45 { background: linear-gradient(90deg, #df3030 45%, transparent 45%); }
+.taskstate6.prog-50 { background: linear-gradient(90deg, #df3030 50%, transparent 50%); }
+.taskstate6.prog-55 { background: linear-gradient(90deg, #df3030 55%, transparent 55%); }
+.taskstate6.prog-60 { background: linear-gradient(90deg, #df3030 60%, transparent 60%); }
+.taskstate6.prog-65 { background: linear-gradient(90deg, #df3030 65%, transparent 65%); }
+.taskstate6.prog-70 { background: linear-gradient(90deg, #df3030 70%, transparent 70%); }
+.taskstate6.prog-75 { background: linear-gradient(90deg, #df3030 75%, transparent 75%); }
+.taskstate6.prog-80 { background: linear-gradient(90deg, #df3030 80%, transparent 80%); }
+.taskstate6.prog-85 { background: linear-gradient(90deg, #df3030 85%, transparent 85%); }
+.taskstate6.prog-90 { background: linear-gradient(90deg, #df3030 90%, transparent 90%); }
+.taskstate6.prog-95 { background: linear-gradient(90deg, #df3030 95%, transparent 95%); }
+.taskstate6.prog-100 { background: linear-gradient(90deg, #df3030 100%, transparent 100%); }
+
diff --git a/assets/sai.js b/assets/sai.js
index 787c0aa..d73564c 100644
--- a/assets/sai.js
+++ b/assets/sai.js
@@ -612,13 +612,11 @@ function flush_segments() {
}
function append_chunk_table(id, chunk_index, target_dom) {
- var html = `
- <table><tr>
- <td class="atop"><div class="dlogsn" id="dlogsn-${id}-${chunk_index}"></div></td>
- <td class="atop"><div class="dlogst" id="dlogst-${id}-${chunk_index}"></div></td>
- <td class="atop"><div class="dlogs"><span class="nowrap" id="dlogs-${id}-${chunk_index}"></span></div></td>
- </tr></table>
- `;
+ var html = '<table><tr>' +
+ '<td class="atop"><div class="dlogsn" id="dlogsn-' + id + '-' + chunk_index + '"></div></td>' +
+ '<td class="atop"><div class="dlogst" id="dlogst-' + id + '-' + chunk_index + '"></div></td>' +
+ '<td class="atop"><div class="dlogs"><span class="nowrap" id="dlogs-' + id + '-' + chunk_index + '"></span></div></td>' +
+ '</tr></table>';
if (target_dom) target_dom.insertAdjacentHTML('beforeend', html);
}
@@ -627,7 +625,7 @@ function push_segment(title, default_folded) {
seg_counter++;
var id = seg_counter;
- var seg = { id: id, title: title, lines_count: 0, error_count: 0, warning_count: 0, folded: default_folded, chunk_index: 0 };
+ var seg = { id: id, title: title, lines_count: 0, error_count: 0, warning_count: 0, folded: default_folded, chunk_index: 0, auto_unfolded: false, user_toggled: false };
var parent_id = segment_stack.length > 0 ? segment_stack[segment_stack.length - 1].id : "root";
// Append to root's dlogs container OR the parent segment's BODY container
@@ -639,18 +637,18 @@ function push_segment(title, default_folded) {
var hideClass = default_folded ? " hide" : "";
var clean_title = title.replace(/^[\s\S]*?(?:>|>)saib(?:>|>)\s*/i, '');
- var html = `<div class="log-segment-wrapper">
- <div class="log-segment-header" id="hdr-seg-${id}">
- <table class="seg-header-table"><tr>
- <td class="seg-td-icon"><span class="fold-icon">${icon}</span></td>
- <td class="seg-td-lines"><span class="seg-lines">0</span> lines</td>
- <td class="seg-td-errors"><span class="seg-errors"></span></td>
- <td class="seg-td-title"><span class="seg-title">${hsanitize(clean_title)}</span></td>
- </tr></table>
- </div>
- <div class="log-segment-body${hideClass}" id="seg-${id}">
- </div>
- </div>`;
+ var html = '<div class="log-segment-wrapper">' +
+ '<div class="log-segment-header" id="hdr-seg-' + id + '">' +
+ '<table class="seg-header-table"><tr>' +
+ '<td class="seg-td-icon"><span class="fold-icon">' + icon + '</span></td>' +
+ '<td class="seg-td-lines"><span class="seg-lines">0</span> lines</td>' +
+ '<td class="seg-td-errors"><span class="seg-errors"></span></td>' +
+ '<td class="seg-td-title"><span class="seg-title">' + hsanitize(clean_title) + '</span></td>' +
+ '</tr></table>' +
+ '</div>' +
+ '<div class="log-segment-body' + hideClass + '" id="seg-' + id + '">' +
+ '</div>' +
+ '</div>';
// If root, we only append once, but wait, root is just flat.
parent_dom.insertAdjacentHTML('beforeend', html);
@@ -669,6 +667,20 @@ function push_segment(title, default_folded) {
function pop_segment() {
if (segment_stack.length > 0) {
flush_segments();
+
+ var p = segment_stack[segment_stack.length - 1];
+ if (p.auto_unfolded && p.error_count === 0 && !p.user_toggled) {
+ var body = document.getElementById("seg-" + p.id);
+ var hdr = document.getElementById("hdr-seg-" + p.id);
+ if (body && !body.classList.contains("hide")) {
+ body.classList.add("hide");
+ if (hdr) {
+ var icon = hdr.querySelector('.fold-icon');
+ if (icon) icon.innerText = "▶";
+ }
+ }
+ }
+
segment_stack.pop();
// When we return to parent, we need a new table below the children we just popped
@@ -682,6 +694,12 @@ function pop_segment() {
}
function toggleSegment(id) {
+ for (var i = 0; i < segment_stack.length; i++) {
+ if (segment_stack[i].id == id) {
+ segment_stack[i].user_toggled = true;
+ break;
+ }
+ }
var body = document.getElementById("seg-" + id);
var hdr = document.getElementById("hdr-seg-" + id);
if (body && hdr) {
@@ -1053,8 +1071,8 @@ function sai_taskinfo_render(t, now_ut)
"id=\"stop-" + san(t.t.uuid) + "\"> ";
if (auth_is_admin)
s += "<img class=\"rebuild\" alt=\"rebuild\" src=\"rebuild.png\" " +
- "id=\"rebuild-" + san(t.t.uuid) + "\"> " +
- sai_stateful_taskname(t.t.state, t.t.taskname, 1);
+ "id=\"rebuild-" + san(t.t.uuid) + "\"> ";
+ s += sai_stateful_taskname(t.t.state, t.t.taskname, 1);
if (t.t.builder_name) {
var now_ut = Math.round((new Date().getTime() / 1000));
@@ -1411,11 +1429,13 @@ function check_and_apply_failure_ui() {
}
}
-function refresh_state(task_uuid, task_state)
+function refresh_state(t)
{
- var tsi = document.getElementById("taskstate_" + task_uuid);
+ var task_uuid = t.uuid;
+ var task_state = t.state;
+ var els = document.querySelectorAll("[id='taskstate_" + task_uuid + "']");
- if (tsi) {
+ els.forEach(function(tsi) {
tsi.classList.remove("taskstate0");
tsi.classList.remove("taskstate1");
tsi.classList.remove("taskstate2");
@@ -1426,8 +1446,26 @@ function refresh_state(task_uuid, task_state)
tsi.classList.remove("taskstate7");
tsi.classList.remove("taskstate10");
tsi.classList.add("taskstate" + task_state);
- // console.log("refresh_state taskstate" + task_state);
- }
+
+ var toRemove = [];
+ for (var i = 0; i < tsi.classList.length; i++) {
+ if (tsi.classList[i].startsWith('prog-')) {
+ toRemove.push(tsi.classList[i]);
+ }
+ }
+ toRemove.forEach(function(cls) { tsi.classList.remove(cls); });
+
+ if (task_state === 1 || task_state === 2 || task_state === 6) {
+ var total = typeof t.total_steps !== 'undefined' ? t.total_steps : t.build_step_count;
+ if (typeof t.build_step !== 'undefined' && typeof total !== 'undefined' && total >= 0) {
+ var pct = Math.round((t.build_step + 1) * 100 / (total + 2));
+ if (pct > 100) pct = 100;
+ if (pct < 0) pct = 0;
+ pct = Math.round(pct / 5) * 5;
+ tsi.classList.add("prog-" + pct);
+ }
+ }
+ });
const urlParams = new URLSearchParams(window.location.search);
const urlTask = urlParams.get('task');
@@ -2272,7 +2310,7 @@ function ws_open_sai()
/* if the task status icons exist, update their state */
for (n = jso.overview[0].t.length - 1; n >= 0; n--)
- refresh_state(jso.overview[0].t[n].uuid, jso.overview[0].t[n].state);
+ refresh_state(jso.overview[0].t[n]);
update_summary_and_progress(jso.overview[0].e.uuid);
@@ -2315,6 +2353,12 @@ function ws_open_sai()
esr_el.innerHTML =
sai_event_summary_render(jso.overview[n], now_ut, 1);
+ if (jso.overview[n].t) {
+ for (var q = 0; q < jso.overview[n].t.length; q++) {
+ refresh_state(jso.overview[n].t[q]);
+ }
+ }
+
update_summary_and_progress(jso.overview[n].e.uuid);
}
aging();
@@ -2416,7 +2460,7 @@ function ws_open_sai()
if (document.getElementById("taskstate_" + jso.t.uuid)) {
console.log("found taskstate_" + jso.t.uuid);
- refresh_state(jso.t.uuid, jso.t.state);
+ refresh_state(jso.t);
update_summary_and_progress(jso.t.uuid.substring(0, 32));
@@ -2868,6 +2912,22 @@ function ws_open_sai()
rightPane.clientHeight <=
rightPane.scrollTop + 1;
+ if (locked) {
+ for (var si = 0; si < segment_stack.length; si++) {
+ var sobj = segment_stack[si];
+ var sdom = document.getElementById("seg-" + sobj.id);
+ var hdom = document.getElementById("hdr-seg-" + sobj.id);
+ if (sdom && sdom.classList.contains("hide")) {
+ sdom.classList.remove("hide");
+ sobj.auto_unfolded = true;
+ if (hdom) {
+ var icon = hdom.querySelector('.fold-icon');
+ if (icon) icon.innerText = "▼";
+ }
+ }
+ }
+ }
+
flush_segments();
check_and_apply_failure_ui();
diff --git a/src/builder/b-power.c b/src/builder/b-power.c
index beaae9b..077abd9 100644
--- a/src/builder/b-power.c
+++ b/src/builder/b-power.c
@@ -325,6 +325,7 @@ saib_power_link_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
if (memcmp(buf, "ACK:", 4)) {
lwsl_warn("%s: sai-power didn't start power-off: %.*s\n",
__func__, (int)len, (const char *)buf);
+ lws_sul_cancel(&builder.sul_do_shutdown);
return LWSSSSRET_OK;
}
diff --git a/src/power/p-http-api.c b/src/power/p-http-api.c
index 30d27d3..518b2a9 100644
--- a/src/power/p-http-api.c
+++ b/src/power/p-http-api.c
@@ -594,11 +594,13 @@ power_off:
} lws_end_foreach_dll(px1);
*/
- if (needs[0] || (pc->flags & SAIP_PCON_F_NEEDED)) {
+ if (needs[0] || (pc->flags & SAIP_PCON_F_NEEDED) ||
+ (pc->flags & SAIP_PCON_F_MANUAL_STAY)) {
g->size = (size_t)lws_snprintf(g->payload,
sizeof(g->payload),
- "NAK: %s needed: %d, deps needed: '%s'",
- pn, !!(pc->flags & SAIP_PCON_F_NEEDED), needs);
+ "NAK: %s needed: %d, stay: %d, deps needed: '%s'",
+ pn, !!(pc->flags & SAIP_PCON_F_NEEDED),
+ !!(pc->flags & SAIP_PCON_F_MANUAL_STAY), needs);
goto bail;
}
}
diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c
index 9d957b0..c1e6293 100644
--- a/src/server/s-ws-builder.c
+++ b/src/server/s-ws-builder.c
@@ -378,6 +378,8 @@ sais_set_builder_power_state(struct vhd *vhd, const char *name, int up, int down
}
} lws_end_foreach_dll_safe(p, p1);
+ ps = NULL;
+
lws_start_foreach_dll(struct lws_dll2 *, p, vhd->server.power_state_owner.head) {
ps = lws_container_of(p, sai_power_state_t, list);