chore(release): bump to 1.4.9
This commit is contained in:
1 parent
7e4c3a0963
commit
68bcc0cb5f
3 files changed
+3
-3
No files matched your search
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dashcaddy-api",
|
||||
"version": "1.4.8",
|
||||
"version": "1.4.9",
|
||||
"description": "DashCaddy API server - Dashboard backend for Docker, Caddy & DNS management",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
||||
Vendored
+1
-1
@@ -16,7 +16,7 @@
|
||||
The interactive tour is unavailable, but you can explore the dashboard freely.
|
||||
Check the documentation for help getting started.
|
||||
</p>
|
||||
`,document.body.appendChild(g),setTimeout(()=>{g.parentNode&&g.parentNode.removeChild(g)},1e4),!0}handleStorageUnavailable(){this.logError("Storage Unavailable","Local storage is not available");const g={data:{},getItem(i){return this.data[i]||null},setItem(i,s){this.data[i]=s},removeItem(i){delete this.data[i]},clear(){this.data={}}};return console.warn("[ErrorHandler] Using in-memory storage - progress will not persist"),g}sendToErrorTracking(g){}}o.ErrorHandler=f,console.log("[ErrorHandler] Module loaded")})(window);const DC={NAME:"DashCaddy",POLL:{DASHBOARD:1e4,LOGS:3e3,STATS:5e3,WEATHER:6e5,HEALTH:1e3,DEPLOY_SSL:5e3},DELAYS:{BTN_RESET:2e3,RELOAD:5e3,MODAL_CLOSE:500,PORT_CHECK:500,DEPLOY_INIT:3e3},DEFAULTS:{DNS_PORT:"5380",SERVICE_PORT:"8080",TTL:300,CADDYFILE:"C:\\caddy\\Caddyfile"}},errorHandler=new ErrorHandler,_cachedCfg=JSON.parse(localStorage.getItem("dashcaddy_site_config")||"null"),SITE={tld:_cachedCfg&&_cachedCfg.tld||".home",dnsIp:"",dnsPort:DC.DEFAULTS.DNS_PORT,dnsServers:{},configurationType:_cachedCfg&&_cachedCfg.configurationType||"homelab",domain:_cachedCfg&&_cachedCfg.domain||"",defaults:_cachedCfg&&_cachedCfg.defaults||{},routingMode:_cachedCfg&&_cachedCfg.routingMode||"subdomain",onboardingCompleted:!1};window.__dashcaddySiteConfigLoaded=(async function(){try{const g=await fetch("/api/v1/config");if(g.ok){const i=await g.json();if(i.tld&&(SITE.tld=i.tld.startsWith(".")?i.tld:"."+i.tld),i.dns&&(SITE.dnsIp=i.dns.ip||"",SITE.dnsPort=i.dns.port||DC.DEFAULTS.DNS_PORT),i.dnsServers&&typeof i.dnsServers=="object")for(const[h,n]of Object.entries(i.dnsServers))h!=="__proto__"&&h!=="constructor"&&h!=="prototype"&&(SITE.dnsServers[h]=n);i.configurationType&&(SITE.configurationType=i.configurationType),i.domain&&(SITE.domain=i.domain),i.defaults&&(SITE.defaults=i.defaults),i.routingMode&&(SITE.routingMode=i.routingMode),SITE.onboardingCompleted=i.onboardingCompleted===!0,localStorage.setItem("dashcaddy_site_config",JSON.stringify({tld:SITE.tld,configurationType:SITE.configurationType,domain:SITE.domain,routingMode:SITE.routingMode})),renderDnsCards();const s=document.getElementById("manage-tokens");s&&(s.style.display=Object.keys(SITE.dnsServers).length?"":"none")}}catch{}document.querySelectorAll("[data-tld]").forEach(g=>g.textContent=SITE.tld);const f=document.getElementById("edit-tld-suffix");f&&(f.textContent=SITE.tld);const u=document.getElementById("external-proxy-ip");u&&SITE.dnsIp&&(u.value=SITE.dnsIp,u.placeholder=SITE.dnsIp)})();function buildDomain(o){return o+SITE.tld}function buildServiceUrl(o){return SITE.routingMode==="subdirectory"&&SITE.domain?"https://"+SITE.domain+"/"+o:SITE.configurationType==="public"&&SITE.domain?"https://"+o+"."+SITE.domain:"https://"+buildDomain(o)}function getDnsServerAddr(o){const f=SITE.dnsServers[o];return f?`${f.ip}:${f.port}`:buildDomain(o)}function getPrimaryDnsId(){if(!SITE.dnsIp)return null;for(const[o,f]of Object.entries(SITE.dnsServers))if(f.ip===SITE.dnsIp)return o;return null}function renderDnsCards(){const o=document.querySelector(".top");if(!o)return;const f=Object.keys(SITE.dnsServers);if(!f.length)return;const u='<svg viewBox="0 0 24 24" class="service-icon"><rect x="3" y="4" width="18" height="16" rx="2" fill="#34495e"/><rect x="5" y="6" width="14" height="2" rx="1" fill="#3498db"/><rect x="5" y="9" width="10" height="1" fill="#ecf0f1"/><rect x="5" y="11" width="12" height="1" fill="#ecf0f1"/><rect x="5" y="13" width="8" height="1" fill="#ecf0f1"/><rect x="5" y="15" width="14" height="1" fill="#ecf0f1"/><circle cx="17" cy="11" r="2" fill="#e74c3c"/><path d="M17 9v4M15 11h4" stroke="white" stroke-width="1"/></svg>',g=o.firstElementChild;f.forEach(i=>{const s=escapeHtml(i),h=escapeHtml((SITE.dnsServers[i].name||i).toUpperCase()),n=document.createElement("div");n.className="card",n.setAttribute("data-app",i),n.setAttribute("data-status","off"),n.innerHTML=`<span id="${s}-dot" class="dot bad at-bl"></span><div class="row"><div class="logo-wrap">${u}</div><span class="name">${h}</span><span class="spacer"></span><span id="${s}-pill" class="badge off">OFF</span></div><div class="response-row"><span id="${s}-time" class="response-time">--</span></div><div class="health-row" id="health-${s}"><span id="uptime-${s}" class="uptime-chip">--</span><div class="uptime-mini-bar"><div class="fill" id="uptime-bar-${s}" style="width: 0%"></div></div></div><div class="btn-row"><button id="${s}-restart" class="restart-btn">Restart</button><button id="${s}-update" class="update-btn" title="Update DNS server">\u2B06\uFE0F</button><button id="${s}-open">Open</button><button id="${s}-logs" class="logs-btn">Logs</button><button id="${s}-settings" class="settings-btn">\u2699\uFE0F</button></div>`,o.insertBefore(n,g)})}window.renderDnsCards=renderDnsCards;let csrfToken=null;async function getCSRFToken(){if(csrfToken)return csrfToken;try{const o=await fetch("/api/v1/csrf-token");if(!o.ok)throw new Error("Failed to fetch CSRF token");return csrfToken=(await o.jsLine truncated
|
||||
`,document.body.appendChild(g),setTimeout(()=>{g.parentNode&&g.parentNode.removeChild(g)},1e4),!0}handleStorageUnavailable(){this.logError("Storage Unavailable","Local storage is not available");const g={data:{},getItem(i){return this.data[i]||null},setItem(i,s){this.data[i]=s},removeItem(i){delete this.data[i]},clear(){this.data={}}};return console.warn("[ErrorHandler] Using in-memory storage - progress will not persist"),g}sendToErrorTracking(g){}}o.ErrorHandler=f,console.log("[ErrorHandler] Module loaded")})(window);const DC={NAME:"DashCaddy",POLL:{DASHBOARD:1e4,LOGS:3e3,STATS:5e3,WEATHER:6e5,HEALTH:1e3,DEPLOY_SSL:5e3},DELAYS:{BTN_RESET:2e3,RELOAD:5e3,MODAL_CLOSE:500,PORT_CHECK:500,DEPLOY_INIT:3e3},DEFAULTS:{DNS_PORT:"5380",SERVICE_PORT:"8080",TTL:300,CADDYFILE:"C:\\caddy\\Caddyfile"}},errorHandler=new ErrorHandler,_cachedCfg=JSON.parse(localStorage.getItem("dashcaddy_site_config")||"null"),SITE={tld:_cachedCfg&&_cachedCfg.tld||".home",dnsIp:"",dnsPort:DC.DEFAULTS.DNS_PORT,dnsServers:{},configurationType:_cachedCfg&&_cachedCfg.configurationType||"homelab",domain:_cachedCfg&&_cachedCfg.domain||"",defaults:_cachedCfg&&_cachedCfg.defaults||{},routingMode:_cachedCfg&&_cachedCfg.routingMode||"subdomain",onboardingCompleted:!1};window.__dashcaddySiteConfigLoaded=(async function(){try{const g=await fetch("/api/v1/config");if(g.ok){const i=await g.json();if(i.tld&&(SITE.tld=i.tld.startsWith(".")?i.tld:"."+i.tld),i.dns&&(SITE.dnsIp=i.dns.ip||"",SITE.dnsPort=i.dns.port||DC.DEFAULTS.DNS_PORT),i.dnsServers&&typeof i.dnsServers=="object")for(const[h,n]of Object.entries(i.dnsServers))h!=="__proto__"&&h!=="constructor"&&h!=="prototype"&&(SITE.dnsServers[h]=n);i.configurationType&&(SITE.configurationType=i.configurationType),i.domain&&(SITE.domain=i.domain),i.defaults&&(SITE.defaults=i.defaults),i.routingMode&&(SITE.routingMode=i.routingMode),SITE.onboardingCompleted=i.onboardingCompleted===!0,localStorage.setItem("dashcaddy_site_config",JSON.stringify({tld:SITE.tld,configurationType:SITE.configurationType,domain:SITE.domain,routingMode:SITE.routingMode})),renderDnsCards();const s=document.getElementById("manage-tokens");s&&(s.style.display=Object.keys(SITE.dnsServers).length?"":"none")}}catch{}document.querySelectorAll("[data-tld]").forEach(g=>g.textContent=SITE.tld);const f=document.getElementById("edit-tld-suffix");f&&(f.textContent=SITE.tld);const u=document.getElementById("external-proxy-ip");u&&SITE.dnsIp&&(u.value=SITE.dnsIp,u.placeholder=SITE.dnsIp)})();function buildDomain(o){return o+SITE.tld}function buildServiceUrl(o){return SITE.routingMode==="subdirectory"&&SITE.domain?"https://"+SITE.domain+"/"+o:SITE.configurationType==="public"&&SITE.domain?"https://"+o+"."+SITE.domain:"https://"+buildDomain(o)}function getDnsServerAddr(o){const f=SITE.dnsServers[o];return f?`${f.ip}:${f.port}`:buildDomain(o)}function getPrimaryDnsId(){if(!SITE.dnsIp)return null;for(const[o,f]of Object.entries(SITE.dnsServers))if(f.ip===SITE.dnsIp)return o;return null}function renderDnsCards(){const o=document.querySelector(".top");if(!o)return;const f=Object.keys(SITE.dnsServers);if(!f.length)return;const u='<svg viewBox="0 0 24 24" class="service-icon"><rect x="3" y="4" width="18" height="16" rx="2" fill="#34495e"/><rect x="5" y="6" width="14" height="2" rx="1" fill="#3498db"/><rect x="5" y="9" width="10" height="1" fill="#ecf0f1"/><rect x="5" y="11" width="12" height="1" fill="#ecf0f1"/><rect x="5" y="13" width="8" height="1" fill="#ecf0f1"/><rect x="5" y="15" width="14" height="1" fill="#ecf0f1"/><circle cx="17" cy="11" r="2" fill="#e74c3c"/><path d="M17 9v4M15 11h4" stroke="white" stroke-width="1"/></svg>',g=o.firstElementChild;f.forEach(i=>{const s=escapeHtml(i),h=escapeHtml((SITE.dnsServers[i].name||i).toUpperCase()),n=document.createElement("div");n.className="card",n.setAttribute("data-app",i),n.setAttribute("data-status","off"),n.innerHTML=`<span id="${s}-dot" class="dot bad at-bl"></span><div class="row"><div class="logo-wrap">${u}</div><span class="name">${h}</span><span class="spacer"></span><span id="${s}-pill" class="badge off">OFF</span></div><div class="response-row"><span id="${s}-time" class="response-time">--</span></div><div class="health-row" id="health-${s}"><span id="uptime-${s}" class="uptime-chip">--</span><div class="uptime-mini-bar"><div class="fill" id="uptime-bar-${s}" style="width: 0%"></div></div></div><div class="btn-row"><button id="${s}-restart" class="restart-btn">Restart</button><button id="${s}-update" class="update-btn" title="Update DNS server">\u2B06\uFE0F</button><button id="${s}-open">Open</button><button id="${s}-logs" class="logs-btn">Logs</button><button id="${s}-settings" class="settings-btn">\u2699\uFE0F</button></div>`,o.insertBefore(n,g)})}window.renderDnsCards=renderDnsCards;let csrfToken=null;async function getCSRFToken(){if(csrfToken)return csrfToken;try{const o=await fetch("/api/v1/csrf-token");if(!o.ok)throw new Error("Failed to fetch CSRF token");return csrfToken=(await o.jsLine truncated
|
||||
position: fixed; top: 20px; right: 20px;
|
||||
background: ${s.bg}; color: ${s.fg};
|
||||
padding: 16px 24px; border-radius: 8px;
|
||||
|
||||
Vendored
+1
-1
@@ -616,7 +616,7 @@ Try refreshing in a moment if you see a certificate error.`,"warning",1e4),!1}};
|
||||
<span class="log-text" style="flex: 1; white-space: pre-wrap; word-break: break-all; color: #e5e5e5;">${r(d.text)}</span>
|
||||
</div>
|
||||
`}function s(d,l=""){if(!d||d.length===0){N.innerHTML='<div class="logs-loading" style="color: var(--muted); text-align: center; padding: 40px;">No logs available</div>',R.textContent="0 lines",M.textContent="0 filtered";return}if(z=d,f=l?d.filter(D=>D.text&&D.text.toLowerCase().includes(l.toLowerCase())):d,R.textContent=`${d.length} lines`,M.textContent=l?`${f.length} of ${d.length} shown`:`${d.length} shown`,f.length===0){N.innerHTML=`<div class="logs-loading" style="color: var(--muted); text-align: center; padding: 40px;">No logs match "${r(l)}"</div>`;return}N.innerHTML=f.map((D,O)=>c(D,O)).join(""),N.scrollTop=N.scrollHeight}async function h(){try{const l=(await getJSON("/api/v1/logs/containers")).containers||[],D=E.value;E.innerHTML='<option value="">Select a container...</option>',l.forEach(O=>{const F=document.createElement("option");F.value=O.id,F.textContent=`${O.name} (${O.image.split(":")[0]}) - ${O.status}`,F.dataset.name=O.name,F.dataset.image=O.image,F.dataset.status=O.status,F.dataset.created=O.created,E.appendChild(F)}),D&&E.querySelector(`option[value="${D}"]`)&&(E.value=D,u(D))}catch(d){console.error("Failed to load containers:",d)}}function u(d){const l=E.querySelector(`option[value="${d}"]`);l&&(j.textContent=l.dataset.image||"-",B.textContent=l.dataset.status||"-",B.style.color=l.dataset.status==="running"?"var(--ok-fg, #4ade80)":"var(--bad-fg, #ef4444)",A.textContent=m(l.dataset.created))}async function a(){const d=E.value;if(!d){N.innerHTML='<div class="logs-loading" style="color: var(--muted); text-align: center; padding: 40px;">Select a container to view logs</div>';return}n(),w=d,u(d);const l=T.value,D=S.value.trim();N.innerHTML='<div class="logs-loading" style="color: var(--muted); text-align: center; padding: 40px;">Loading logs...</div>';try{const O=`/api/v1/logs/container/${d}${l!=="all"?`?tail=${l}`:""}`,F=await getJSON(O);F.logs&&F.logs.length>0?s(F.logs,D):(N.innerHTML='<div class="logs-loading" style="color: var(--muted); text-align: center; padding: 40px;">No logs found for this container</div>',R.textContent="0 lines",M.textContent="0 filtered")}catch(O){N.innerHTML=`<div class="logs-loading" style="color: var(--bad-fg, #ef4444); text-align: center; padding: 40px;">Error loading logs: ${r(O.message)}</div>`}}function e(){const d=E.value;if(!d)return;n(),y=!0,L.textContent="\u23F9 Stop",x.style.display="flex",$.textContent="\u{1F7E2}",C.textContent="Connecting...";const l=`/api/v1/logs/stream/${d}`;p=new EventSource(l),p.onopen=()=>{$.textContent="\u{1F7E2}",C.textContent="Connected - streaming logs"},p.onmessage=D=>{try{const O=JSON.parse(D.data);if(O.error){$.textContent="\u{1F534}",C.textContent=`Error: ${O.error}`;return}z.push(O),f.push(O),R.textContent=`${z.length} lines`,M.textContent=`${f.length} shown`;const F=S.value.trim();if(!F||O.text&&O.text.toLowerCase().includes(F.toLowerCase())){const q=document.createElement("div");q.innerHTML=c(O,f.length-1);const U=q.firstElementChild;U.style.background="#1a3a1a",N.appendChild(U),N.scrollTop=N.scrollHeight}}catch(O){console.error("Error parsing log:",O)}},p.onerror=()=>{$.textContent="\u{1F534}",C.textContent="Disconnected",y=!1,L.textContent="\u25B6 Stream"},b._eventSource=p}function n(){p&&(p.close(),p=null),b._eventSource&&(b._eventSource.close(),b._eventSource=null),y=!1,L.textContent="\u25B6 Stream",x.style.display="none"}function t(){if(!z||z.length===0){showNotification("No logs to download","error");return}const d=E.querySelector(`option[value="${w}"]`)?.dataset.name||w,l=new Date().toISOString().replace(/[:.]/g,"-"),D=`${d}-logs-${l}.txt`,O=z.map(G=>{const W=G.timestamp||"",X=G.stream==="stderr"?"[ERR]":"[OUT]";return`${W?W+" ":""}${X} ${G.text}`}).join(`
|
||||
`),F=new Blob([O],{type:"text/plain"}),q=URL.createObjectURL(F),U=document.createElement("a");U.href=q,U.download=D,document.body.appendChild(U),U.click(),document.body.removeChild(U),URL.revokeObjectURL(q),showNotification(`Downloaded ${z.length} log lines`,"success")}E?.addEventListener("change",()=>{a()}),T?.addEventListener("change",()=>{a()}),P?.addEventListener("click",()=>{a()}),L?.addEventListener("click",()=>{y?n():e()}),H?.addEventListener("click",()=>{t()}),g?.addEventListener("click",()=>{S.value="",s(z,"")}),S?.addEventListener("input",()=>{clearTimeout(v),v=setTimeout(()=>{s(z,S.value.trim())},300)}),S?.addEventListener("keydown",d=>{d.key==="Escape"&&(S.value="",s(z,""))}),document.getElementById("view-container-logs")?.addEventListener("click",()=>{b.classList.add("show"),h()});function o(){n(),b.classList.remove("show")}I?.addEventListener("click",o),k?.addEventListener("click",o),document.addEventListener("keydown",d=>{d.key==="Escape"&&b.classList.contains("show")&&o()}),wireModal(b,null,o),window.openContainerLogsModal=function(d,l){b.classList.add("show"),h().then(()=>{const D=Array.from(E.options).find(O=>O.value===d||O.dataset.name===l);D?(E.value=D.value,u(D.value),a()):d?(w=d,j.textContent=l||d,B.textContent="-",A.textContent="-",a()):N.innerHTML='<div class="logs-loading" style="color: var(--muted); text-align: center; padding: 40px;">Select a container to view logs</div>'})}})(),(function(){injectModal("snapshot-modal",`<div id="snapshot-modal" class="weather-modal">
|
||||
`),F=new Blob([O],{type:"text/plain"}),q=URL.createObjectURL(F),U=document.createElement("a");U.href=q,U.download=D,document.body.appendChild(U),U.click(),document.body.removeChild(U),URL.revokeObjectURL(q),showNotification(`Downloaded ${z.length} log lines`,"success")}E?.addEventListener("change",()=>{a()}),T?.addEventListener("change",()=>{a()}),P?.addEventListener("click",()=>{a()}),L?.addEventListener("click",()=>{y?n():e()}),H?.addEventListener("click",()=>{t()}),g?.addEventListener("click",()=>{S.value="",s(z,"")}),S?.addEventListener("input",()=>{clearTimeout(v),v=setTimeout(()=>{s(z,S.value.trim())},300)}),S?.addEventListener("keydown",d=>{d.key==="Escape"&&(S.value="",s(z,""))}),document.getElementById("view-container-logs")?.addEventListener("click",()=>{b.classList.add("show"),h()});function o(){n(),b.classList.remove("show")}I?.addEventListener("click",o),k?.addEventListener("click",o),document.addEventListener("keydown",d=>{d.key==="Escape"&&b.classList.contains("show")&&o()}),b.addEventListener("click",d=>{d.target===b&&o()}),window.openContainerLogsModal=function(d,l){b.classList.add("show"),h().then(()=>{const D=Array.from(E.options).find(O=>O.value===d||O.dataset.name===l);D?(E.value=D.value,u(D.value),a()):d?(w=d,j.textContent=l||d,B.textContent="-",A.textContent="-",a()):N.innerHTML='<div class="logs-loading" style="color: var(--muted); text-align: center; padding: 40px;">Select a container to view logs</div>'})}})(),(function(){injectModal("snapshot-modal",`<div id="snapshot-modal" class="weather-modal">
|
||||
<div class="weather-modal-content" style="min-width: 700px; max-width: 850px;">
|
||||
<h3>\u{1F4BE} Container Snapshots</h3>
|
||||
<p class="modal-subtitle">
|
||||
|
||||
Reference in new issue
Block a user