Sync build v0.9.121

Automated sync from private repository.
Commit: f3ea20a95cd38e0965a7c01f70d6d14299b450c7
This commit is contained in:
GitHub Actions Bot
2026-01-05 03:42:09 +00:00
parent 70a943d519
commit 67f9e0e708
66 changed files with 74 additions and 374 deletions
+2 -25
View File
@@ -143,9 +143,7 @@ The installer will:
/opt/pymc_console/ ← Installed console files
```
Once complete, access your dashboard at `https://<your-pi-ip>:8000`
> **Note:** The installer automatically generates a self-signed SSL certificate and enables HTTPS. Your browser will show a security warning — click **Advanced** → **Proceed** to accept the self-signed certificate. This is a one-time action per browser.
Once complete, access your dashboard at `http://<your-pi-ip>:8000`
## Management Menu
@@ -274,33 +272,12 @@ cat /etc/pymc_repeater/config.yaml
### Dashboard not loading
1. Verify service is running: `sudo systemctl status pymc-repeater`
2. Check if port 8000 is accessible: `curl -k https://localhost:8000/api/stats`
3. If using HTTP instead of HTTPS, the connection will be refused (HTTPS is required)
2. Check if port 8000 is accessible: `curl http://localhost:8000/api/stats`
### "Radio presets file not found" warning
This warning during installation is non-fatal. The installer will continue and you can configure radio settings manually. The presets are fetched from an API; if the API is unavailable, common presets are offered as fallback options.
### Browser shows "Your connection is not private"
This is expected behavior for the self-signed SSL certificate. Click **Advanced****Proceed to [IP] (unsafe)** to access the dashboard. This is safe for local network use.
### Using custom SSL certificates
To use your own certificates (e.g., from Let's Encrypt), update the paths in `/etc/pymc_repeater/config.yaml`:
```yaml
web:
ssl_certificate: /path/to/your/cert.pem
ssl_private_key: /path/to/your/key.pem
```
Then restart the service: `sudo systemctl restart pymc-repeater`
The auto-generated certificates are stored at:
- Certificate: `/etc/pymc_repeater/ssl/cert.pem`
- Private key: `/etc/pymc_repeater/ssl/key.pem`
## Uninstalling
```bash
+1 -1
View File
@@ -1 +1 @@
0.9.133
0.9.121
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{c as e,j as t,X as o}from"./index-DoxF6EWj.js";import{b as n,g as s,c as d}from"./recharts-bmHCCfxU.js";import{T as r}from"./triangle-alert-CakoZNuk.js";const a=e("trash-2",[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]]),l=n.memo(function({isOpen:e,title:a="Confirm",message:l,confirmLabel:c="Confirm",cancelLabel:i="Cancel",variant:m="default",onConfirm:b,onCancel:x}){if(n.useEffect(()=>{if(!e)return;const t=e=>{"Escape"===e.key&&x()};window.addEventListener("keydown",t);const o=document.body.style.overflow,n=document.body.style.position,s=document.body.style.width,d=document.body.style.top,r=window.scrollY;return document.body.style.overflow="hidden",document.body.style.position="fixed",document.body.style.width="100%",document.body.style.top=`-${r}px`,()=>{window.removeEventListener("keydown",t),document.body.style.overflow=o,document.body.style.position=n,document.body.style.width=s,document.body.style.top=d,window.scrollTo(0,r)}},[e,x]),!e)return null;const u={danger:{icon:"text-accent-danger",button:"bg-accent-danger hover:bg-red-600"},warning:{icon:"text-accent-secondary",button:"bg-accent-secondary hover:bg-yellow-600 text-bg-body"},default:{icon:"text-accent-primary",button:"bg-accent-primary hover:bg-violet-500 text-bg-body"}}[m];return s.createPortal(t.jsx("div",{className:"fixed inset-0 bg-black/40 backdrop-blur-md z-[10010] flex items-end sm:items-center justify-center",onClick:x,role:"dialog","aria-modal":"true","aria-labelledby":"confirm-modal-title",children:t.jsxs("div",{className:d("glass-card-elevated w-full max-w-sm","sm:mx-4 sm:rounded-xl","rounded-t-2xl rounded-b-none sm:rounded-b-xl","pb-safe"),onClick:e=>e.stopPropagation(),children:[t.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-border-subtle",children:[t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsx("div",{className:d("p-2 rounded-lg bg-bg-subtle",u.icon),children:t.jsx(r,{className:"w-5 h-5"})}),t.jsx("h3",{id:"confirm-modal-title",className:"text-base font-semibold text-text-primary",children:a})]}),t.jsx("button",{onClick:x,className:"p-2 rounded-lg text-text-muted hover:text-text-primary hover:bg-bg-subtle transition-colors",children:t.jsx(o,{className:"w-5 h-5"})})]}),t.jsx("div",{className:"p-4",children:t.jsx("p",{className:"text-sm text-text-secondary",children:l})}),t.jsxs("div",{className:"flex gap-3 p-4 pt-0",children:[t.jsx("button",{onClick:x,className:"flex-1 px-4 py-2.5 rounded-lg text-sm font-medium text-text-secondary bg-bg-subtle hover:bg-bg-elevated border border-border-subtle transition-colors",children:i}),t.jsx("button",{onClick:b,className:d("flex-1 px-4 py-2.5 rounded-lg text-sm font-medium text-text-primary transition-colors",u.button),children:c})]})]})}),document.body)});export{l as C,a as T};
import{c as e,j as t,X as o}from"./index-BA-LoFOy.js";import{b as n,g as s,c as d}from"./recharts-bmHCCfxU.js";import{T as r}from"./triangle-alert-SBaK73du.js";const a=e("trash-2",[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]]),l=n.memo(function({isOpen:e,title:a="Confirm",message:l,confirmLabel:c="Confirm",cancelLabel:i="Cancel",variant:m="default",onConfirm:b,onCancel:x}){if(n.useEffect(()=>{if(!e)return;const t=e=>{"Escape"===e.key&&x()};window.addEventListener("keydown",t);const o=document.body.style.overflow,n=document.body.style.position,s=document.body.style.width,d=document.body.style.top,r=window.scrollY;return document.body.style.overflow="hidden",document.body.style.position="fixed",document.body.style.width="100%",document.body.style.top=`-${r}px`,()=>{window.removeEventListener("keydown",t),document.body.style.overflow=o,document.body.style.position=n,document.body.style.width=s,document.body.style.top=d,window.scrollTo(0,r)}},[e,x]),!e)return null;const u={danger:{icon:"text-accent-danger",button:"bg-accent-danger hover:bg-red-600"},warning:{icon:"text-accent-secondary",button:"bg-accent-secondary hover:bg-yellow-600 text-bg-body"},default:{icon:"text-accent-primary",button:"bg-accent-primary hover:bg-violet-500 text-bg-body"}}[m];return s.createPortal(t.jsx("div",{className:"fixed inset-0 bg-black/40 backdrop-blur-md z-[10010] flex items-end sm:items-center justify-center",onClick:x,role:"dialog","aria-modal":"true","aria-labelledby":"confirm-modal-title",children:t.jsxs("div",{className:d("glass-card-elevated w-full max-w-sm","sm:mx-4 sm:rounded-xl","rounded-t-2xl rounded-b-none sm:rounded-b-xl","pb-safe"),onClick:e=>e.stopPropagation(),children:[t.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-border-subtle",children:[t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsx("div",{className:d("p-2 rounded-lg bg-bg-subtle",u.icon),children:t.jsx(r,{className:"w-5 h-5"})}),t.jsx("h3",{id:"confirm-modal-title",className:"text-base font-semibold text-text-primary",children:a})]}),t.jsx("button",{onClick:x,className:"p-2 rounded-lg text-text-muted hover:text-text-primary hover:bg-bg-subtle transition-colors",children:t.jsx(o,{className:"w-5 h-5"})})]}),t.jsx("div",{className:"p-4",children:t.jsx("p",{className:"text-sm text-text-secondary",children:l})}),t.jsxs("div",{className:"flex gap-3 p-4 pt-0",children:[t.jsx("button",{onClick:x,className:"flex-1 px-4 py-2.5 rounded-lg text-sm font-medium text-text-secondary bg-bg-subtle hover:bg-bg-elevated border border-border-subtle transition-colors",children:i}),t.jsx("button",{onClick:b,className:d("flex-1 px-4 py-2.5 rounded-lg text-sm font-medium text-text-primary transition-colors",u.button),children:c})]})]})}),document.body)});export{l as C,a as T};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{j as e}from"./index-DoxF6EWj.js";import{b as o,c as l}from"./recharts-bmHCCfxU.js";const t={hero:{mobile:12},panel:{mobile:12,md:6},feature:{mobile:12,md:6,lg:4},standard:{mobile:12,sm:6,lg:4,xl:3},compact:{mobile:6,lg:3},widget:{mobile:6,sm:4,md:3,lg:2},auto:{mobile:12}},n={hero:"bento-row-hero",panel:"bento-row-panel",feature:"bento-row-feature",standard:"bento-row-standard",compact:"bento-row-compact",widget:"bento-row-widget",auto:"bento-row-auto"};function a({template:a,children:s,className:r,gap:c}){const i=t[a],d=n[a],b="none"===c?"gap-0":"sm"===c?"bento-gap-sm":"lg"===c?"bento-gap-lg":"bento-gap",p=o.Children.map(s,l=>o.isValidElement(l)?l.type===m?l:e.jsx(m,{span:i.mobile,sm:i.sm,md:i.md,lg:i.lg,xl:i.xl,children:l}):l);return e.jsx("div",{className:l("bento-row",d,b,r),children:p})}function s(e,o=""){const l=o?`${o}:`:"";return"full"===e?`${l}col-span-full`:"auto"===e?`${l}col-auto`:1.5===e?`${l}bento-col-1-5`:`${l}bento-col-${e}`}function m({children:o,span:t,sm:n,md:a,lg:m,xl:r,className:c}){const i=l("bento-cell",s(t),n&&s(n,"sm"),a&&s(a,"md"),m&&s(m,"lg"),r&&s(r,"xl"),c);return e.jsx("div",{className:i,children:o})}export{m as C,a as R};
import{j as e}from"./index-BA-LoFOy.js";import{b as o,c as l}from"./recharts-bmHCCfxU.js";const t={hero:{mobile:12},panel:{mobile:12,md:6},feature:{mobile:12,md:6,lg:4},standard:{mobile:12,sm:6,lg:4,xl:3},compact:{mobile:6,lg:3},widget:{mobile:6,sm:4,md:3,lg:2},auto:{mobile:12}},n={hero:"bento-row-hero",panel:"bento-row-panel",feature:"bento-row-feature",standard:"bento-row-standard",compact:"bento-row-compact",widget:"bento-row-widget",auto:"bento-row-auto"};function a({template:a,children:s,className:r,gap:c}){const i=t[a],d=n[a],b="none"===c?"gap-0":"sm"===c?"bento-gap-sm":"lg"===c?"bento-gap-lg":"bento-gap",p=o.Children.map(s,l=>o.isValidElement(l)?l.type===m?l:e.jsx(m,{span:i.mobile,sm:i.sm,md:i.md,lg:i.lg,xl:i.xl,children:l}):l);return e.jsx("div",{className:l("bento-row",d,b,r),children:p})}function s(e,o=""){const l=o?`${o}:`:"";return"full"===e?`${l}col-span-full`:"auto"===e?`${l}col-auto`:1.5===e?`${l}bento-col-1-5`:`${l}bento-col-${e}`}function m({children:o,span:t,sm:n,md:a,lg:m,xl:r,className:c}){const i=l("bento-cell",s(t),n&&s(n,"sm"),a&&s(a,"md"),m&&s(m,"lg"),r&&s(r,"xl"),c);return e.jsx("div",{className:i,children:o})}export{m as C,a as R};
@@ -1 +1 @@
import{j as e}from"./index-DoxF6EWj.js";import{b as t,c as o}from"./recharts-bmHCCfxU.js";import{a as n,C as s}from"./triangle-alert-CakoZNuk.js";function a({hash:a,prefixLength:r=8,suffixLength:i=6,full:c=!1,className:l,size:p="md"}){const[u,d]=t.useState(!1),[x,h]=t.useState(!1),m=t.useRef(null),w=t.useCallback(e=>{e.preventDefault(),e.stopPropagation(),m.current&&clearTimeout(m.current),function(e){if("undefined"!=typeof window&&(window.isSecureContext||"https:"===window.location.protocol||"localhost"===window.location.hostname)&&navigator.clipboard&&"function"==typeof navigator.clipboard.writeText)return navigator.clipboard.writeText(e).catch(()=>{}),!0;try{const t=window.scrollX,o=window.scrollY,n=document.createElement("textarea");n.value=e,n.style.position="fixed",n.style.left="-9999px",n.style.top="-9999px",n.style.opacity="0",n.style.pointerEvents="none",n.setAttribute("readonly",""),document.body.appendChild(n),n.focus({preventScroll:!0}),n.select(),n.setSelectionRange(0,e.length);const s=document.execCommand("copy");return document.body.removeChild(n),window.scrollTo(t,o),s}catch(t){return!1}}(a),d(!0),m.current=setTimeout(()=>d(!1),2e3)},[a]),y=c||a.length<=r+i+3?a:`${a.slice(0,r)}${a.slice(-i)}`,f={xs:"text-[9px] px-1 py-0.5 gap-0.5",sm:"text-[10px] px-1.5 py-0.5 gap-1",md:"text-xs px-2 py-1 gap-1.5"}[p];return e.jsxs("button",{type:"button",onClick:w,onMouseEnter:()=>h(!0),onMouseLeave:()=>h(!1),className:o("inline-flex items-center font-mono rounded transition-all duration-200","bg-white/[0.04] hover:bg-white/[0.08]","border border-white/[0.06] hover:border-white/[0.12]","cursor-pointer select-none",f,l),title:`Click to copy: ${a}`,"aria-label":`Copy hash ${a}`,children:[e.jsx("span",{className:o("transition-colors duration-200",u?"text-accent-success":"text-text-secondary"),children:y}),e.jsx("span",{className:o("flex items-center justify-center transition-all duration-200","xs"===p?"w-2.5 h-2.5":"sm"===p?"w-3 h-3":"w-3.5 h-3.5"),children:u?e.jsx(n,{className:o("text-accent-success","xs"===p?"w-2 h-2":"sm"===p?"w-2.5 h-2.5":"w-3 h-3")}):e.jsx(s,{className:o("transition-opacity duration-200",x?"opacity-70":"opacity-40","xs"===p?"w-2 h-2":"sm"===p?"w-2.5 h-2.5":"w-3 h-3")})})]})}export{a as H};
import{j as e}from"./index-BA-LoFOy.js";import{b as t,c as o}from"./recharts-bmHCCfxU.js";import{a as n,C as s}from"./triangle-alert-SBaK73du.js";function a({hash:a,prefixLength:r=8,suffixLength:i=6,full:c=!1,className:l,size:p="md"}){const[u,d]=t.useState(!1),[x,h]=t.useState(!1),m=t.useRef(null),w=t.useCallback(e=>{e.preventDefault(),e.stopPropagation(),m.current&&clearTimeout(m.current),function(e){if("undefined"!=typeof window&&(window.isSecureContext||"https:"===window.location.protocol||"localhost"===window.location.hostname)&&navigator.clipboard&&"function"==typeof navigator.clipboard.writeText)return navigator.clipboard.writeText(e).catch(()=>{}),!0;try{const t=window.scrollX,o=window.scrollY,n=document.createElement("textarea");n.value=e,n.style.position="fixed",n.style.left="-9999px",n.style.top="-9999px",n.style.opacity="0",n.style.pointerEvents="none",n.setAttribute("readonly",""),document.body.appendChild(n),n.focus({preventScroll:!0}),n.select(),n.setSelectionRange(0,e.length);const s=document.execCommand("copy");return document.body.removeChild(n),window.scrollTo(t,o),s}catch(t){return!1}}(a),d(!0),m.current=setTimeout(()=>d(!1),2e3)},[a]),y=c||a.length<=r+i+3?a:`${a.slice(0,r)}${a.slice(-i)}`,f={xs:"text-[9px] px-1 py-0.5 gap-0.5",sm:"text-[10px] px-1.5 py-0.5 gap-1",md:"text-xs px-2 py-1 gap-1.5"}[p];return e.jsxs("button",{type:"button",onClick:w,onMouseEnter:()=>h(!0),onMouseLeave:()=>h(!1),className:o("inline-flex items-center font-mono rounded transition-all duration-200","bg-white/[0.04] hover:bg-white/[0.08]","border border-white/[0.06] hover:border-white/[0.12]","cursor-pointer select-none",f,l),title:`Click to copy: ${a}`,"aria-label":`Copy hash ${a}`,children:[e.jsx("span",{className:o("transition-colors duration-200",u?"text-accent-success":"text-text-secondary"),children:y}),e.jsx("span",{className:o("flex items-center justify-center transition-all duration-200","xs"===p?"w-2.5 h-2.5":"sm"===p?"w-3 h-3":"w-3.5 h-3.5"),children:u?e.jsx(n,{className:o("text-accent-success","xs"===p?"w-2 h-2":"sm"===p?"w-2.5 h-2.5":"w-3 h-3")}):e.jsx(s,{className:o("transition-opacity duration-200",x?"opacity-70":"opacity-40","xs"===p?"w-2 h-2":"sm"===p?"w-2.5 h-2.5":"w-3 h-3")})})]})}export{a as H};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{c as e,j as s,a0 as a,a1 as t,e as l,a2 as c,a3 as n,a4 as r,p as i,a5 as o,a6 as d}from"./index-DoxF6EWj.js";import{c as m,b as x}from"./recharts-bmHCCfxU.js";import{u as p}from"./usePolling-DMtQdE_y.js";import{P as h,a as u,C as j,b as f}from"./PageLayout-BAvbXbcx.js";import{C as g}from"./circle-gFGO101j.js";import{R as y}from"./refresh-cw-D1N2KIKK.js";import{I as N}from"./info-DDL2WS1Q.js";import{L as v}from"./loader-circle-H5ql_XUe.js";import"./maplibre-gl-CFO9X1Ek.js";const b=e("bug",[["path",{d:"M12 20v-9",key:"1qisl0"}],["path",{d:"M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z",key:"uouzyp"}],["path",{d:"M14.12 3.88 16 2",key:"qol33r"}],["path",{d:"M21 21a4 4 0 0 0-3.81-4",key:"1b0z45"}],["path",{d:"M21 5a4 4 0 0 1-3.55 3.97",key:"5cxbf6"}],["path",{d:"M22 13h-4",key:"1jl80f"}],["path",{d:"M3 21a4 4 0 0 1 3.81-4",key:"1fjd4g"}],["path",{d:"M3 5a4 4 0 0 0 3.55 3.97",key:"1d7oge"}],["path",{d:"M6 13H2",key:"82j7cp"}],["path",{d:"m8 2 1.88 1.88",key:"fmnt4t"}],["path",{d:"M9 7.13V6a3 3 0 1 1 6 0v1.13",key:"1vgav8"}]]);function w({className:e,style:a}){return s.jsx("div",{className:m("animate-pulse bg-white/[0.06] rounded",e),style:a})}function k(){return s.jsx("div",{className:"p-3 rounded-lg border border-border-subtle bg-bg-subtle",children:s.jsxs("div",{className:"flex items-start gap-3",children:[s.jsx(w,{className:"w-14 h-6 rounded shrink-0"}),s.jsxs("div",{className:"flex-1 min-w-0 space-y-2",children:[s.jsx(w,{className:"h-4 w-full"}),s.jsx(w,{className:"h-4 w-3/4"}),s.jsx(w,{className:"h-3 w-32 mt-1"})]})]})})}function M({count:e=8}){return s.jsx("div",{className:"space-y-2",children:Array.from({length:e}).map((e,a)=>s.jsx(k,{},a))})}const C=x.memo(function({log:e}){const a=d(e.level);return s.jsx("div",{className:"p-3 rounded-2xl bg-white/[0.02] hover:bg-white/[0.04] transition-colors",children:s.jsxs("div",{className:"flex items-baseline gap-3",children:[s.jsx("span",{className:m("type-data-sm w-14 shrink-0",a),children:e.level}),s.jsxs("div",{className:"flex-1 min-w-0",children:[s.jsx("p",{className:"type-data-sm text-text-primary break-words whitespace-pre-wrap",children:e.message}),s.jsx("p",{className:"type-data-xs text-text-muted mt-1",children:new Date(e.timestamp).toLocaleString()})]})]})})});function F(){const e=a(),[t,l]=x.useState("INFO"),[c,n]=x.useState(!1),[r,i]=x.useState(null);x.useEffect(()=>{if(e.length>0){const s=e.some(e=>"DEBUG"===e.level);l(s?"DEBUG":"INFO")}},[e]);const d=x.useCallback(async e=>{if(e!==t&&!c){n(!0),i(null);try{const s=await o(e);s.success&&s.data?(l(e),i(s.data.message),setTimeout(()=>i(null),5e3)):(i(s.error||"Failed to change log level"),setTimeout(()=>i(null),3e3))}catch{i("Failed to change log level"),setTimeout(()=>i(null),3e3)}finally{n(!1)}}},[t,c]);return s.jsxs("div",{className:"flex items-center gap-2",children:[r&&s.jsx("span",{className:"text-xs text-text-muted animate-pulse",children:r}),s.jsxs("div",{className:"flex bg-white/[0.02] rounded-full p-1",children:[s.jsxs("button",{onClick:()=>d("INFO"),disabled:c,className:m("h-[32px] px-4 rounded-full type-data flex items-center gap-2 transition-colors","INFO"===t?"bg-accent-primary/20 text-accent-primary":"text-text-muted hover:text-text-secondary",c&&"opacity-50 cursor-not-allowed"),children:[s.jsx(N,{className:"w-4 h-4"}),"Info"]}),s.jsxs("button",{onClick:()=>d("DEBUG"),disabled:c,className:m("h-[32px] px-4 rounded-full type-data flex items-center gap-2 transition-colors","DEBUG"===t?"bg-amber-500/20 text-amber-400":"text-text-muted hover:text-text-secondary",c&&"opacity-50 cursor-not-allowed"),children:[c?s.jsx(v,{className:"w-4 h-4 animate-spin"}):s.jsx(b,{className:"w-4 h-4"}),"Debug"]})]})]})}function L(){const e=a(),o=t(),d=l(),x=c(),N=n();return p(x,i.logs,d),s.jsxs(h,{children:[s.jsx(u,{title:"System Logs",icon:s.jsx(r,{}),controls:s.jsxs(s.Fragment,{children:[s.jsx(F,{}),d&&s.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[s.jsx(g,{className:"w-2 h-2 fill-accent-success text-accent-success animate-pulse"}),s.jsx("span",{className:"text-text-muted",children:"Live"})]}),s.jsxs("button",{onClick:()=>N(!d),className:m("h-[32px] px-4 rounded-full type-data transition-colors","flex items-center gap-2",d?"bg-accent-success/20 text-accent-success":"bg-white/[0.02] text-text-muted hover:text-text-secondary"),children:[s.jsx(y,{className:m("w-4 h-4",d&&"animate-spin")}),s.jsx("span",{className:"hidden xs:inline",children:d?"Live":"Paused"})]})]})}),s.jsxs(j,{noPadding:!0,children:[s.jsx(f,{listHeader:!0,icon:s.jsx(r,{className:"icon-sm"}),title:"Log Entries"}),s.jsx("div",{className:"space-y-2 max-h-[calc(100vh-300px)] sm:max-h-[calc(100vh-250px)] overflow-y-auto p-4",children:o&&0===e.length?s.jsx(M,{count:10}):0===e.length?s.jsx("div",{className:"text-center py-12 text-text-muted",children:"No logs available"}):e.map((e,a)=>s.jsx(C,{log:e},`${e.timestamp}-${a}`))})]})]})}export{L as default};
import{c as e,j as s,a0 as a,a1 as t,d as l,a2 as c,a3 as n,a4 as r,o as i,a5 as o,a6 as d}from"./index-BA-LoFOy.js";import{c as m,b as x}from"./recharts-bmHCCfxU.js";import{u as p}from"./usePolling-DMtQdE_y.js";import{P as h,a as u,C as j,b as f}from"./PageLayout-BVrxHani.js";import{C as g}from"./circle-XCBuG0eA.js";import{R as y}from"./refresh-cw-Di1wuK5R.js";import{I as N}from"./info-C4_AIqlt.js";import{L as v}from"./loader-circle-DJ5Po0GD.js";import"./maplibre-gl-CFO9X1Ek.js";const b=e("bug",[["path",{d:"M12 20v-9",key:"1qisl0"}],["path",{d:"M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z",key:"uouzyp"}],["path",{d:"M14.12 3.88 16 2",key:"qol33r"}],["path",{d:"M21 21a4 4 0 0 0-3.81-4",key:"1b0z45"}],["path",{d:"M21 5a4 4 0 0 1-3.55 3.97",key:"5cxbf6"}],["path",{d:"M22 13h-4",key:"1jl80f"}],["path",{d:"M3 21a4 4 0 0 1 3.81-4",key:"1fjd4g"}],["path",{d:"M3 5a4 4 0 0 0 3.55 3.97",key:"1d7oge"}],["path",{d:"M6 13H2",key:"82j7cp"}],["path",{d:"m8 2 1.88 1.88",key:"fmnt4t"}],["path",{d:"M9 7.13V6a3 3 0 1 1 6 0v1.13",key:"1vgav8"}]]);function w({className:e,style:a}){return s.jsx("div",{className:m("animate-pulse bg-white/[0.06] rounded",e),style:a})}function k(){return s.jsx("div",{className:"p-3 rounded-lg border border-border-subtle bg-bg-subtle",children:s.jsxs("div",{className:"flex items-start gap-3",children:[s.jsx(w,{className:"w-14 h-6 rounded shrink-0"}),s.jsxs("div",{className:"flex-1 min-w-0 space-y-2",children:[s.jsx(w,{className:"h-4 w-full"}),s.jsx(w,{className:"h-4 w-3/4"}),s.jsx(w,{className:"h-3 w-32 mt-1"})]})]})})}function M({count:e=8}){return s.jsx("div",{className:"space-y-2",children:Array.from({length:e}).map((e,a)=>s.jsx(k,{},a))})}const C=x.memo(function({log:e}){const a=d(e.level);return s.jsx("div",{className:"p-3 rounded-2xl bg-white/[0.02] hover:bg-white/[0.04] transition-colors",children:s.jsxs("div",{className:"flex items-baseline gap-3",children:[s.jsx("span",{className:m("type-data-sm w-14 shrink-0",a),children:e.level}),s.jsxs("div",{className:"flex-1 min-w-0",children:[s.jsx("p",{className:"type-data-sm text-text-primary break-words whitespace-pre-wrap",children:e.message}),s.jsx("p",{className:"type-data-xs text-text-muted mt-1",children:new Date(e.timestamp).toLocaleString()})]})]})})});function F(){const e=a(),[t,l]=x.useState("INFO"),[c,n]=x.useState(!1),[r,i]=x.useState(null);x.useEffect(()=>{if(e.length>0){const s=e.some(e=>"DEBUG"===e.level);l(s?"DEBUG":"INFO")}},[e]);const d=x.useCallback(async e=>{if(e!==t&&!c){n(!0),i(null);try{const s=await o(e);s.success&&s.data?(l(e),i(s.data.message),setTimeout(()=>i(null),5e3)):(i(s.error||"Failed to change log level"),setTimeout(()=>i(null),3e3))}catch{i("Failed to change log level"),setTimeout(()=>i(null),3e3)}finally{n(!1)}}},[t,c]);return s.jsxs("div",{className:"flex items-center gap-2",children:[r&&s.jsx("span",{className:"text-xs text-text-muted animate-pulse",children:r}),s.jsxs("div",{className:"flex bg-white/[0.02] rounded-full p-1",children:[s.jsxs("button",{onClick:()=>d("INFO"),disabled:c,className:m("h-[32px] px-4 rounded-full type-data flex items-center gap-2 transition-colors","INFO"===t?"bg-accent-primary/20 text-accent-primary":"text-text-muted hover:text-text-secondary",c&&"opacity-50 cursor-not-allowed"),children:[s.jsx(N,{className:"w-4 h-4"}),"Info"]}),s.jsxs("button",{onClick:()=>d("DEBUG"),disabled:c,className:m("h-[32px] px-4 rounded-full type-data flex items-center gap-2 transition-colors","DEBUG"===t?"bg-amber-500/20 text-amber-400":"text-text-muted hover:text-text-secondary",c&&"opacity-50 cursor-not-allowed"),children:[c?s.jsx(v,{className:"w-4 h-4 animate-spin"}):s.jsx(b,{className:"w-4 h-4"}),"Debug"]})]})]})}function L(){const e=a(),o=t(),d=l(),x=c(),N=n();return p(x,i.logs,d),s.jsxs(h,{children:[s.jsx(u,{title:"System Logs",icon:s.jsx(r,{}),controls:s.jsxs(s.Fragment,{children:[s.jsx(F,{}),d&&s.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[s.jsx(g,{className:"w-2 h-2 fill-accent-success text-accent-success animate-pulse"}),s.jsx("span",{className:"text-text-muted",children:"Live"})]}),s.jsxs("button",{onClick:()=>N(!d),className:m("h-[32px] px-4 rounded-full type-data transition-colors","flex items-center gap-2",d?"bg-accent-success/20 text-accent-success":"bg-white/[0.02] text-text-muted hover:text-text-secondary"),children:[s.jsx(y,{className:m("w-4 h-4",d&&"animate-spin")}),s.jsx("span",{className:"hidden xs:inline",children:d?"Live":"Paused"})]})]})}),s.jsxs(j,{noPadding:!0,children:[s.jsx(f,{listHeader:!0,icon:s.jsx(r,{className:"icon-sm"}),title:"Log Entries"}),s.jsx("div",{className:"space-y-2 max-h-[calc(100vh-300px)] sm:max-h-[calc(100vh-250px)] overflow-y-auto p-4",children:o&&0===e.length?s.jsx(M,{count:10}):0===e.length?s.jsx("div",{className:"text-center py-12 text-text-muted",children:"No logs available"}):e.map((e,a)=>s.jsx(C,{log:e},`${e.timestamp}-${a}`))})]})]})}export{L as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{j as e}from"./index-DoxF6EWj.js";import{c as s}from"./recharts-bmHCCfxU.js";function a({children:a,className:l}){return e.jsx("div",{className:s("section-gap",l),children:a})}function l({title:s,icon:a,controls:l,subtitle:c}){return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[e.jsxs("h1",{className:"type-title text-text-primary flex items-center gap-3",children:[a&&e.jsx("span",{className:"w-6 h-6 text-accent-primary flex-shrink-0",children:a}),s]}),l&&e.jsx("div",{className:"flex items-center gap-2 sm:gap-3",children:l})]}),c&&e.jsx("div",{children:c})]})}const c={sm:"card-sm",md:"card-md",lg:"card-lg",hero:"card-hero",auto:"card-auto"};function t({children:a,size:l,compact:t,noPadding:r,className:i,style:n,onClick:d}){return e.jsx("div",{className:s("glass-card h-full flex flex-col",!r&&(t?"card-padding-sm":"card-padding"),l&&c[l],i),style:n,onClick:d,children:a})}function r({icon:a,title:l,badge:c,subtitle:t,actions:r,iconColor:i="text-accent-primary",largeTitle:n=!1,listHeader:d=!1}){return e.jsxs("div",{className:s("flex flex-col gap-1 flex-shrink-0",d?"px-4 py-3 border-b border-border-subtle bg-bg-elevated/20":"mb-3"),children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[a&&e.jsx("span",{className:s("icon-md",i),children:a}),e.jsx("span",{className:n?"type-subheading text-text-primary":"type-micro",children:l}),c&&e.jsx("span",{className:"pill-tag",children:c})]}),r&&e.jsx("div",{className:"flex items-center gap-2 flex-wrap",children:r})]}),t&&e.jsx("p",{className:"type-label text-text-muted ml-7",children:t})]})}export{t as C,a as P,l as a,r as b};
import{j as e}from"./index-BA-LoFOy.js";import{c as s}from"./recharts-bmHCCfxU.js";function a({children:a,className:l}){return e.jsx("div",{className:s("section-gap",l),children:a})}function l({title:s,icon:a,controls:l,subtitle:c}){return e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[e.jsxs("h1",{className:"type-title text-text-primary flex items-center gap-3",children:[a&&e.jsx("span",{className:"w-6 h-6 text-accent-primary flex-shrink-0",children:a}),s]}),l&&e.jsx("div",{className:"flex items-center gap-2 sm:gap-3",children:l})]}),c&&e.jsx("div",{children:c})]})}const c={sm:"card-sm",md:"card-md",lg:"card-lg",hero:"card-hero",auto:"card-auto"};function t({children:a,size:l,compact:t,noPadding:r,className:i,style:n,onClick:d}){return e.jsx("div",{className:s("glass-card h-full flex flex-col",!r&&(t?"card-padding-sm":"card-padding"),l&&c[l],i),style:n,onClick:d,children:a})}function r({icon:a,title:l,badge:c,subtitle:t,actions:r,iconColor:i="text-accent-primary",largeTitle:n=!1,listHeader:d=!1}){return e.jsxs("div",{className:s("flex flex-col gap-1 flex-shrink-0",d?"px-4 py-3 border-b border-border-subtle bg-bg-elevated/20":"mb-3"),children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[a&&e.jsx("span",{className:s("icon-md",i),children:a}),e.jsx("span",{className:n?"type-subheading text-text-primary":"type-micro",children:l}),c&&e.jsx("span",{className:"pill-tag",children:c})]}),r&&e.jsx("div",{className:"flex items-center gap-2 flex-wrap",children:r})]}),t&&e.jsx("p",{className:"type-label text-text-muted ml-7",children:t})]})}export{t as C,a as P,l as a,r as b};
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{c as e,j as t}from"./index-DoxF6EWj.js";import{c as a,b as s}from"./recharts-bmHCCfxU.js";const n=e("signal-high",[["path",{d:"M2 20h.01",key:"4haj6o"}],["path",{d:"M7 20v-4",key:"j294jx"}],["path",{d:"M12 20v-8",key:"i3yub9"}],["path",{d:"M17 20V8",key:"1tkaf5"}]]),r=e("signal-low",[["path",{d:"M2 20h.01",key:"4haj6o"}],["path",{d:"M7 20v-4",key:"j294jx"}]]),i=e("signal-medium",[["path",{d:"M2 20h.01",key:"4haj6o"}],["path",{d:"M7 20v-4",key:"j294jx"}],["path",{d:"M12 20v-8",key:"i3yub9"}]]),c=e("signal-zero",[["path",{d:"M2 20h.01",key:"4haj6o"}]]),o=e("signal",[["path",{d:"M2 20h.01",key:"4haj6o"}],["path",{d:"M7 20v-4",key:"j294jx"}],["path",{d:"M12 20v-8",key:"i3yub9"}],["path",{d:"M17 20V8",key:"1tkaf5"}],["path",{d:"M22 4v16",key:"sih9yq"}]]);function l(e){return e>=-90?"excellent":e>=-100?"good":e>=-110?"fair":e>=-120?"weak":"poor"}function d(e,t){if(!t)return"bg-white/10";switch(e){case"excellent":return"bg-signal-excellent";case"good":return"bg-signal-good";case"fair":return"bg-signal-fair";case"weak":return"bg-signal-poor";case"poor":return"bg-signal-critical";default:return"bg-white/20"}}function x({rssi:e,className:s="w-4 h-4"}){const d=l(e),x=function(e){switch(e){case"excellent":return"text-signal-excellent";case"good":return"text-signal-good";case"fair":return"text-signal-fair";case"weak":return"text-signal-poor";case"poor":return"text-signal-critical";default:return"text-text-muted"}}(d),h=a(x,s);switch(d){case"excellent":return t.jsx(o,{className:h});case"good":return t.jsx(n,{className:h});case"fair":return t.jsx(i,{className:h});case"weak":return t.jsx(r,{className:h});default:return t.jsx(c,{className:h})}}const h=s.memo(function({rssi:e,snr:s,compact:n=!1,showValues:r=!0}){const i=l(e),c={excellent:4,good:3,fair:2,weak:1,poor:0}[i];return n?t.jsxs("div",{className:"flex items-center justify-end gap-1.5",children:[r&&t.jsx("span",{className:"text-[10px] font-mono text-text-secondary",children:e}),t.jsx("div",{className:"flex items-end gap-[2px] h-3",children:Array.from({length:4}).map((e,s)=>t.jsx("div",{className:a("w-[3px] rounded-[1px] transition-colors",d(i,s<c)),style:{height:(s+1)/4*100+"%"}},s))})]}):t.jsxs("div",{className:"flex items-center justify-end gap-2",children:[r&&t.jsxs("div",{className:"flex flex-col items-end",children:[t.jsxs("span",{className:"text-xs font-mono text-text-secondary leading-tight",children:[e," dBm"]}),void 0!==s&&t.jsxs("span",{className:"text-[10px] font-mono text-text-muted leading-tight",children:[s.toFixed(1)," dB"]})]}),t.jsx("div",{className:"flex items-end gap-[2px] h-3.5",children:Array.from({length:4}).map((e,s)=>t.jsx("div",{className:a("w-[3px] rounded-[1px] transition-colors",d(i,s<c)),style:{height:(s+1)/4*100+"%"}},s))})]})});function p(e){const t=l(e);return t.charAt(0).toUpperCase()+t.slice(1)}export{h as S,x as a,p as g};
import{c as e,j as t}from"./index-BA-LoFOy.js";import{c as a,b as s}from"./recharts-bmHCCfxU.js";const n=e("signal-high",[["path",{d:"M2 20h.01",key:"4haj6o"}],["path",{d:"M7 20v-4",key:"j294jx"}],["path",{d:"M12 20v-8",key:"i3yub9"}],["path",{d:"M17 20V8",key:"1tkaf5"}]]),r=e("signal-low",[["path",{d:"M2 20h.01",key:"4haj6o"}],["path",{d:"M7 20v-4",key:"j294jx"}]]),i=e("signal-medium",[["path",{d:"M2 20h.01",key:"4haj6o"}],["path",{d:"M7 20v-4",key:"j294jx"}],["path",{d:"M12 20v-8",key:"i3yub9"}]]),c=e("signal-zero",[["path",{d:"M2 20h.01",key:"4haj6o"}]]),o=e("signal",[["path",{d:"M2 20h.01",key:"4haj6o"}],["path",{d:"M7 20v-4",key:"j294jx"}],["path",{d:"M12 20v-8",key:"i3yub9"}],["path",{d:"M17 20V8",key:"1tkaf5"}],["path",{d:"M22 4v16",key:"sih9yq"}]]);function l(e){return e>=-90?"excellent":e>=-100?"good":e>=-110?"fair":e>=-120?"weak":"poor"}function d(e,t){if(!t)return"bg-white/10";switch(e){case"excellent":return"bg-signal-excellent";case"good":return"bg-signal-good";case"fair":return"bg-signal-fair";case"weak":return"bg-signal-poor";case"poor":return"bg-signal-critical";default:return"bg-white/20"}}function x({rssi:e,className:s="w-4 h-4"}){const d=l(e),x=function(e){switch(e){case"excellent":return"text-signal-excellent";case"good":return"text-signal-good";case"fair":return"text-signal-fair";case"weak":return"text-signal-poor";case"poor":return"text-signal-critical";default:return"text-text-muted"}}(d),h=a(x,s);switch(d){case"excellent":return t.jsx(o,{className:h});case"good":return t.jsx(n,{className:h});case"fair":return t.jsx(i,{className:h});case"weak":return t.jsx(r,{className:h});default:return t.jsx(c,{className:h})}}const h=s.memo(function({rssi:e,snr:s,compact:n=!1,showValues:r=!0}){const i=l(e),c={excellent:4,good:3,fair:2,weak:1,poor:0}[i];return n?t.jsxs("div",{className:"flex items-center justify-end gap-1.5",children:[r&&t.jsx("span",{className:"text-[10px] font-mono text-text-secondary",children:e}),t.jsx("div",{className:"flex items-end gap-[2px] h-3",children:Array.from({length:4}).map((e,s)=>t.jsx("div",{className:a("w-[3px] rounded-[1px] transition-colors",d(i,s<c)),style:{height:(s+1)/4*100+"%"}},s))})]}):t.jsxs("div",{className:"flex items-center justify-end gap-2",children:[r&&t.jsxs("div",{className:"flex flex-col items-end",children:[t.jsxs("span",{className:"text-xs font-mono text-text-secondary leading-tight",children:[e," dBm"]}),void 0!==s&&t.jsxs("span",{className:"text-[10px] font-mono text-text-muted leading-tight",children:[s.toFixed(1)," dB"]})]}),t.jsx("div",{className:"flex items-end gap-[2px] h-3.5",children:Array.from({length:4}).map((e,s)=>t.jsx("div",{className:a("w-[3px] rounded-[1px] transition-colors",d(i,s<c)),style:{height:(s+1)/4*100+"%"}},s))})]})});function p(e){const t=l(e);return t.charAt(0).toUpperCase()+t.slice(1)}export{h as S,x as a,p as g};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{b as e}from"./recharts-bmHCCfxU.js";import{j as t}from"./index-DoxF6EWj.js";function o(t,o){const[s,r]=e.useState(t);return e.useEffect(()=>{const e=setTimeout(()=>r(t),o);return()=>clearTimeout(e)},[t,o]),s}const s=e.memo(function({ranges:e,selectedIndex:o,onSelect:s}){return t.jsx("div",{className:"toggle-group flex-shrink-0 overflow-x-auto",children:e.map((e,r)=>t.jsx("button",{onClick:()=>s(r),className:"toggle-group-item "+(o===r?"active":""),children:e.label},e.label))})});export{s as T,o as u};
import{b as e}from"./recharts-bmHCCfxU.js";import{j as t}from"./index-BA-LoFOy.js";function o(t,o){const[s,r]=e.useState(t);return e.useEffect(()=>{const e=setTimeout(()=>r(t),o);return()=>clearTimeout(e)},[t,o]),s}const s=e.memo(function({ranges:e,selectedIndex:o,onSelect:s}){return t.jsx("div",{className:"toggle-group flex-shrink-0 overflow-x-auto",children:e.map((e,r)=>t.jsx("button",{onClick:()=>s(r),className:"toggle-group-item "+(o===r?"active":""),children:e.label},e.label))})});export{s as T,o as u};
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.
@@ -1 +1 @@
import{c as a}from"./index-DoxF6EWj.js";const t=a("activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);export{t as A};
import{c as a}from"./index-BA-LoFOy.js";const t=a("activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]]);export{t as A};
+1
View File
@@ -0,0 +1 @@
import{c as t}from"./index-BA-LoFOy.js";const h=t("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);export{h as C};
-1
View File
@@ -1 +0,0 @@
import{c as t}from"./index-DoxF6EWj.js";const h=t("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);export{h as C};
+1
View File
@@ -0,0 +1 @@
import{c}from"./index-BA-LoFOy.js";const r=c("circle",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]);export{r as C};
-1
View File
@@ -1 +0,0 @@
import{c}from"./index-DoxF6EWj.js";const r=c("circle",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]]);export{r as C};
@@ -1 +1 @@
import{c as a}from"./index-DoxF6EWj.js";const t=a("external-link",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);export{t as E};
import{c as a}from"./index-BA-LoFOy.js";const t=a("external-link",[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]]);export{t as E};
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

@@ -1 +1 @@
import{c as a}from"./index-DoxF6EWj.js";const s=a("house",[["path",{d:"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8",key:"5wwlr5"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",key:"r6nss1"}]]);export{s as H};
import{c as a}from"./index-BA-LoFOy.js";const s=a("house",[["path",{d:"M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8",key:"5wwlr5"}],["path",{d:"M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",key:"r6nss1"}]]);export{s as H};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{c as e}from"./index-DoxF6EWj.js";const c=e("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);export{c as I};
import{c as e}from"./index-BA-LoFOy.js";const c=e("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);export{c as I};
@@ -1 +1 @@
import{c as a}from"./index-DoxF6EWj.js";const e=a("loader-circle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);export{e as L};
import{c as a}from"./index-BA-LoFOy.js";const e=a("loader-circle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]]);export{e as L};
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

@@ -1 +1 @@
import{c as a}from"./index-DoxF6EWj.js";const p=a("map",[["path",{d:"M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z",key:"169xi5"}],["path",{d:"M15 5.764v15",key:"1pn4in"}],["path",{d:"M9 3.236v15",key:"1uimfh"}]]);export{p as M};
import{c as a}from"./index-BA-LoFOy.js";const p=a("map",[["path",{d:"M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z",key:"169xi5"}],["path",{d:"M15 5.764v15",key:"1pn4in"}],["path",{d:"M9 3.236v15",key:"1uimfh"}]]);export{p as M};
@@ -1 +1 @@
import{c}from"./index-DoxF6EWj.js";const r=c("map-pin",[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0",key:"1r0f0z"}],["circle",{cx:"12",cy:"10",r:"3",key:"ilqhr7"}]]);export{r as M};
import{c}from"./index-BA-LoFOy.js";const r=c("map-pin",[["path",{d:"M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0",key:"1r0f0z"}],["circle",{cx:"12",cy:"10",r:"3",key:"ilqhr7"}]]);export{r as M};
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{c as a}from"./index-DoxF6EWj.js";const e=a("refresh-cw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);export{e as R};
import{c as a}from"./index-BA-LoFOy.js";const e=a("refresh-cw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);export{e as R};
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

@@ -1 +1 @@
import{c as t}from"./index-DoxF6EWj.js";const e=t("network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]),h=t("trending-up",[["path",{d:"M16 7h6v6",key:"box55l"}],["path",{d:"m22 7-8.5 8.5-5-5L2 17",key:"1t1m79"}]]);export{e as N,h as T};
import{c as t}from"./index-BA-LoFOy.js";const e=t("network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]),h=t("trending-up",[["path",{d:"M16 7h6v6",key:"box55l"}],["path",{d:"m22 7-8.5 8.5-5-5L2 17",key:"1t1m79"}]]);export{e as N,h as T};
@@ -1 +1 @@
import{c as a}from"./index-DoxF6EWj.js";const e=a("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]),t=a("copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]),h=a("triangle-alert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);export{t as C,h as T,e as a};
import{c as a}from"./index-BA-LoFOy.js";const e=a("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]),t=a("copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]),h=a("triangle-alert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);export{t as C,h as T,e as a};
@@ -1 +1 @@
import{c as a}from"./index-DoxF6EWj.js";const e=a("users",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["path",{d:"M16 3.128a4 4 0 0 1 0 7.744",key:"16gr8j"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}]]);export{e as U};
import{c as a}from"./index-BA-LoFOy.js";const e=a("users",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["path",{d:"M16 3.128a4 4 0 0 1 0 7.744",key:"16gr8j"}],["path",{d:"M22 21v-2a4 4 0 0 0-3-3.87",key:"kshegd"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}]]);export{e as U};
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

@@ -1 +1 @@
import{c as a}from"./index-DoxF6EWj.js";const t=a("minus",[["path",{d:"M5 12h14",key:"1ays0h"}]]),d=a("trending-down",[["path",{d:"M16 17h6v-6",key:"t6n2it"}],["path",{d:"m22 17-8.5-8.5-5 5L2 7",key:"x473p"}]]),h=a("zap",[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]]);export{t as M,d as T,h as Z};
import{c as a}from"./index-BA-LoFOy.js";const t=a("minus",[["path",{d:"M5 12h14",key:"1ays0h"}]]),d=a("trending-down",[["path",{d:"M16 17h6v-6",key:"t6n2it"}],["path",{d:"m22 17-8.5-8.5-5 5L2 7",key:"x473p"}]]),h=a("zap",[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]]);export{t as M,d as T,h as Z};
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

-9
View File
@@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#B49DFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4.9 16.1C1 12.2 1 5.8 4.9 1.9"/>
<path d="M7.8 4.7a6.14 6.14 0 0 0-.8 7.5"/>
<circle cx="12" cy="9" r="2"/>
<path d="M16.2 4.8c2 2 2.26 5.11.8 7.47"/>
<path d="M19.1 1.9a9.96 9.96 0 0 1 0 14.1"/>
<path d="M9.5 18h5"/>
<path d="m8 22 4-11 4 11"/>
</svg>

Before

Width:  |  Height:  |  Size: 454 B

+1 -2
View File
@@ -5,7 +5,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>pyMC Console</title>
<meta name="description" content="LoRa Mesh Network Repeater Monitoring Dashboard" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<!-- Fonts: Inter (display) and JetBrains Mono (data) -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
@@ -39,7 +38,7 @@
--font-data: 'JetBrains Mono', 'SF Mono', Monaco, monospace;
}
</style>
<script type="module" crossorigin src="/assets/index-DoxF6EWj.js"></script>
<script type="module" crossorigin src="/assets/index-BA-LoFOy.js"></script>
<link rel="modulepreload" crossorigin href="/assets/maplibre-gl-CFO9X1Ek.js">
<link rel="modulepreload" crossorigin href="/assets/recharts-bmHCCfxU.js">
<link rel="stylesheet" crossorigin href="/assets/index--XvAw-pH.css">
+29 -2
View File
@@ -1,8 +1,18 @@
{
"name": "pymc_console",
"version": "0.9.133",
"version": "0.9.121",
"description": "Vite + React Dashboard for pyMC_Repeater",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"typecheck": "tsc --noEmit",
"build": "vite build",
"build:static": "npm run build && npm run package",
"package": "rm -rf dist && mkdir -p dist && cp -r out/* dist/",
"preview": "vite preview",
"lint": "eslint"
},
"dependencies": {
"@deck.gl/aggregation-layers": "^9.2.5",
"@deck.gl/core": "^9.2.5",
@@ -24,5 +34,22 @@
"react-router-dom": "^7.1.1",
"recharts": "^3.5.1",
"zustand": "^5.0.9"
},
"devDependencies": {
"@eslint/js": "^9.39.2",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@vitejs/plugin-react-swc": "^3.7.2",
"eslint": "^9",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.25",
"globals": "^16.5.0",
"tailwindcss": "^4",
"terser": "^5.44.1",
"typescript": "^5",
"typescript-eslint": "^8.50.0",
"vite": "^6.0.5"
}
}
}
+3 -297
View File
@@ -585,8 +585,7 @@ print_completion() {
echo ""
echo -e "${BOLD}Access your dashboard:${NC}"
echo -e " ${ARROW} Dashboard: ${CYAN}https://$ip_address:8000/${NC}"
echo -e " ${DIM}(Self-signed cert - browser will show warning)${NC}"
echo -e " ${ARROW} Dashboard: ${CYAN}http://$ip_address:8000/${NC}"
echo -e " ${DIM}(API endpoints also available at /api/*)${NC}"
echo ""
}
@@ -910,7 +909,6 @@ do_install() {
patch_log_level_api "$INSTALL_DIR" # PATCH 3: Log level toggle API
patch_mesh_cli "$INSTALL_DIR" # PATCH 4: MeshCore CLI parity
patch_private_key_api "$INSTALL_DIR" # PATCH 6: Private key get/set API
patch_ssl_support "$INSTALL_DIR" # PATCH 7: SSL/HTTPS support
# =========================================================================
# Step 5: Install dashboard and console extras
@@ -940,9 +938,6 @@ do_install() {
# Fix permissions for console directory
chown -R "$SERVICE_USER:$SERVICE_USER" "$CONSOLE_DIR" 2>/dev/null || true
# Setup HTTPS (generate self-signed cert and configure)
setup_https
# =========================================================================
# Step 6: Finalize installation
# =========================================================================
@@ -1209,8 +1204,7 @@ Continue?"; then
echo -e " ${CHECK} pyMC Console: ${DIM}${current_console_ver}${NC}${CYAN}${new_console_ver}${NC}"
echo ""
echo -e " ${CHECK} Configuration preserved"
echo -e " ${CHECK} Dashboard: ${CYAN}https://$ip_address:8000${NC}"
echo -e " ${DIM}(Self-signed cert - browser will show warning)${NC}"
echo -e " ${CHECK} Dashboard: ${CYAN}http://$ip_address:8000${NC}"
echo ""
return 0
fi
@@ -1303,7 +1297,6 @@ Continue?"; then
patch_mesh_cli "$INSTALL_DIR" # PATCH 4: MeshCore CLI parity
patch_stats_api "$INSTALL_DIR" # PATCH 5: Extend stats API with MeshCore config
patch_private_key_api "$INSTALL_DIR" # PATCH 6: Private key get/set API
patch_ssl_support "$INSTALL_DIR" # PATCH 7: SSL/HTTPS support
# Ensure --log-level DEBUG
if [ -f /etc/systemd/system/pymc-repeater.service ]; then
@@ -1320,9 +1313,6 @@ Continue?"; then
print_warning "Dashboard update failed - service will continue with existing UI"
}
# Setup HTTPS (generate self-signed cert if missing, configure)
setup_https
# Step 5: Restart service with patches
((step_num++)) || true
print_step $step_num $total_steps "Restarting service"
@@ -1356,8 +1346,7 @@ Continue?"; then
echo -e " ${CHECK} pyMC Console: ${DIM}${current_console_ver}${NC}${CYAN}${new_console_ver}${NC}"
echo ""
echo -e " ${CHECK} Configuration preserved"
echo -e " ${CHECK} Dashboard: ${CYAN}https://$ip_address:8000${NC}"
echo -e " ${DIM}(Self-signed cert - browser will show warning)${NC}"
echo -e " ${CHECK} Dashboard: ${CYAN}http://$ip_address:8000${NC}"
echo ""
}
@@ -2331,13 +2320,6 @@ run_upstream_installer() {
# - Stores key in config['mesh']['identity_key']
# - PR Status: Pending
#
# 7. patch_ssl_support (http_server.py)
# - Enables HTTPS via CherryPy's built-in SSL adapter
# - Reads ssl_certificate and ssl_private_key from config.yaml web section
# - Auto-generates self-signed cert during install/upgrade (see setup_https)
# - Falls back to HTTP if certs not configured
# - PR Status: Pending
#
# NOTE: patch_static_file_serving was removed
# Upstream's default() method already returns index.html for all unknown routes,
# which is exactly what a true SPA needs. React Router handles client-side routing.
@@ -2668,282 +2650,6 @@ patch_private_key_api() {
fi
}
# ------------------------------------------------------------------------------
# SSL Certificate Generation
# ------------------------------------------------------------------------------
# Generates self-signed certificate for HTTPS support.
# Called during install/upgrade to enable HTTPS automatically.
# ------------------------------------------------------------------------------
generate_ssl_cert() {
local ssl_dir="/etc/pymc_repeater/ssl"
local ssl_cert="$ssl_dir/cert.pem"
local ssl_key="$ssl_dir/key.pem"
print_info "Checking SSL certificates..."
# Skip if certs already exist and are valid
if [ -f "$ssl_cert" ] && [ -f "$ssl_key" ]; then
# Verify cert is readable
if openssl x509 -in "$ssl_cert" -noout 2>/dev/null; then
print_info "SSL certificates already exist and are valid"
return 0
else
print_warning "Existing SSL certificate is invalid, regenerating..."
rm -f "$ssl_cert" "$ssl_key"
fi
fi
# Check if openssl is available
if ! command -v openssl &> /dev/null; then
print_warning "openssl not found - installing..."
apt-get install -y openssl 2>/dev/null || {
print_error "Could not install openssl - HTTPS will not be available"
return 1
}
fi
# Create SSL directory with proper permissions
mkdir -p "$ssl_dir"
chmod 755 "$ssl_dir"
# Get hostname and IP for certificate
local cert_hostname=$(hostname 2>/dev/null || echo "pymc-repeater")
local cert_ip=$(hostname -I 2>/dev/null | awk '{print $1}')
[ -z "$cert_ip" ] && cert_ip="127.0.0.1"
print_info "Generating SSL certificate for $cert_hostname ($cert_ip)..."
# Create OpenSSL config for SAN (Subject Alternative Names)
# This avoids the -addext flag which isn't available on older openssl
local ssl_conf=$(mktemp)
cat > "$ssl_conf" << SSLCONF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = $cert_hostname
O = pyMC Console
C = US
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = $cert_hostname
DNS.2 = localhost
IP.1 = $cert_ip
IP.2 = 127.0.0.1
SSLCONF
# Generate the certificate
if openssl req -x509 -newkey rsa:2048 \
-keyout "$ssl_key" \
-out "$ssl_cert" \
-days 3650 \
-nodes \
-config "$ssl_conf" \
2>/dev/null; then
# Clean up temp config
rm -f "$ssl_conf"
# Set permissions - key should be readable by service user
chmod 600 "$ssl_key"
chmod 644 "$ssl_cert"
chown -R "${SERVICE_USER:-root}:${SERVICE_USER:-root}" "$ssl_dir" 2>/dev/null || true
# Verify the generated cert
if openssl x509 -in "$ssl_cert" -noout 2>/dev/null; then
print_success "Generated SSL certificate for $cert_hostname"
print_info " Certificate: $ssl_cert"
print_info " Private key: $ssl_key"
return 0
else
print_error "Generated certificate is invalid"
rm -f "$ssl_cert" "$ssl_key"
return 1
fi
else
rm -f "$ssl_conf"
print_error "Failed to generate SSL certificate"
return 1
fi
}
configure_ssl_in_config() {
local config_file="/etc/pymc_repeater/config.yaml"
local ssl_cert="/etc/pymc_repeater/ssl/cert.pem"
local ssl_key="/etc/pymc_repeater/ssl/key.pem"
print_info "Configuring SSL in config.yaml..."
# Check config file exists
if [ ! -f "$config_file" ]; then
print_warning "Config file not found: $config_file"
return 1
fi
# Check certs exist
if [ ! -f "$ssl_cert" ] || [ ! -f "$ssl_key" ]; then
print_warning "SSL certificates not found, skipping config"
return 1
fi
# Check yq is available
if ! command -v yq &> /dev/null; then
print_warning "yq not available - adding SSL config manually"
# Fallback: append to config if not already present
if ! grep -q "ssl_certificate" "$config_file" 2>/dev/null; then
cat >> "$config_file" << SSLCONFIG
# SSL/HTTPS Configuration (auto-generated by pymc_console)
web:
ssl_certificate: "$ssl_cert"
ssl_private_key: "$ssl_key"
SSLCONFIG
print_success "Added SSL config to config.yaml (manual append)"
fi
return 0
fi
# Use yq to add/update SSL config
# First ensure web section exists
yq -i '.web //= {}' "$config_file" 2>/dev/null || true
# Set SSL paths
yq -i ".web.ssl_certificate = \"$ssl_cert\"" "$config_file" 2>/dev/null
yq -i ".web.ssl_private_key = \"$ssl_key\"" "$config_file" 2>/dev/null
# Verify config was written
if grep -q "ssl_certificate" "$config_file" 2>/dev/null; then
print_success "Configured HTTPS in config.yaml"
return 0
else
print_warning "Could not verify SSL config was written"
return 1
fi
}
setup_https() {
print_info "Setting up HTTPS..."
# Step 1: Generate certificate
if ! generate_ssl_cert; then
print_warning "SSL certificate generation failed - HTTPS will not be available"
return 1
fi
# Step 2: Configure in config.yaml
if ! configure_ssl_in_config; then
print_warning "SSL configuration failed - HTTPS may not work"
return 1
fi
print_success "HTTPS setup complete"
return 0
}
# ------------------------------------------------------------------------------
# PATCH 7: SSL/HTTPS Support (http_server.py)
# ------------------------------------------------------------------------------
# File: repeater/web/http_server.py
# Purpose: Enable HTTPS via CherryPy's built-in SSL adapter
# Config:
# web:
# ssl_certificate: /path/to/cert.pem
# ssl_private_key: /path/to/key.pem
# When both paths are set, CherryPy will serve HTTPS on the configured port.
# PR Status: Pending upstream submission
# ------------------------------------------------------------------------------
patch_ssl_support() {
local target_dir="${1:-$CLONE_DIR}"
local http_file="$target_dir/repeater/web/http_server.py"
if [ ! -f "$http_file" ]; then
print_warning "http_server.py not found, skipping SSL patch"
return 0
fi
# Check if already patched
if grep -q 'ssl_certificate' "$http_file" 2>/dev/null; then
print_info "SSL support already patched"
return 0
fi
# Use Python to patch the file
python3 - "$http_file" << 'PATCHEOF'
import sys
http_file = sys.argv[1]
with open(http_file, 'r') as f:
content = f.read()
# Find the cherrypy.config.update block and add SSL config
# We need to add SSL settings before server.socket_host
old_config = ''' cherrypy.config.update(
{
"server.socket_host": self.host,
"server.socket_port": self.port,'''
new_config = ''' # SSL/HTTPS Configuration (pymc_console patch)
ssl_cert = self.config.get("web", {}).get("ssl_certificate", "")
ssl_key = self.config.get("web", {}).get("ssl_private_key", "")
ssl_config = {}
if ssl_cert and ssl_key and os.path.isfile(ssl_cert) and os.path.isfile(ssl_key):
ssl_config = {
"server.ssl_module": "builtin",
"server.ssl_certificate": ssl_cert,
"server.ssl_private_key": ssl_key,
}
logger.info(f"SSL enabled with certificate: {ssl_cert}")
elif ssl_cert or ssl_key:
logger.warning(f"SSL certificate or key not found, running HTTP only")
cherrypy.config.update(
{
**ssl_config,
"server.socket_host": self.host,
"server.socket_port": self.port,'''
if old_config in content:
content = content.replace(old_config, new_config)
else:
print("Could not find config block to patch")
sys.exit(1)
# Also update the log message to show https:// when SSL is enabled
old_log = ''' server_url = "http://{}:{}".format(self.host, self.port)
logger.info(f"HTTP stats server started on {server_url}")'''
new_log = ''' protocol = "https" if ssl_config else "http"
server_url = "{}://{}:{}".format(protocol, self.host, self.port)
logger.info(f"{protocol.upper()} stats server started on {server_url}")'''
if old_log in content:
content = content.replace(old_log, new_log)
with open(http_file, 'w') as f:
f.write(content)
print("Patched http_server.py with SSL support")
PATCHEOF
# Verify patch was applied
if grep -q 'ssl_certificate' "$http_file" 2>/dev/null; then
print_success "Patched http_server.py with SSL support"
else
print_warning "SSL patch may not have applied correctly"
fi
}
install_backend_service() {
# Copy upstream's service file as base (from clone directory)
local service_file="$CLONE_DIR/pymc-repeater.service"