signalk-container 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/805.js +1 -1
package/package.json
CHANGED
package/public/805.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";(self.webpackChunksignalk_container=self.webpackChunksignalk_container||[]).push([[805],{805(e,t,n){n.r(t),n.d(t,{default:()=>s});var a=n(231);const o={root:{fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',color:"#333",padding:"16px 0"},sectionTitle:{fontSize:13,fontWeight:600,color:"#888",textTransform:"uppercase",letterSpacing:"0.05em",marginBottom:10,marginTop:24},btn:{display:"inline-flex",alignItems:"center",gap:8,padding:"8px 16px",border:"none",borderRadius:6,fontSize:13,fontWeight:600,cursor:"pointer"},btnPrimary:{background:"#3b82f6",color:"#fff"},btnDanger:{background:"#ef4444",color:"#fff",padding:"6px 12px",fontSize:12},btnWarning:{background:"#f59e0b",color:"#fff",padding:"6px 12px",fontSize:12},btnSave:{background:"#3b82f6",color:"#fff"},btnSuccess:{background:"#10b981",color:"#fff"},btnDisabled:{opacity:.5,cursor:"not-allowed"},status:{marginTop:8,fontSize:12,minHeight:18},runtimeCard:{display:"flex",alignItems:"center",gap:14,padding:"14px 18px",background:"#f8f9fa",border:"1px solid #e0e0e0",borderRadius:10,marginBottom:12},runtimeIcon:{width:44,height:44,borderRadius:10,display:"flex",alignItems:"center",justifyContent:"center",fontSize:22,flexShrink:0},runtimeInfo:{flex:1},runtimeName:{fontSize:15,fontWeight:600,color:"#333"},runtimeVersion:{fontSize:12,color:"#888"},containerItem:{display:"flex",alignItems:"center",gap:12,padding:"10px 14px",background:"#f8f9fa",border:"1px solid #e0e0e0",borderRadius:10,marginBottom:8},stateIndicator:{width:10,height:10,borderRadius:"50%",flexShrink:0},containerInfo:{flex:1,minWidth:0},containerName:{fontSize:14,fontWeight:600,color:"#333"},containerMeta:{fontSize:11,color:"#888",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},containerActions:{display:"flex",gap:6,flexShrink:0},empty:{textAlign:"center",padding:"30px 16px",color:"#999",fontSize:13},fieldRow:{display:"flex",alignItems:"center",gap:12,marginBottom:10},label:{fontSize:13,fontWeight:500,color:"#555",width:160,flexShrink:0},select:{padding:"6px 10px",borderRadius:6,border:"1px solid #ccc",fontSize:13,background:"#fff",color:"#333"},hint:{fontSize:11,color:"#aaa",marginLeft:8},pruneResult:{fontSize:12,color:"#10b981",marginTop:6}},r={running:"#10b981",stopped:"#f59e0b",missing:"#94a3b8","no-runtime":"#ef4444"},i={running:"Running",stopped:"Stopped",missing:"Not created","no-runtime":"No runtime"};function l({label:e,value:t,options:n,onChange:r,hint:i}){return a.createElement("div",{style:o.fieldRow},a.createElement("span",{style:o.label},e),a.createElement("select",{style:o.select,value:t,onChange:e=>r(e.target.value)},n.map(e=>a.createElement("option",{key:e.value,value:e.value},e.label))),i&&a.createElement("span",{style:o.hint},i))}function s({configuration:e,save:t}){const n=e||{},[s,c]=(0,a.useState)(n.runtime||"auto"),[m,d]=(0,a.useState)(n.pruneSchedule||"weekly"),[u,p]=(0,a.useState)(null),[f,g]=(0,a.useState)([]),[b,y]=(0,a.useState)(!0),[h,v]=(0,a.useState)(""),[S,k]=(0,a.useState)(!1),[E,x]=(0,a.useState)(null),w=(0,a.useCallback)(async()=>{try{const[e,t]=await Promise.all([fetch("/plugins/signalk-container/api/runtime"),fetch("/plugins/signalk-container/api/containers")]);e.ok?p(await e.json()):p(null),t.ok?g(await t.json()):g([])}catch{p(null),g([])}y(!1)},[]);return(0,a.useEffect)(()=>{w();const e=setInterval(w,5e3);return()=>clearInterval(e)},[w]),a.createElement("div",{style:o.root},a.createElement("div",{style:o.sectionTitle},"Runtime"),b?a.createElement("div",{style:o.empty},"Detecting container runtime..."):u?a.createElement("div",{style:o.runtimeCard},a.createElement("div",{style:{...o.runtimeIcon,background:"podman"===u.runtime?"#892ca0":"#2496ed",color:"#fff"}},"podman"===u.runtime?"P":"D"),a.createElement("div",{style:o.runtimeInfo},a.createElement("div",{style:o.runtimeName},u.runtime.charAt(0).toUpperCase()+u.runtime.slice(1),u.isPodmanDockerShim?" (via docker shim)":""),a.createElement("div",{style:o.runtimeVersion},"Version ",u.version)),a.createElement("div",{style:{...o.stateIndicator,background:"#10b981"},title:"Runtime available"})):a.createElement("div",{style:o.runtimeCard},a.createElement("div",{style:{...o.runtimeIcon,background:"#fef2f2",color:"#ef4444"}},"!"),a.createElement("div",{style:o.runtimeInfo},a.createElement("div",{style:o.runtimeName},"No container runtime found"),a.createElement("div",{style:o.runtimeVersion},"Install Podman: sudo apt install podman"))),a.createElement("div",{style:o.sectionTitle},"Settings"),a.createElement(l,{label:"Preferred runtime",value:s,onChange:c,options:[{value:"auto",label:"Auto-detect (Podman preferred)"},{value:"podman",label:"Podman"},{value:"docker",label:"Docker"}]}),a.createElement(l,{label:"Auto-prune images",value:m,onChange:d,options:[{value:"off",label:"Off"},{value:"weekly",label:"Weekly"},{value:"monthly",label:"Monthly"}]}),a.createElement("div",{style:o.sectionTitle},"Managed Containers"),0===f.length?a.createElement("div",{style:o.empty},b?"Loading...":"No managed containers. Other plugins will create them."):f.map(e=>a.createElement("div",{key:e.name,style:o.containerItem},a.createElement("div",{style:{...o.stateIndicator,background:r[e.state]||"#94a3b8"},title:i[e.state]||e.state}),a.createElement("div",{style:o.containerInfo},a.createElement("div",{style:o.containerName},e.name),a.createElement("div",{style:o.containerMeta},e.image," · ",i[e.state]||e.state,e.ports&&e.ports.length>0&&e.ports[0]?` · ${e.ports.join(", ")}`:"")),a.createElement("div",{style:o.containerActions},"stopped"===e.state&&a.createElement("button",{style:{...o.btn,...o.btnPrimary,padding:"6px 12px",fontSize:12},onClick:()=>(async e=>{v(`Starting ${e}...`),k(!1);try{const t=await fetch(`/plugins/signalk-container/api/containers/${encodeURIComponent(e)}/start`,{method:"POST"});if(t.ok)v(`${e} started.`),w();else{const e=await t.json().catch(()=>({error:t.statusText}));v(`Failed: ${e.error}`),k(!0)}}catch(e){v(`Error: ${e.message}`),k(!0)}})(e.name)},"Start"),"running"===e.state&&a.createElement("button",{style:{...o.btn,...o.btnWarning},onClick:()=>(async e=>{v(`Stopping ${e}...`),k(!1);try{const t=await fetch(`/plugins/signalk-container/api/containers/${encodeURIComponent(e)}/stop`,{method:"POST"});if(t.ok)v(`${e} stopped.`),w();else{const e=await t.json().catch(()=>({error:t.statusText}));v(`Failed: ${e.error}`),k(!0)}}catch(e){v(`Error: ${e.message}`),k(!0)}})(e.name)},"Stop"),a.createElement("button",{style:{...o.btn,...o.btnDanger},onClick:()=>(async(e,t)=>{if("running"!==t||window.confirm(`${e} is running. Stop and remove it?`)){v(`Removing ${e}...`),k(!1);try{const t=await fetch(`/plugins/signalk-container/api/containers/${encodeURIComponent(e)}/remove`,{method:"POST"});if(t.ok)v(`${e} removed.`),w();else{const e=await t.json().catch(()=>({error:t.statusText}));v(`Failed: ${e.error}`),k(!0)}}catch(e){v(`Error: ${e.message}`),k(!0)}}})(e.name,e.state)},"Remove")))),a.createElement("div",{style:o.sectionTitle},"Maintenance"),a.createElement("button",{style:{...o.btn,...o.btnSuccess},onClick:async()=>{v("Pruning dangling images..."),k(!1),x(null);try{const e=await fetch("/plugins/signalk-container/api/prune",{method:"POST"});if(e.ok){const t=await e.json().catch(()=>({error:e.statusText}));x(t),v(`Pruned ${t.imagesRemoved} image(s), reclaimed ${t.spaceReclaimed}.`)}else{const t=await e.json().catch(()=>({error:e.statusText}));v(`Prune failed: ${t.error}`),k(!0)}}catch(e){v(`Error: ${e.message}`),k(!0)}}},"Prune Dangling Images"),h&&a.createElement("div",{style:{...o.status,color:S?"#ef4444":"#10b981"}},h),a.createElement("div",{style:{...o.sectionTitle,marginTop:28}}," "),a.createElement("button",{style:{...o.btn,...o.btnSave},onClick:()=>{t({runtime:s,pruneSchedule:m,maxConcurrentJobs:n.maxConcurrentJobs||2}),v("Saved! Plugin will restart."),k(!1)}},"Save Configuration"))}}}]);
|
|
1
|
+
"use strict";(self.webpackChunksignalk_container=self.webpackChunksignalk_container||[]).push([[805],{805(e,t,n){n.r(t),n.d(t,{default:()=>u});var r=n(231);const i={root:{fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',color:"#333",padding:"16px 0"},sectionTitle:{fontSize:13,fontWeight:600,color:"#888",textTransform:"uppercase",letterSpacing:"0.05em",marginBottom:10,marginTop:24},btn:{display:"inline-flex",alignItems:"center",gap:8,padding:"8px 16px",border:"none",borderRadius:6,fontSize:13,fontWeight:600,cursor:"pointer"},btnPrimary:{background:"#3b82f6",color:"#fff"},btnDanger:{background:"#ef4444",color:"#fff",padding:"6px 12px",fontSize:12},btnWarning:{background:"#f59e0b",color:"#fff",padding:"6px 12px",fontSize:12},btnSave:{background:"#3b82f6",color:"#fff"},btnSuccess:{background:"#10b981",color:"#fff"},btnDisabled:{opacity:.5,cursor:"not-allowed"},status:{marginTop:8,fontSize:12,minHeight:18},runtimeCard:{display:"flex",alignItems:"center",gap:14,padding:"14px 18px",background:"#f8f9fa",border:"1px solid #e0e0e0",borderRadius:10,marginBottom:12},runtimeIcon:{width:44,height:44,borderRadius:10,display:"flex",alignItems:"center",justifyContent:"center",fontSize:22,flexShrink:0},runtimeInfo:{flex:1},runtimeName:{fontSize:15,fontWeight:600,color:"#333"},runtimeVersion:{fontSize:12,color:"#888"},containerItem:{display:"flex",alignItems:"center",gap:12,padding:"10px 14px",background:"#f8f9fa",border:"1px solid #e0e0e0",borderRadius:10,marginBottom:8},stateIndicator:{width:10,height:10,borderRadius:"50%",flexShrink:0},containerInfo:{flex:1,minWidth:0},containerName:{fontSize:14,fontWeight:600,color:"#333"},containerMeta:{fontSize:11,color:"#888",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},containerActions:{display:"flex",gap:6,flexShrink:0},containerCard:{background:"#f8f9fa",border:"1px solid #e0e0e0",borderRadius:10,marginBottom:8,overflow:"hidden"},containerItemFlat:{display:"flex",alignItems:"center",gap:12,padding:"10px 14px"},limitsRow:{display:"flex",alignItems:"center",gap:10,padding:"6px 14px 10px 34px",fontSize:11,color:"#666",flexWrap:"wrap"},limitBadge:{display:"inline-flex",alignItems:"center",padding:"2px 8px",borderRadius:12,background:"#e5e7eb",color:"#374151",fontSize:11,fontWeight:500},overrideBadge:{display:"inline-flex",alignItems:"center",padding:"2px 8px",borderRadius:12,background:"#fef3c7",color:"#92400e",fontSize:10,fontWeight:600},editLimitsBtn:{marginLeft:"auto",padding:"3px 10px",fontSize:11,background:"#fff",color:"#3b82f6",border:"1px solid #3b82f6",borderRadius:6,cursor:"pointer",fontWeight:500},limitsEditor:{borderTop:"1px solid #e0e0e0",padding:"14px 14px 14px 34px",background:"#fff"},limitsEditorGrid:{display:"grid",gridTemplateColumns:"160px 1fr auto",gap:"8px 12px",alignItems:"center",marginBottom:10},limitsEditorLabel:{fontSize:12,fontWeight:500,color:"#555"},limitsEditorInput:{padding:"5px 8px",borderRadius:5,border:"1px solid #ccc",fontSize:12,background:"#fff",color:"#333",fontFamily:"inherit",width:"100%",boxSizing:"border-box"},limitsEditorInputDisabled:{background:"#f3f4f6",color:"#9ca3af",fontStyle:"italic"},limitsEditorUnsetBtn:{width:24,height:24,borderRadius:4,border:"1px solid #d1d5db",background:"#fff",color:"#6b7280",fontSize:14,lineHeight:1,cursor:"pointer",display:"inline-flex",alignItems:"center",justifyContent:"center"},limitsEditorUnsetBtnActive:{background:"#fee2e2",color:"#991b1b",border:"1px solid #fca5a5"},limitsEditorAdvancedToggle:{fontSize:11,color:"#3b82f6",cursor:"pointer",userSelect:"none",marginTop:6,marginBottom:10,display:"inline-block"},limitsEditorActions:{display:"flex",gap:8,justifyContent:"flex-end",marginTop:10},limitsEditorResult:{marginTop:10,padding:"8px 10px",borderRadius:6,fontSize:11,lineHeight:1.5},limitsEditorResultLive:{background:"#d1fae5",color:"#065f46"},limitsEditorResultRecreated:{background:"#fef3c7",color:"#92400e"},limitsEditorResultError:{background:"#fee2e2",color:"#991b1b"},limitsEditorWarning:{marginTop:4,fontSize:10,opacity:.85,fontStyle:"italic"},empty:{textAlign:"center",padding:"30px 16px",color:"#999",fontSize:13},fieldRow:{display:"flex",alignItems:"center",gap:12,marginBottom:10},label:{fontSize:13,fontWeight:500,color:"#555",width:160,flexShrink:0},select:{padding:"6px 10px",borderRadius:6,border:"1px solid #ccc",fontSize:13,background:"#fff",color:"#333"},hint:{fontSize:11,color:"#aaa",marginLeft:8},pruneResult:{fontSize:12,color:"#10b981",marginTop:6}},a={running:"#10b981",stopped:"#f59e0b",missing:"#94a3b8","no-runtime":"#ef4444"},o={running:"Running",stopped:"Stopped",missing:"Not created","no-runtime":"No runtime"};function l({label:e,value:t,options:n,onChange:a,hint:o}){return r.createElement("div",{style:i.fieldRow},r.createElement("span",{style:i.label},e),r.createElement("select",{style:i.select,value:t,onChange:e=>a(e.target.value)},n.map(e=>r.createElement("option",{key:e.value,value:e.value},e.label))),o&&r.createElement("span",{style:i.hint},o))}function s({label:e,value:t,onChange:n,hint:a}){return r.createElement("div",{style:i.fieldRow},r.createElement("span",{style:i.label},e),r.createElement("label",{style:{display:"inline-flex",alignItems:"center",cursor:"pointer",gap:8}},r.createElement("input",{type:"checkbox",checked:t,onChange:e=>n(e.target.checked),style:{width:16,height:16,cursor:"pointer"}}),r.createElement("span",{style:{fontSize:13,color:"#555"}},t?"Enabled":"Disabled")),a&&r.createElement("span",{style:i.hint},a))}function c(e){return e&&e.startsWith("sk-")?e.slice(3):e}const d=[{key:"cpus",label:"CPU (cores)",type:"number",step:"0.1",min:"0.1",placeholder:"e.g. 1.5",primary:!0},{key:"memory",label:"Memory",type:"text",placeholder:"e.g. 512m, 2g",primary:!0},{key:"memorySwap",label:"Memory + swap",type:"text",placeholder:"= memory to disable swap",primary:!0},{key:"pidsLimit",label:"Max processes",type:"number",step:"1",min:"1",placeholder:"e.g. 200",primary:!0},{key:"cpuShares",label:"CPU shares (weight)",type:"number",step:"1",min:"2",placeholder:"default 1024",primary:!1},{key:"cpusetCpus",label:"Pin to CPUs",type:"text",placeholder:'e.g. "0,1" or "1-3"',primary:!1},{key:"memoryReservation",label:"Memory reservation",type:"text",placeholder:"soft floor, e.g. 256m",primary:!1},{key:"oomScoreAdj",label:"OOM score adjust",type:"number",step:"1",min:"-1000",max:"1000",placeholder:"-1000 to 1000",primary:!1}];function m({containerName:e,effective:t,initialOverride:n,onApply:a,onClose:o}){const l=()=>{const e={};for(const n of d)t&&void 0!==t[n.key]&&null!==t[n.key]?e[n.key]=String(t[n.key]):e[n.key]="";return e},[s,c]=(0,r.useState)(l),[m,u]=(0,r.useState)(()=>!!n&&d.some(e=>!e.primary&&void 0!==n[e.key])),[p,f]=(0,r.useState)(!1),[g,y]=(0,r.useState)(null),b=t=>{const n=s[t.key],a=null===n;return r.createElement(r.Fragment,{key:t.key},r.createElement("label",{style:i.limitsEditorLabel,htmlFor:`lim-${e}-${t.key}`},t.label),r.createElement("input",{id:`lim-${e}-${t.key}`,type:a?"text":t.type,value:a?"":n,step:t.step,min:t.min,max:t.max,placeholder:a?"(unset — remove limit)":t.placeholder,disabled:a,onChange:e=>{return n=t.key,r=e.target.value,void c(e=>({...e,[n]:r}));var n,r},style:{...i.limitsEditorInput,...a?i.limitsEditorInputDisabled:{}}}),r.createElement("button",{type:"button",onClick:()=>{return e=t.key,void c(t=>({...t,[e]:null===t[e]?"":null}));var e},title:a?"Click to set a value again":"Click to explicitly unset (remove this limit)",style:{...i.limitsEditorUnsetBtn,...a?i.limitsEditorUnsetBtnActive:{}}},a?"↺":"×"))},h=d.filter(e=>e.primary),v=d.filter(e=>!e.primary);return r.createElement("div",{style:i.limitsEditor},r.createElement("div",{style:i.limitsEditorGrid},h.map(b)),r.createElement("span",{onClick:()=>u(!m),style:i.limitsEditorAdvancedToggle},m?"▾":"▸"," Advanced (",v.length," more fields)"),m&&r.createElement("div",{style:i.limitsEditorGrid},v.map(b)),r.createElement("div",{style:i.limitsEditorActions},r.createElement("button",{type:"button",onClick:o,style:{...i.btn,padding:"6px 12px",fontSize:12,background:"#fff",color:"#6b7280",border:"1px solid #d1d5db"}},"Close"),r.createElement("button",{type:"button",onClick:()=>{c(l()),y(null)},style:{...i.btn,padding:"6px 12px",fontSize:12,background:"#fff",color:"#6b7280",border:"1px solid #d1d5db"}},"Reset"),r.createElement("button",{type:"button",onClick:async()=>{f(!0),y(null);try{const e=function(e){const t={};for(const n of d){const r=e[n.key];if(null!==r){if(void 0!==r&&""!==r)if("number"===n.type){const e=Number(r);if(!Number.isFinite(e))continue;t[n.key]=e}else t[n.key]=r}else t[n.key]=null}return t}(s),t=await a(e);y(t)}catch(e){y({error:e.message||String(e)})}f(!1)},disabled:p,style:{...i.btn,...i.btnPrimary,padding:"6px 14px",fontSize:12,...p?i.btnDisabled:{}}},p?"Applying...":"Apply")),g&&r.createElement("div",{style:{...i.limitsEditorResult,...g.error?i.limitsEditorResultError:"recreated"===g.method?i.limitsEditorResultRecreated:i.limitsEditorResultLive}},g.error?r.createElement(r.Fragment,null,r.createElement("strong",null,"Error:")," ",g.error):r.createElement(r.Fragment,null,r.createElement("strong",null,"live"===g.method?"Applied live (no restart)":"Container recreated"),g.warnings&&g.warnings.length>0&&r.createElement("div",{style:i.limitsEditorWarning},g.warnings.map((e,t)=>r.createElement("div",{key:t},"⚠ ",e))))))}function u({configuration:e,save:t}){const n=e||{},[u,p]=(0,r.useState)(n.runtime||"auto"),[f,g]=(0,r.useState)(n.pruneSchedule||"weekly"),[y,b]=(0,r.useState)(n.updateCheckInterval||"24h"),[h,v]=(0,r.useState)(!1!==n.backgroundUpdateChecks),[k,E]=(0,r.useState)(n.containerOverrides||{}),[x,S]=(0,r.useState)(null),[w,C]=(0,r.useState)([]),[R,I]=(0,r.useState)({}),[$,z]=(0,r.useState)(new Set),[T,P]=(0,r.useState)(!0),[A,B]=(0,r.useState)(""),[j,U]=(0,r.useState)(!1),[W,O]=(0,r.useState)(null),D=(0,r.useCallback)(async()=>{try{const[e,t]=await Promise.all([fetch("/plugins/signalk-container/api/runtime"),fetch("/plugins/signalk-container/api/containers")]);e.ok?S(await e.json()):S(null);let n=[];if(t.ok?(n=await t.json(),C(n)):C([]),n.length>0){const e={};await Promise.all(n.map(async t=>{const n=c(t.name);try{const t=await fetch(`/plugins/signalk-container/api/containers/${encodeURIComponent(n)}/resources`);if(t.ok){const r=await t.json();e[n]=r.effective||{}}}catch{}})),I(e)}}catch{S(null),C([])}P(!1)},[]);(0,r.useEffect)(()=>{D();const e=setInterval(D,5e3);return()=>clearInterval(e)},[D]);const N=e=>{const t=c(e);z(e=>{const n=new Set(e);return n.has(t)?n.delete(t):n.add(t),n})};return r.createElement("div",{style:i.root},r.createElement("div",{style:i.sectionTitle},"Runtime"),T?r.createElement("div",{style:i.empty},"Detecting container runtime..."):x?r.createElement("div",{style:i.runtimeCard},r.createElement("div",{style:{...i.runtimeIcon,background:"podman"===x.runtime?"#892ca0":"#2496ed",color:"#fff"}},"podman"===x.runtime?"P":"D"),r.createElement("div",{style:i.runtimeInfo},r.createElement("div",{style:i.runtimeName},x.runtime.charAt(0).toUpperCase()+x.runtime.slice(1),x.isPodmanDockerShim?" (via docker shim)":""),r.createElement("div",{style:i.runtimeVersion},"Version ",x.version)),r.createElement("div",{style:{...i.stateIndicator,background:"#10b981"},title:"Runtime available"})):r.createElement("div",{style:i.runtimeCard},r.createElement("div",{style:{...i.runtimeIcon,background:"#fef2f2",color:"#ef4444"}},"!"),r.createElement("div",{style:i.runtimeInfo},r.createElement("div",{style:i.runtimeName},"No container runtime found"),r.createElement("div",{style:i.runtimeVersion},"Install Podman: sudo apt install podman"))),r.createElement("div",{style:i.sectionTitle},"Settings"),r.createElement(l,{label:"Preferred runtime",value:u,onChange:p,options:[{value:"auto",label:"Auto-detect (Podman preferred)"},{value:"podman",label:"Podman"},{value:"docker",label:"Docker"}]}),r.createElement(l,{label:"Auto-prune images",value:f,onChange:g,options:[{value:"off",label:"Off"},{value:"weekly",label:"Weekly"},{value:"monthly",label:"Monthly"}]}),r.createElement(l,{label:"Update check interval",value:y,onChange:b,options:[{value:"1h",label:"Every hour"},{value:"6h",label:"Every 6 hours"},{value:"12h",label:"Every 12 hours"},{value:"24h",label:"Daily (recommended)"},{value:"48h",label:"Every 2 days"},{value:"168h",label:"Weekly"}],hint:"How often to check for new container images"}),r.createElement(s,{label:"Background update checks",value:h,onChange:v,hint:"Disable on metered connections; manual check still works"}),r.createElement("div",{style:i.sectionTitle},"Managed Containers"),0===w.length?r.createElement("div",{style:i.empty},T?"Loading...":"No managed containers. Other plugins will create them."):w.map(e=>{const t=c(e.name),n=R[t]||{},l=k[t]&&Object.keys(k[t]).length>0,s=$.has(t),u=d.map(e=>function(e,t){if(null==t||""===t)return null;switch(e){case"cpus":return`${t} CPU`;case"memory":return`${t}`;case"memorySwap":return`swap: ${t}`;case"memoryReservation":return`reserve: ${t}`;case"pidsLimit":return`${t} PIDs`;case"cpuShares":return`shares: ${t}`;case"cpusetCpus":return`cpus: ${t}`;case"oomScoreAdj":return`oom: ${t}`;default:return`${e}: ${t}`}}(e.key,n[e.key])).filter(Boolean);return r.createElement("div",{key:e.name,style:i.containerCard},r.createElement("div",{style:i.containerItemFlat},r.createElement("div",{style:{...i.stateIndicator,background:a[e.state]||"#94a3b8"},title:o[e.state]||e.state}),r.createElement("div",{style:i.containerInfo},r.createElement("div",{style:i.containerName},e.name),r.createElement("div",{style:i.containerMeta},e.image," · ",o[e.state]||e.state,e.ports&&e.ports.length>0&&e.ports[0]?` · ${e.ports.join(", ")}`:"")),r.createElement("div",{style:i.containerActions},"stopped"===e.state&&r.createElement("button",{style:{...i.btn,...i.btnPrimary,padding:"6px 12px",fontSize:12},onClick:()=>(async e=>{B(`Starting ${e}...`),U(!1);try{const t=await fetch(`/plugins/signalk-container/api/containers/${encodeURIComponent(e)}/start`,{method:"POST"});if(t.ok)B(`${e} started.`),D();else{const e=await t.json().catch(()=>({error:t.statusText}));B(`Failed: ${e.error}`),U(!0)}}catch(e){B(`Error: ${e.message}`),U(!0)}})(e.name)},"Start"),"running"===e.state&&r.createElement("button",{style:{...i.btn,...i.btnWarning},onClick:()=>(async e=>{B(`Stopping ${e}...`),U(!1);try{const t=await fetch(`/plugins/signalk-container/api/containers/${encodeURIComponent(e)}/stop`,{method:"POST"});if(t.ok)B(`${e} stopped.`),D();else{const e=await t.json().catch(()=>({error:t.statusText}));B(`Failed: ${e.error}`),U(!0)}}catch(e){B(`Error: ${e.message}`),U(!0)}})(e.name)},"Stop"),r.createElement("button",{style:{...i.btn,...i.btnDanger},onClick:()=>(async(e,t)=>{if("running"!==t||window.confirm(`${e} is running. Stop and remove it?`)){B(`Removing ${e}...`),U(!1);try{const t=await fetch(`/plugins/signalk-container/api/containers/${encodeURIComponent(e)}/remove`,{method:"POST"});if(t.ok)B(`${e} removed.`),D();else{const e=await t.json().catch(()=>({error:t.statusText}));B(`Failed: ${e.error}`),U(!0)}}catch(e){B(`Error: ${e.message}`),U(!0)}}})(e.name,e.state)},"Remove"))),r.createElement("div",{style:i.limitsRow},0===u.length?r.createElement("span",{style:{color:"#9ca3af",fontStyle:"italic"}},"No resource limits set"):u.map((e,t)=>r.createElement("span",{key:t,style:i.limitBadge},e)),l&&r.createElement("span",{style:i.overrideBadge,title:"You have a user override configured for this container"},"Override active"),"running"===e.state&&r.createElement("button",{type:"button",style:i.editLimitsBtn,onClick:()=>N(e.name)},s?"Collapse ▾":"Edit Limits ▸")),s&&r.createElement(m,{containerName:t,effective:n,initialOverride:k[t],onApply:e=>(async(e,t)=>{const n=await fetch(`/plugins/signalk-container/api/containers/${encodeURIComponent(e)}/resources`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));return n.ok?(r.effective&&I(t=>({...t,[e]:r.effective})),E(n=>({...n,[e]:t})),{method:r.method,warnings:r.warnings}):{error:r.error||`${n.status} ${n.statusText}`}})(t,e),onClose:()=>N(e.name)}))}),r.createElement("div",{style:i.sectionTitle},"Maintenance"),r.createElement("button",{style:{...i.btn,...i.btnSuccess},onClick:async()=>{B("Pruning dangling images..."),U(!1),O(null);try{const e=await fetch("/plugins/signalk-container/api/prune",{method:"POST"});if(e.ok){const t=await e.json().catch(()=>({error:e.statusText}));O(t),B(`Pruned ${t.imagesRemoved} image(s), reclaimed ${t.spaceReclaimed}.`)}else{const t=await e.json().catch(()=>({error:e.statusText}));B(`Prune failed: ${t.error}`),U(!0)}}catch(e){B(`Error: ${e.message}`),U(!0)}}},"Prune Dangling Images"),A&&r.createElement("div",{style:{...i.status,color:j?"#ef4444":"#10b981"}},A),r.createElement("div",{style:{...i.sectionTitle,marginTop:28}}," "),r.createElement("button",{style:{...i.btn,...i.btnSave},onClick:()=>{t({...n,runtime:u,pruneSchedule:f,maxConcurrentJobs:n.maxConcurrentJobs||2,updateCheckInterval:y,backgroundUpdateChecks:h,containerOverrides:k}),B("Saved! Plugin will restart."),U(!1)}},"Save Configuration"))}}}]);
|