Add dashboard version button and self-update UI wiring
This commit is contained in:
3 files changed
+17
-2
No files matched your search
@@ -54,11 +54,25 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Frontend assets use browser globals
|
// Browser-side assets (client JS)
|
||||||
files: ['assets/**/*.js', 'frontend/**/*.js'],
|
files: ['assets/**/*.js', 'frontend/**/*.js'],
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es2021: true,
|
es2021: true,
|
||||||
|
node: false,
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
// Common dashboard globals from status/index.html context
|
||||||
|
apiUrl: 'readonly',
|
||||||
|
API_BASE_URL: 'readonly',
|
||||||
|
CONFIG: 'readonly',
|
||||||
|
// Client-side dashboard classes (loaded via script tags)
|
||||||
|
ErrorHandler: 'readonly',
|
||||||
|
ProgressTracker: 'readonly',
|
||||||
|
ThemeAdapter: 'readonly',
|
||||||
|
DnsTemplateSelector: 'readonly',
|
||||||
|
TourManager: 'readonly',
|
||||||
|
TooltipDefinitions: 'readonly',
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'no-undef': 'warn',
|
'no-undef': 'warn',
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1288,7 +1288,7 @@ Try refreshing in a moment if you see a certificate error.`,"warning",1e4),!1}};
|
|||||||
<option value="daily"${U==="daily"?" selected":""}>Daily</option>
|
<option value="daily"${U==="daily"?" selected":""}>Daily</option>
|
||||||
<option value="weekly"${U==="weekly"?" selected":""}>Weekly</option>
|
<option value="weekly"${U==="weekly"?" selected":""}>Weekly</option>
|
||||||
<option value="monthly"${U==="monthly"?" selected":""}>Monthly</option>
|
<option value="monthly"${U==="monthly"?" selected":""}>Monthly</option>
|
||||||
</select></td>`,w+=`<td style="padding: 8px;"><input type="text" class="auto-window" data-id="${escapeHtml(P)}" value="${escapeHtml(J)}" placeholder="02:00-04:00" style="width: 90px; padding: 3px 6px; font-size: 0.78rem; border: 1px solid var(--border); border-radius: 4px; background: var(--bg); color: var(--fg);" /></td>`,w+=`<td style="padding: 8px;"><input type="checkbox" class="auto-rollback" data-id="${escapeHtml(P)}"${_?" checked":""} /></td>`,w+=`<td style="padding: 8px; font-size: 0.78rem; color: var(--muted);">${F}</td>`,w+=`<td style="padding: 8px; text-align: right;"><button class="save-auto-btn" data-id="${escapeHtml(P)}" data-name="${escapeHtml($)}" style="padding: 4px 10px; font-size: 0.78rem;">Save</button></td>`,w+="</tr>"}w+="</table>",B.innerHTML=w,B.querySelectorAll(".save-auto-btn").forEach(T=>{T.addEventListener("click",async()=>{const $=T.dataset.id,P=T.closest("tr"),R=P.querySelector(".auto-schedule").value,U=P.querySelector(".auto-rollback").checked,_=P.querySelector(".auto-window").value.trim();T.textContent="Saving...",T.disabled=!0;try{const F=await(await secureFetch(`/api/v1/updates/auto-update/${encodeURIComponent($)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({enabled:!!R,schedule:R||"weekly",autoRollback:U,maintenanceWindow:_||void 0})})).json();if(F.success)T.textContent="\u2713 Saved";else throw new Error(F.error)}catch(J){T.textContent="\u2717 Error",showNotification("Save error: "+J.message,"error")}setTimeout(()=>{T.textContent="Save",T.disabled=!1},2e3)})})}catch(p){B.innerHTML=`<div class="panel-empty" style="color: var(--bad-fg);">Failed: ${escapeHtml(p.message)}</div>`}}const x=document.getElementById("dashcaddy-current-version"),O=document.getElementById("dashcaddy-update-badge"),A=document.getElementById("dashcaddy-update-details"),N=document.getElementById("dashcaddy-new-version"),z=document.getElementById("dashcaddy-changelog"),H=document.getElementById("dashcaddy-apply-btn"),L=document.getElementById("dashcaddy-check-btn"),g=document.getElementById("dashcaddy-rollback-btn"),I=document.getElementById("dashcaddy-status-bar"),c=document.getElementById("dashcaddy-history-container");let l=null;function a(p,b){I&&(I.style.display="block",I.style.background=b==="error"?"var(--bad-bg)":b==="success"?"var(--ok-bg)":"var(--bg)",I.style.color=b==="error"?"var(--bad-fg)":b==="success"?"var(--ok-fg)":"var(--fg)",I.textContent=p)}async function n(){try{const b=await(await fetch("/api/v1/system/version")).json();b.success&&(x.textContent="v"+b.version+(b.commit?" ("+b.commit.substring(0,7)+")":""))}catch{x.textContent="Unable to fetch version"}}async function e(p){p||(L.textContent="Checking...",L.disabled=!0);try{const o=await(await fetch("/api/v1/system/update-check")).json();if(l=o,o.success&&o.available&&o.remote){O.style.display="",A.style.display="",N.textContent="v"+o.remote.version,z.textContent=o.remote.changelog||"No changelog available.";const i=document.getElementById("updates-btn");if(i&&!i.querySelector(".update-dot")){const d=document.createElement("span");d.className="update-dot",d.style.cssText="position:absolute;top:2px;right:2px;width:8px;height:8px;border-radius:50%;background:var(--accent);",i.style.position="relative",i.appendChild(d)}const v=document.getElementById("updates-dashcaddy-tab");if(v&&!v.querySelector(".update-dot")){const d=document.createElement("span");d.className="update-dot",d.style.cssText="display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--accent);margin-left:4px;vertical-align:middle;",v.appendChild(d)}}else O.style.display="none",A.style.display="none",p||a("You are running the latest version.","success");p||(L.textContent="Check for Updates",L.disabled=!1)}catch(b){p||(a("Failed to check: "+b.message,"error"),L.textContent="Check for Updates",L.disabled=!1)}}async function t(){if(confirm("Apply DashCaddy update? The API container will restart.")){H.textContent="Updating...",H.disabled=!0,a("Downloading and applying update...","info");try{const b=await(await secureFetch("/api/v1/system/update-apply",{method:"POST"})).json();if(b.success)a("Update initiated: v"+(b.fromVersion||"?")+" \u2192 v"+(b.toVersion||"?")+". The container will restart shortly.","success"),H.textContent="Applied!",document.querySelectorAll(".update-dot").forEach(o=>o.remove());else throw new Error(b.error||"Update failed")}catch(p){a("Update failed: "+p.message,"error"),H.textContent="Update Now",H.disabled=!1}}}async function s(){try{const b=await(await fetch("/api/v1/system/update-history")).json(),o=b.success&&b.history?b.history:[];if(o.length===0){c.innerHTML='<div class="panel-empty"><span class="empty-icon">\u{1F4E6}</span>No self-update history.</div>';return}let i='<table style="width: 100%; border-collapse: collapse; font-size: 0.82rem;">';i+='<tr style="border-bottom: 1px solid var(--border); color: var(--muted);"><th style="padding: 6px; text-align: left;">When</th><th style="paLine truncated
|
</select></td>`,w+=`<td style="padding: 8px;"><input type="text" class="auto-window" data-id="${escapeHtml(P)}" value="${escapeHtml(J)}" placeholder="02:00-04:00" style="width: 90px; padding: 3px 6px; font-size: 0.78rem; border: 1px solid var(--border); border-radius: 4px; background: var(--bg); color: var(--fg);" /></td>`,w+=`<td style="padding: 8px;"><input type="checkbox" class="auto-rollback" data-id="${escapeHtml(P)}"${_?" checked":""} /></td>`,w+=`<td style="padding: 8px; font-size: 0.78rem; color: var(--muted);">${F}</td>`,w+=`<td style="padding: 8px; text-align: right;"><button class="save-auto-btn" data-id="${escapeHtml(P)}" data-name="${escapeHtml($)}" style="padding: 4px 10px; font-size: 0.78rem;">Save</button></td>`,w+="</tr>"}w+="</table>",B.innerHTML=w,B.querySelectorAll(".save-auto-btn").forEach(T=>{T.addEventListener("click",async()=>{const $=T.dataset.id,P=T.closest("tr"),R=P.querySelector(".auto-schedule").value,U=P.querySelector(".auto-rollback").checked,_=P.querySelector(".auto-window").value.trim();T.textContent="Saving...",T.disabled=!0;try{const F=await(await secureFetch(`/api/v1/updates/auto-update/${encodeURIComponent($)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({enabled:!!R,schedule:R||"weekly",autoRollback:U,maintenanceWindow:_||void 0})})).json();if(F.success)T.textContent="\u2713 Saved";else throw new Error(F.error)}catch(J){T.textContent="\u2717 Error",showNotification("Save error: "+J.message,"error")}setTimeout(()=>{T.textContent="Save",T.disabled=!1},2e3)})})}catch(p){B.innerHTML=`<div class="panel-empty" style="color: var(--bad-fg);">Failed: ${escapeHtml(p.message)}</div>`}}const x=document.getElementById("dashcaddy-current-version"),O=document.getElementById("dashcaddy-update-badge"),A=document.getElementById("dashcaddy-update-details"),N=document.getElementById("dashcaddy-new-version"),z=document.getElementById("dashcaddy-changelog"),H=document.getElementById("dashcaddy-apply-btn"),L=document.getElementById("dashcaddy-check-btn"),g=document.getElementById("dashcaddy-rollback-btn"),I=document.getElementById("dashcaddy-status-bar"),c=document.getElementById("dashcaddy-history-container");let l=null;function a(p,b){I&&(I.style.display="block",I.style.background=b==="error"?"var(--bad-bg)":b==="success"?"var(--ok-bg)":"var(--bg)",I.style.color=b==="error"?"var(--bad-fg)":b==="success"?"var(--ok-fg)":"var(--fg)",I.textContent=p)}async function n(){try{const b=await(await fetch("/api/v1/system/version")).json();b.success&&(x.textContent="v"+b.version+(b.commit?" ("+b.commit.substring(0,7)+")":""))}catch{x.textContent="Unable to fetch version"}}async function e(p){p||(L.textContent="Checking...",L.disabled=!0);try{const o=await(await fetch("/api/v1/system/update-check")).json();if(l=o,o.success&&o.available&&o.remote){O.style.display="",A.style.display="",N.textContent="v"+o.remote.version,z.textContent=o.remote.changelog||"No changelog available.";const i=document.getElementById("updates-btn");if(i&&!i.querySelector(".update-dot")){const d=document.createElement("span");d.className="update-dot",d.style.cssText="position:absolute;top:2px;right:2px;width:8px;height:8px;border-radius:50%;background:var(--accent);",i.style.position="relative",i.appendChild(d)}const v=document.getElementById("updates-dashcaddy-tab");if(v&&!v.querySelector(".update-dot")){const d=document.createElement("span");d.className="update-dot",d.style.cssText="display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--accent);margin-left:4px;vertical-align:middle;",v.appendChild(d)}}else O.style.display="none",A.style.display="none",await n(),p||a("You are running the latest version.","success");p||(L.textContent="Check for Updates",L.disabled=!1)}catch(b){p||(a("Failed to check: "+b.message,"error"),L.textContent="Check for Updates",L.disabled=!1)}}async function t(){if(confirm("Apply DashCaddy update? The API container will restart.")){H.textContent="Updating...",H.disabled=!0,a("Downloading and applying update...","info");try{const b=await(await secureFetch("/api/v1/system/update-apply",{method:"POST"})).json();if(b.success)a("Update initiated: v"+(b.fromVersion||"?")+" \u2192 v"+(b.toVersion||"?")+". The container will restart shortly.","success"),H.textContent="Applied!",document.querySelectorAll(".update-dot").forEach(o=>o.remove());else throw new Error(b.error||"Update failed")}catch(p){a("Update failed: "+p.message,"error"),H.textContent="Update Now",H.disabled=!1}}}async function s(){try{const b=await(await fetch("/api/v1/system/update-history")).json(),o=b.success&&b.history?b.history:[];if(o.length===0){c.innerHTML='<div class="panel-empty"><span class="empty-icon">\u{1F4E6}</span>No self-update history.</div>';return}let i='<table style="width: 100%; border-collapse: collapse; font-size: 0.82rem;">';i+='<tr style="border-bottom: 1px solid var(--border); color: var(--muted);"><th style="padding: 6px; text-align: left;">When</th><thLine truncated
|
||||||
`+o.join(`
|
`+o.join(`
|
||||||
`)+`
|
`)+`
|
||||||
|
|
||||||
|
|||||||
@@ -378,6 +378,7 @@
|
|||||||
} else {
|
} else {
|
||||||
dcUpdateBadge.style.display = 'none';
|
dcUpdateBadge.style.display = 'none';
|
||||||
dcUpdateDetails.style.display = 'none';
|
dcUpdateDetails.style.display = 'none';
|
||||||
|
await dcLoadVersion();
|
||||||
if (!silent) dcShowStatus('You are running the latest version.', 'success');
|
if (!silent) dcShowStatus('You are running the latest version.', 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user