@camstack/addon-admin-ui 1.2.399 → 1.2.400

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.
Files changed (44) hide show
  1. package/dist/assets/{AddonAssetIcon-DRk5tFmQ.js → AddonAssetIcon-B8b_Ij3Q.js} +1 -1
  2. package/dist/assets/{Addons-eoG6yhVR.js → Addons-Csw_pkUq.js} +1 -1
  3. package/dist/assets/{AdminSectionedPage-Dmz5o8uT.js → AdminSectionedPage-Dr8lqQvA.js} +1 -1
  4. package/dist/assets/{AiProfiles-CeyoQkna.js → AiProfiles-BUQCPfG2.js} +1 -1
  5. package/dist/assets/{BrokerForm-UsE-opKN.js → BrokerForm-CDY_h_x_.js} +1 -1
  6. package/dist/assets/{Build-BJTgUvs3.js → Build-DowgaoRs.js} +1 -1
  7. package/dist/assets/{Calendars-hNy0XH9U.js → Calendars-CjwnNTaU.js} +1 -1
  8. package/dist/assets/{Cluster-DL2F6C0r.js → Cluster-B1dInAlz.js} +1 -1
  9. package/dist/assets/{ClusterStepModel-BMV2GNMb.js → ClusterStepModel-DvaE2Fpn.js} +1 -1
  10. package/dist/assets/{CompositionEditorPage-BcxlauFW.js → CompositionEditorPage-ILN_BH-0.js} +1 -1
  11. package/dist/assets/{Data-DJzLYSz8.js → Data-hUPfS0O6.js} +1 -1
  12. package/dist/assets/{DeviceDetail-BxQ2qayC.js → DeviceDetail-ClcUBiDj.js} +1 -1
  13. package/dist/assets/{Devices-CLznFJt2.js → Devices-CbF91f1O.js} +1 -1
  14. package/dist/assets/Identity-Xw1YjqEJ.js +1 -0
  15. package/dist/assets/{IntegrationDetail-ClkIqZkc.js → IntegrationDetail-CuUjr1ni.js} +1 -1
  16. package/dist/assets/IntegrationHub-4oemdKQs.js +1 -0
  17. package/dist/assets/Integrations-CsXxPeGb.js +1 -0
  18. package/dist/assets/{Integrations-tr28SRdu.js → Integrations-DPziOQvV.js} +1 -1
  19. package/dist/assets/{Library-BoOL6cQN.js → Library-OaGdN5V3.js} +1 -1
  20. package/dist/assets/{Locations-ddLOQn97.js → Locations-CPEee4O7.js} +1 -1
  21. package/dist/assets/Logs-BertXlLF.js +1 -0
  22. package/dist/assets/{MyAccess-BvNfguhI.js → MyAccess-HQF6tebp.js} +1 -1
  23. package/dist/assets/Network-CK42t6W_.js +1 -0
  24. package/dist/assets/{NodeAddonsSettingsPanel-DNm2sNWd.js → NodeAddonsSettingsPanel-DRU_bmGt.js} +1 -1
  25. package/dist/assets/{Notifications-C77viP5y.js → Notifications-D5aHMhvU.js} +1 -1
  26. package/dist/assets/{Recordings-a6r7fxRd.js → Recordings-dPu5GCbo.js} +1 -1
  27. package/dist/assets/{ScanNetworkModal-yeTJMkLN.js → ScanNetworkModal-XJW0dYom.js} +1 -1
  28. package/dist/assets/{SessionsList-JKR36vEz.js → SessionsList-ZafI0kEX.js} +1 -1
  29. package/dist/assets/{Showroom-uPs2cinU.js → Showroom-CMomGEBv.js} +1 -1
  30. package/dist/assets/{System-iyC6su3q.js → System-SugJLaIm.js} +1 -1
  31. package/dist/assets/{Terminal-B_bk9YuG.js → Terminal-CMZkgAtq.js} +1 -1
  32. package/dist/assets/{device-integration-icon-BVEgrVH7.js → device-integration-icon-DoswOpEp.js} +1 -1
  33. package/dist/assets/{index-V6k1XLI7.js → index-B-BE8EYQ.js} +2 -2
  34. package/dist/assets/{location-owned-usage-CG3xos_0.js → location-owned-usage-DxjPP5ha.js} +1 -1
  35. package/dist/assets/{mf-entry-bootstrap-0-0ee3f006.js → mf-entry-bootstrap-0-50b2491c.js} +1 -1
  36. package/dist/assets/{useNodeUrlSelection-CM809DR6.js → useNodeUrlSelection-yjHHXKaf.js} +1 -1
  37. package/dist/index.html +2 -2
  38. package/dist/server/addon.js +1 -1
  39. package/package.json +1 -1
  40. package/dist/assets/AddonIntegrationBody-BokvIE2T.js +0 -1
  41. package/dist/assets/Identity-DQuyqFAW.js +0 -1
  42. package/dist/assets/Integrations-Di2rQcGB.js +0 -1
  43. package/dist/assets/Logs-DOMXl7b_.js +0 -1
  44. package/dist/assets/Network-xLYgrYEu.js +0 -1
@@ -1 +1 @@
1
- import{I as e,Q as t,S as n,W as r,o as i,q as a,x as o,z as s}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{D as c,_t as l,ct as u,gt as d,ht as f,ot as p,yt as m}from"./player-overlays-9CMoB0XN.js";import{An as h,Ed as g,Fd as _,Fn as v,Gn as y,In as b,Mn as x,Pn as S,U as C,Vt as w,Wc as ee,dd as T,jn as te,ld as E,md as D,mu as O,nl as ne,nr as k,wr as A}from"./src-Ca4gNAmP.js";import{t as j}from"./archive-D3wNUMUT.js";import{n as M,t as N}from"./format-size-CI1h5otl.js";import{ei as P}from"./client-1JBRoe76.js";import{I as re}from"./index-V6k1XLI7.js";var F=m(`eraser`,[[`path`,{d:`M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21`,key:`g5wo59`}],[`path`,{d:`m5.082 11.09 8.828 8.828`,key:`1wx5vj`}]]);t();function I(e){let t=e.trim().toLowerCase();return t.length===0?``:t.replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)}function ie(e,t){let n=I(t);return`${e}:${n.length>0?n:`default`}`}var ae=new Set([`proc`,`sys`,`dev`,`run`,`boot`,`lost+found`,`snap`,`bin`,`sbin`,`lib`,`lib32`,`lib64`,`libx32`,`usr`,`etc`,`var`,`opt`,`tmp`,`srv`,`root`,`System`,`Library`,`Applications`,`cores`,`private`]),oe=[`recordings`,`data`,`mnt`,`media`,`Volumes`];function L(e){let t=oe.findIndex(t=>t===`recordings`?e.startsWith(t):e===t);return t===-1?oe.length:t}function R(e){return e.filter(e=>!e.name.startsWith(`.`)&&!ae.has(e.name)).toSorted((e,t)=>{let n=L(e.name)-L(t.name);return n===0?e.name.localeCompare(t.name):n}).map(e=>e.path)}function se({nodeId:t,value:r,onChange:i}){let c=p().trpcClient,[l,u]=a([]),[d,f]=a(r),[m,h]=a(null),[g,_]=a(null),[v,y]=a(``),b=e(async(e,n)=>{_(null);try{let r=await c.filesystemBrowse.browse.query({nodeId:t,path:e});h(r),f(r.path),n&&i(r.path)}catch(e){_(e instanceof Error?e.message:`browse failed`)}},[c,t,i]);s(()=>{f(r),(async()=>{let e=await c.filesystemBrowse.browse.query({nodeId:t,path:`/`}).then(e=>e).catch(()=>null);if(e){u(R(e.entries)),await b(r.startsWith(`/`)?r:`/`,!1);return}try{let e=await c.filesystemBrowse.listAllowedRoots.query({nodeId:t});u(e),await b(r&&e.some(e=>r.startsWith(e))?r:e[0]??``,!1)}catch(e){u([]),h(null),_(e instanceof Error&&/not available on node/i.test(e.message)?`This node can't be browsed remotely. Enter the path manually.`:e instanceof Error?e.message:`browse unavailable`)}})()},[t]);let x=async()=>{if(!(!v.trim()||!m)){_(null);try{let e=`${m.path.replace(/\/+$/,``)}/${v.trim()}`;await c.filesystemBrowse.createDir.mutate({nodeId:t,path:e}),y(``),await b(m.path,!0)}catch(e){_(e instanceof Error?e.message:`create failed`)}}},S=e=>`${(e/1e9).toFixed(1)} GB`;return n(`div`,{className:`space-y-2 rounded border border-border p-2`,children:[o(`div`,{className:`flex flex-wrap gap-1`,children:l.map(e=>o(`button`,{onClick:()=>void b(e,!0),className:`rounded border border-border px-2 py-0.5 text-[10px] font-mono hover:border-primary/40`,children:e},e))}),o(`input`,{value:d,onChange:e=>{f(e.target.value),i(e.target.value)},placeholder:`/absolute/path`,className:`w-full rounded border border-border bg-background px-2 py-1 text-[11px] font-mono`}),m&&n(`div`,{className:`text-[10px] text-foreground-subtle`,children:[S(m.freeBytes),` free / `,S(m.totalBytes)]}),o(`div`,{className:`max-h-40 space-y-0.5 overflow-y-auto`,children:m?.entries.map(e=>n(`button`,{onClick:()=>void b(e.path,!0),className:`block w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-primary/5`,children:[`📁 `,e.name]},e.path))}),n(`div`,{className:`flex gap-1`,children:[o(`input`,{value:v,onChange:e=>y(e.target.value),placeholder:`new folder`,className:`flex-1 rounded border border-border bg-background px-2 py-1 text-xs`}),o(`button`,{onClick:()=>void x(),className:`rounded border border-border px-2 py-1 text-xs hover:border-primary/40`,children:`Create`})]}),g&&o(`div`,{className:`text-[10px] text-red-400`,children:g})]})}t();function z({initialType:e,onClose:t,onCreated:s}){let c=i(),l=p().trpcClient,[u,f]=a(`picker`),[m,_]=a(e),[y,S]=a(null),[w,T]=a({}),[E,D]=a(``),[O,k]=a(null),[A,j]=a(!1),[M,N]=a(`hub`),[P,F]=a(null),[I,ae]=a(!1),oe=e=>T(t=>({...t,basePath:e})),L=typeof w.basePath==`string`?w.basePath:``,R=ee(),z=r(()=>{let e=R.data;return Array.isArray(e)?e:[]},[R.data]),B=R.isLoading,V=ne({onSuccess:()=>{c.invalidateQueries({queryKey:[[`storage`,`listLocations`]]}),s()}}),H=r(()=>O!==null&&O.length>0?O:ie(m,E),[m,E,O]),U=async()=>{if(y){ae(!0),F(null);try{F(await(e=>l.storage.testConfig.query(e))({providerId:y.providerId,config:w}))}catch(e){F({ok:!1,error:e instanceof Error?e.message:String(e)})}finally{ae(!1)}}};return o(h,{open:!0,onOpenChange:e=>!e&&t(),children:n(x,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,children:[n(v,{children:[o(b,{children:{picker:`Add Location — Pick provider`,config:`Add Location — ${y?.displayName??``}`,naming:`Add Location — Name`}[u]}),n(`p`,{className:`text-xs text-foreground-subtle`,children:[`Type: `,o(`span`,{className:`font-mono`,children:m})]})]}),n(te,{children:[u===`picker`&&n(`div`,{className:`space-y-2`,children:[B&&o(`div`,{className:`text-xs text-foreground-subtle animate-pulse`,children:`Loading providers…`}),!B&&z.length===0&&n(`div`,{className:`rounded border border-border bg-background px-3 py-4 text-center text-xs text-foreground-subtle`,children:[`No storage-provider supports type "`,m,`". Install a compatible addon (e.g.`,` `,o(`code`,{className:`font-mono`,children:`remote-storage`}),`) to enable it.`]}),z.map(e=>n(`button`,{onClick:()=>{S(e),f(`config`)},className:`flex w-full items-center gap-3 rounded-lg border border-border bg-background p-3 text-left hover:border-primary/40 hover:bg-primary/5 transition-colors`,children:[n(`div`,{className:`flex-1 min-w-0`,children:[o(`p`,{className:`text-xs font-semibold text-foreground`,children:e.displayName}),o(`p`,{className:`text-[10px] text-foreground-subtle font-mono mt-0.5 truncate`,children:e.providerId})]}),o(d,{className:`h-4 w-4 text-foreground-subtle flex-shrink-0`})]},e.providerId))]}),u===`config`&&y&&n(`div`,{className:`space-y-3`,children:[o(re,{schema:y.configSchema,values:w,onChange:T}),y.nodeLocal&&n(`div`,{className:`space-y-2`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Node`}),o(C,{selectedNodeId:M,onSelect:N,alwaysShow:!0}),o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Path`}),o(se,{nodeId:M,value:L,onChange:oe})]}),P&&o(`div`,{className:`rounded border px-3 py-2 text-[11px] ${P.ok?`border-success/30 bg-success/5 text-success`:`border-danger/30 bg-danger/5 text-danger`}`,children:P.ok?`Connection OK.`:`Test failed: ${P.error??`unknown error`}`}),n(`div`,{className:`flex items-center justify-between pt-1`,children:[o(`button`,{onClick:()=>f(`picker`),className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Back`}),n(`div`,{className:`flex items-center gap-2`,children:[o(`button`,{onClick:()=>{U()},disabled:I,className:`rounded border border-border px-3 py-1.5 text-xs text-foreground hover:bg-primary/10 disabled:opacity-50`,children:I?`Testing…`:`Test connection`}),o(`button`,{onClick:()=>f(`naming`),className:`rounded bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90`,children:`Next`})]})]})]}),u===`naming`&&y&&n(`div`,{className:`space-y-3`,children:[n(`div`,{className:`space-y-1`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Display name`}),o(`input`,{type:`text`,value:E,onChange:e=>D(e.target.value),placeholder:`${m} on ${y.displayName}`,className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`})]}),n(`div`,{className:`space-y-1`,children:[n(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:[`Id`,` `,o(`span`,{className:`lowercase tracking-normal text-foreground-subtle`,children:`— auto-derived; override if needed`})]}),o(`input`,{type:`text`,value:H,onChange:e=>k(e.target.value),placeholder:ie(m,E),className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground font-mono focus:outline-none focus:ring-1 focus:ring-primary`}),n(`p`,{className:`text-[10px] text-foreground-subtle`,children:[`Format: `,o(`code`,{className:`font-mono`,children:`<type>:<slug>`}),`. Lowercase letters, digits, and hyphens only.`]})]}),n(`label`,{className:`flex items-start gap-2 rounded border border-border bg-background px-3 py-2 cursor-pointer hover:bg-primary/5`,children:[o(`input`,{type:`checkbox`,checked:A,onChange:e=>j(e.target.checked),className:`accent-primary mt-0.5`}),n(`div`,{className:`space-y-0.5`,children:[o(`div`,{className:`text-xs font-medium text-foreground`,children:`Enable immediately`}),o(`div`,{className:`text-[10px] text-foreground-subtle`,children:`A disabled location receives no writes until enabled — reads and age-based retention still work, nothing new lands here. Leave this off to register the disk now and start using it later.`})]})]}),V.isError&&o(`div`,{className:`rounded border border-danger/30 bg-danger/5 text-danger px-3 py-2 text-[11px]`,children:V.error instanceof Error?V.error.message:String(V.error)}),n(`div`,{className:`flex items-center justify-between pt-1`,children:[o(`button`,{onClick:()=>f(`config`),className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Back`}),n(`div`,{className:`flex items-center gap-2`,children:[o(`button`,{onClick:()=>{_(e),S(null),T({}),D(``),k(null),j(!1),F(null),f(`picker`)},className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Reset`}),n(`button`,{onClick:()=>{y&&V.mutate({id:H,type:m,displayName:E.trim(),providerId:y.providerId,config:w,enabled:A,nodeId:y.nodeLocal?M:void 0})},disabled:V.isPending||E.trim().length===0,className:`inline-flex items-center gap-1.5 rounded bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50`,children:[V.isPending?o(g,{className:`h-3.5 w-3.5 animate-spin`}):null,V.isPending?`Saving…`:`Save`]})]})]})]})]})]})})}function B(e){return P(e)}function V(e){return e===`active`}var H=[{mode:`active`,label:`Active`,consequence:`New data is written here, and the oldest is deleted when the disk fills up.`,requiresConfirmation:!1},{mode:`readonly`,label:`Read-only`,consequence:`Nothing new is written and nothing is ever deleted. A frozen archive that stays on the timeline.`,requiresConfirmation:!1},{mode:`drain`,label:`Drain`,consequence:`Nothing new is written. The oldest data here is deleted, a little at a time, until this location is empty.`,requiresConfirmation:!1},{mode:`disabled`,label:`Disabled`,consequence:`Nothing is written and nothing is read: the footage stored here disappears from the timeline until you turn it back on. Nothing is deleted.`,requiresConfirmation:!0}];function U(e){return H.find(t=>t.mode===e)??H[0]}function ce(e){return U(e).requiresConfirmation}var le=`Blocked: this is the last location of its type still accepting writes — new data would have nowhere to go.`;function W(e,t,n){return V(n)||!V(B(e))||t.some(t=>t.id!==e.id&&V(B(t)))?{blocked:!1,reason:``}:{blocked:!0,reason:le}}function ue(e,t){return{active:W(e,t,`active`).reason,readonly:W(e,t,`readonly`).reason,drain:W(e,t,`drain`).reason,disabled:W(e,t,`disabled`).reason}}function de(e,t,n){return!(n===`single`||t.length<=1||V(B(e))&&t.filter(e=>V(B(e))).length<=1)}function fe(e){return e===`active`?{enabled:!0,readOnly:!1}:e===`readonly`?{enabled:!1,readOnly:!1}:{enabled:!1,readOnly:!0}}function pe(e,t){let n={...t.config??e.config};t.mode!==void 0&&(fe(t.mode).readOnly?n.readOnly=!0:delete n.readOnly);let r={id:e.id,type:e.type,displayName:t.displayName??e.displayName,providerId:e.providerId,config:n,isSystem:e.isSystem},i=t.nodeId??e.nodeId,a=i===void 0?r:{...r,nodeId:i};return t.mode===void 0?e.enabled===void 0?a:{...a,enabled:e.enabled}:{...a,mode:t.mode,enabled:fe(t.mode).enabled}}var me=1024**3;function G(e){return typeof e==`number`&&Number.isFinite(e)?e:null}function he(e){let t=G(e.minFreeGb);if(t!==null)return{unit:`gb`,value:t};let n=G(e.minFreePercent);return n===null?{unit:`percent`,value:null}:{unit:`percent`,value:n}}function ge(e,t){return G(e[t===`gb`?`minFreeGb`:`minFreePercent`])}function _e(e,t){let n={...e};return delete n.minFreePercent,delete n.minFreeGb,t.value===null?n:t.unit===`gb`?{...n,minFreeGb:t.value}:{...n,minFreePercent:t.value}}function ve(e,t){let n=e.trim();if(n===``)return null;let r=Number(n);return Number.isFinite(r)?t===`percent`?Math.min(100,Math.max(0,r)):Math.max(0,r):null}function ye(e,t,n=10){if(e.unit===`gb`&&e.value!==null)return e.value*me;let r=e.value??n;return t===null||t<=0?null:t*Math.min(100,Math.max(0,r))/100}function be(e){return e===`gb`?`GB to keep free`:`% of the volume to keep free`}function xe(e,t=10){return`CamStack keeps ${e.value===null?`${String(t)}% of the volume (the default)`:e.unit===`gb`?`${String(e.value)} GB`:`${String(e.value)}% of the volume`} free on this disk. As it fills up, the OLDEST data stored here is deleted to hold that much free space; if it cannot be held any more, new data goes to another location of the same type instead of filling the disk. Leave the value empty for the default (${String(t)}%).`}function Se(e){return G(e.maxUsedGb)}function Ce(e,t){let n=G(e.maxUsedGb);return n===null||n<0?null:{bytes:n*me,reason:t===`drain`?`drain`:`legacy`}}t();function we({config:e,defaultPercent:t=10,onChange:r}){let[i,s]=a(()=>he(e).unit),c=ge(e,i),l={unit:i,value:c},u=Se(e),d=t=>{s(t),r(_e(e,{unit:t,value:null}))},f=t=>{r(_e(e,{unit:i,value:ve(t,i)}))};return n(`div`,{className:`space-y-1`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Protected free space`}),n(`div`,{className:`flex items-center gap-2`,children:[n(`select`,{"aria-label":`Protection unit`,value:i,onChange:e=>{d(e.target.value===`gb`?`gb`:`percent`)},className:`rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`,children:[o(`option`,{value:`percent`,children:`% of the volume`}),o(`option`,{value:`gb`,children:`GB of free space`})]}),o(`input`,{type:`number`,"aria-label":be(i),min:0,max:i===`percent`?100:void 0,value:c===null?``:c,placeholder:i===`percent`?String(t):`default`,onChange:e=>{f(e.target.value)},className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`})]}),o(`p`,{className:`text-[10px] text-foreground-subtle`,children:xe(l,t)}),u!==null&&n(`p`,{className:`text-[10px] text-foreground-subtle italic`,children:[`This location also stores a maximum used size of `,u,` GB. That limit is managed by the system now and is no longer editable here; it is kept as it is.`]})]})}t();function Te({location:e,onClose:t,onSaved:s}){let c=i(),l=p().trpcClient,[u,d]=a({...e.config}),[f,m]=a(e.displayName),[_,w]=a(null),[T,E]=a(!1),D=ee(),O=r(()=>{let t=D.data;return Array.isArray(t)?t.find(t=>t.providerId===e.providerId)??null:null},[D.data,e.providerId]),k=ne({onSuccess:()=>{c.invalidateQueries({queryKey:[[`storage`,`listLocations`]]}),s()}}),A=O?.shouldSaveDiskSpace===!0,j=O?.nodeLocal===!0,[M,N]=a(typeof e.nodeId==`string`?e.nodeId:`hub`),P=typeof u.basePath==`string`?u.basePath:``,F=e=>d(t=>({...t,basePath:e})),I=O?.minFreePercent??10,ie=async()=>{E(!0),w(null);try{w(await(e=>l.storage.testConfig.query(e))({providerId:e.providerId,locationId:e.id,config:u}))}catch(e){w({ok:!1,error:e instanceof Error?e.message:String(e)})}finally{E(!1)}};return o(h,{open:!0,onOpenChange:e=>!e&&t(),children:n(x,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,children:[n(v,{children:[n(b,{children:[`Edit `,e.displayName]}),n(`p`,{className:`text-xs text-foreground-subtle`,children:[o(`span`,{className:`font-mono`,children:e.id}),` ·`,` `,O?.displayName??e.providerId]})]}),n(te,{className:`space-y-3`,children:[n(`div`,{className:`space-y-1`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Display name`}),o(`input`,{type:`text`,value:f,onChange:e=>m(e.target.value),className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`})]}),O&&o(re,{schema:O.configSchema,values:u,onChange:d}),j&&n(`div`,{className:`space-y-2`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Node`}),o(C,{selectedNodeId:M,onSelect:N,alwaysShow:!0}),o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Path`}),o(se,{nodeId:M,value:P,onChange:F})]}),A&&o(we,{config:u,defaultPercent:I,onChange:d}),!O&&!D.isLoading&&n(`div`,{className:`rounded border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-[11px] text-amber-700 dark:text-amber-300`,children:[`Provider `,o(`code`,{className:`font-mono`,children:e.providerId}),` is no longer registered. Field schema unavailable; raw config is preserved on save.`]}),_&&o(`div`,{className:`rounded border px-3 py-2 text-[11px] ${_.ok?`border-success/30 bg-success/5 text-success`:`border-danger/30 bg-danger/5 text-danger`}`,children:_.ok?`Connection OK.`:`Test failed: ${_.error??`unknown error`}`}),k.isError&&o(`div`,{className:`rounded border border-danger/30 bg-danger/5 text-danger px-3 py-2 text-[11px]`,children:k.error instanceof Error?k.error.message:String(k.error)})]}),n(S,{className:`justify-between`,children:[o(y,{variant:`outline`,size:`sm`,onClick:t,children:`Cancel`}),n(`div`,{className:`flex items-center gap-2`,children:[o(y,{variant:`outline`,size:`sm`,onClick:()=>{ie()},disabled:T||!O,children:T?`Testing…`:`Test connection`}),n(y,{size:`sm`,onClick:()=>{k.mutate(pe(e,{displayName:f.trim(),config:u,nodeId:j?M:e.nodeId}))},disabled:k.isPending||f.trim().length===0,children:[k.isPending?o(g,{className:`h-3.5 w-3.5 animate-spin`}):null,k.isPending?`Saving…`:`Save`]})]})]})]})})}function Ee(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function K(e){return typeof e==`number`&&Number.isFinite(e)?e:null}function De(e){if(!Ee(e))return null;let t=e.locationId;if(typeof t!=`string`||t.length===0)return null;let n=K(e.startedAtMs),r=K(e.startBytes),i=K(e.bytesRemaining);return n===null||r===null||i===null||typeof e.drained!=`boolean`?null:{locationId:t,startedAtMs:n,startBytes:r,bytesRemaining:i,drained:e.drained,estimatedEmptyAtMs:K(e.estimatedEmptyAtMs)}}function Oe(e){let t=new Map;if(!Array.isArray(e))return t;for(let n of e){let e=De(n);e!==null&&t.set(e.locationId,e)}return t}function ke(e){if(e.startBytes<=0||e.bytesRemaining>e.startBytes)return null;let t=e.startBytes-e.bytesRemaining;return Math.min(1,Math.max(0,t/e.startBytes))}function Ae(e,t){return e?t===void 0?{kind:`unreported`}:t.drained||t.bytesRemaining<=0?{kind:`drained`}:{kind:`draining`,bytesRemaining:t.bytesRemaining,drainedFraction:ke(t),estimatedEmptyAtMs:t.estimatedEmptyAtMs}:{kind:`idle`}}function je(e){return e.mode===`drain`?!0:e.config.readOnly===!0}function Me(e){return`${(e/1024**3).toFixed(e>=100*1024**3?0:1)} GB`}function Ne(e){return new Date(e).toLocaleString(void 0,{dateStyle:`medium`,timeStyle:`short`})}function Pe({drain:e}){if(e.kind===`idle`)return null;if(e.kind===`unreported`)return o(`span`,{"data-testid":`drain-unreported`,className:`text-[10px] text-foreground-subtle`,title:`This location is draining. The hub has not reported how much is left yet — either the drain has just been set, or this hub predates the drain-progress report.`,children:`Draining · remaining not reported yet`});if(e.kind===`drained`)return o(`span`,{"data-testid":`drain-done`,className:`text-[10px] text-success`,title:`Everything that was here has been moved. The location is still in drain mode — it receives no new writes until you change that.`,children:`Drained · nothing left to move`});let t=e.drainedFraction===null?null:Math.round(e.drainedFraction*100);return n(`span`,{"data-testid":`drain-progress`,className:`flex items-center gap-1.5 text-[10px] text-foreground-subtle tabular-nums`,children:[t!==null&&o(`span`,{"data-testid":`drain-bar`,className:`inline-block h-1 w-10 rounded-full bg-border overflow-hidden`,role:`progressbar`,"aria-label":`Drain progress`,"aria-valuenow":t,"aria-valuemin":0,"aria-valuemax":100,children:o(`span`,{className:`block h-full bg-warning`,style:{width:`${t}%`}})}),n(`span`,{children:[Me(e.bytesRemaining),` left`,t!==null&&` · ${t}% moved`]}),e.estimatedEmptyAtMs===null?o(`span`,{"data-testid":`drain-eta-absent`,title:`The drain is paced by how fast this class of data is still growing, and that has not been observed yet. There is no date to give.`,children:`· empty time not known yet`}):n(`span`,{"data-testid":`drain-eta`,children:[`· empty `,Ne(e.estimatedEmptyAtMs)]})]})}var Fe=[`ok`,`warn`,`critical`];function Ie(e){return e>=.95?`critical`:e>=.85?`warn`:`ok`}function Le(e){return Math.min(1,Math.max(0,e))}function q(e){let{availableBytes:t,totalBytes:n}=e;if(n===null||n<=0||t===null)return null;let r=Le((n-t)/n);return{fraction:r,severity:Ie(r),recordingsFraction:Le(e.usedBytes/n),freeBytes:t,totalBytes:n}}function Re(e){if(e.length===0)return null;let t=0,n=0,r=0,i=0,a=null;for(let o of e){let e=q(o);if(e===null){n+=1;continue}t+=1,r+=e.totalBytes,i+=e.freeBytes,(a===null||Fe.indexOf(e.severity)>Fe.indexOf(a))&&(a=e.severity)}return{knownVolumes:t,unknownVolumes:n,totalBytes:r,freeBytes:i,severity:a}}var ze=`Capacity unknown — nothing measured this volume from here. A remote share, a location on another node, or a mount that did not answer.`;function J(e){return Math.min(1,Math.max(0,e))}function Y(e){return Math.round(J(e)*1e3)/10}function X(e){return`${String(Math.round(J(e)*100))}%`}function Be(e,t,n={}){let{totalBytes:r,availableBytes:i}=e;if(r===null||r<=0||i===null){let e=n.ownedBytes===void 0||n.ownedBytes===null||!Number.isFinite(n.ownedBytes)||n.ownedBytes<0?null:N(n.ownedBytes);return{kind:`unknown`,owned:e,summary:e===null?ze:`${ze} CamStack holds ${e} here.`}}let a=Math.min(Math.max(i,0),r),o=r-a,s=t===null||t<=0?0:t,c=Math.min(s,a),l=s>a,u=a-c,d=n.ownedBytes,f=d==null||!Number.isFinite(d)||d<0?null:Math.min(d,o),p=o-(f??0),m=q({usedBytes:f??o,availableBytes:a,totalBytes:r}),h=m===null?J(o/r):m.fraction,g=m===null?Ie(h):m.severity,_=f===null?null:m===null?J(f/r):m.recordingsFraction,v=n.cap??null,y=v===null?null:J((c+v.bytes)/r),b=v!==null&&f!==null&&f>v.bytes,x={total:N(r),freeTotal:N(a),freeTotalPercent:X(a/r),used:N(p),usedPercent:X(p/r),free:N(u),freePercent:X(u/r),reserve:s===0?null:`${N(s)} kept free`,owned:f===null||_===null?null:`${N(f)} of recordings (${X(_)})`,cap:v===null?null:v.reason===`drain`?`${N(v.bytes)} drain target (managed by CamStack)`:`${N(v.bytes)} cap (managed by CamStack)`},S=[`${x.freeTotal} free of ${x.total} (${x.freeTotalPercent} free)`,x.owned===null?null:`CamStack holds ${x.owned}`,x.reserve===null?null:l?`${x.reserve} — less free than that right now, the oldest data here is being deleted`:x.reserve,x.cap===null?null:b?`${x.cap} — already below what is stored here, the oldest is being deleted`:x.cap].filter(e=>e!==null).join(` · `),C=[];return c>0&&C.push({segment:`reserve`,label:`Kept free`,value:N(s),alarming:l}),x.owned!==null&&C.push({segment:`owned`,label:`CamStack`,value:x.owned,alarming:!1}),C.push({segment:`used`,label:f===null?`Used`:`Other data`,value:`${x.used} (${x.usedPercent})`,alarming:g===`critical`}),C.push({segment:`free`,label:`Free`,value:`${x.free} (${x.freePercent})`,alarming:l}),v!==null&&C.push({segment:`cap`,label:v.reason===`drain`?`Drain target`:`Cap`,value:`${N(v.bytes)} · managed by CamStack`,alarming:b}),{kind:`known`,severity:g,legend:C,segments:{reservePercent:Y(c/r),ownedPercent:f===null?null:Y(f/r),usedPercent:Y(p/r),freePercent:Y(u/r),capPercent:y===null?null:Y(y)},labels:x,reserveExceedsFree:l,capBelowOwned:b,summary:S}}var Ve={ok:`bg-primary`,warn:`bg-amber-500`,critical:`bg-danger`},Z={reserve:`bg-danger/40`,owned:`bg-foreground/50`,free:`bg-border`,cap:`bg-foreground`};function He(e,t){return e===`used`?Ve[t]:Z[e]}function Ue({location:e,ownedBytes:t}){let r=e.capacity??null,i=ye(he(e.config),r?.totalBytes??null),a=Be({totalBytes:r?.totalBytes??null,availableBytes:r?.availableBytes??null},i,{ownedBytes:t??null,cap:Ce(e.config,B(e))});return a.kind===`unknown`?o(`div`,{className:`text-[10px] text-foreground-subtle italic`,title:a.summary,children:a.owned===null?`Capacity unknown`:`Capacity unknown · CamStack holds ${a.owned}`}):n(`div`,{className:`mt-1 space-y-1`,title:a.summary,children:[n(`div`,{className:`relative flex h-2 w-full max-w-md overflow-hidden rounded-full bg-border`,children:[a.segments.reservePercent>0&&o(`div`,{className:Z.reserve,style:{width:`${String(a.segments.reservePercent)}%`}}),a.segments.ownedPercent!==null&&o(`div`,{className:Z.owned,style:{width:`${String(a.segments.ownedPercent)}%`}}),o(`div`,{className:Ve[a.severity],style:{width:`${String(a.segments.usedPercent)}%`}}),o(`div`,{className:Z.free,style:{width:`${String(a.segments.freePercent)}%`}}),a.segments.capPercent!==null&&o(`div`,{className:`absolute inset-y-0 w-px ${a.capBelowOwned?`bg-danger`:Z.cap}`,style:{left:`${String(a.segments.capPercent)}%`}})]}),o(`ul`,{className:`flex flex-wrap items-center gap-x-3 gap-y-0.5`,children:a.legend.map(e=>n(`li`,{className:`flex items-center gap-1`,children:[o(`span`,{"aria-hidden":`true`,className:`inline-block h-2 w-2 shrink-0 rounded-sm ${He(e.segment,a.severity)}`}),n(`span`,{className:`text-[10px] tabular-nums ${e.alarming?`text-danger`:`text-foreground-subtle`}`,children:[e.label,` `,e.value]})]},e.segment))})]})}var We={eventMedia:[`events`],recordingsLow:[`low`],recordings:[`high`,`mid`]},Ge={eventMedia:`pipeline-analytics`,recordingsLow:`recorder`,recordings:`recorder`},Ke=`storage.discardLocation`,qe=`storage.discardLocationStatus`,Je=`storage.discardLocationCancel`;function Ye(e){return We[e]??null}function Xe(e){return Ge[e]??null}function Ze(e){return!V(B(e))}function Qe(e,t){return Ye(e.type)===null||!Ze(e)?!1:t.some(t=>t.id!==e.id&&V(B(t)))}var $e=new Set([`idle`,`scanning`,`wiping-fs`,`wiping-db`,`cascading`,`complete`,`cancelled`,`error`]);function Q(e){return typeof e==`number`&&Number.isFinite(e)?e:null}function $(e){return typeof e==`string`?e:null}function et(e){if(typeof e!=`object`||!e)return null;let t=e;if(typeof t.running!=`boolean`)return null;let n=t.phase;if(typeof n!=`string`||!$e.has(n))return null;let r=Q(t.camerasTotal),i=Q(t.camerasDone),a=Q(t.foldersRemoved),o=Q(t.dbRowsDeleted),s=Q(t.ownersCascaded);return r===null||i===null||a===null||o===null||s===null?null:{running:t.running,locationId:$(t.locationId),jobId:$(t.jobId),phase:n,camerasTotal:r,camerasDone:i,foldersRemoved:a,dbRowsDeleted:o,ownersCascaded:s,detail:$(t.detail)??``,startedAtMs:Q(t.startedAtMs),finishedAtMs:Q(t.finishedAtMs),error:$(t.error)}}function tt(e){if(e.phase===`complete`)return 100;if(e.phase===`cancelled`||e.phase===`error`||e.phase===`idle`)return e.camerasTotal>0?Math.min(99,Math.round(e.camerasDone/e.camerasTotal*100)):0;if(e.camerasTotal>0){let t=e.camerasDone/e.camerasTotal*85;return e.phase===`wiping-db`?Math.min(95,Math.round(t+8)):e.phase===`cascading`?Math.min(99,Math.round(t+12)):Math.min(85,Math.round(t))}return e.phase===`scanning`?2:5}t();var nt=2e3;function rt({location:e,siblings:t}){return Qe(e,t)?o(it,{location:e}):null}function it({location:e}){let t=k(),r=A(),i=Xe(e.type),[c,l]=a(null),[u,d]=a(null),[f,p]=a(!1);if(s(()=>{if(i===null)return;let e=!1,t=async()=>{try{let t=await r.mutateAsync({addonId:i,action:qe,input:{}});if(e)return;l(et(t))}catch{}};t();let n=setInterval(()=>void t(),nt);return()=>{e=!0,clearInterval(n)}},[i,e.id]),i===null)return null;let m=c!==null&&c.locationId===e.id,h=m&&c.running,_=m&&c.phase!==`idle`,v=async()=>{if(await t({title:`Discard leftovers on "${e.displayName}"?`,message:`Permanently deletes leftover files and records on this location. Other locations are not affected. This cannot be undone and may take hours.`,confirmLabel:`Discard leftovers`,variant:`danger`})){d(null),p(!0);try{await r.mutateAsync({addonId:i,action:Ke,input:{locationId:e.id}}),l(et(await r.mutateAsync({addonId:i,action:qe,input:{}})))}catch(e){d(e instanceof Error?e.message:String(e))}finally{p(!1)}}},y=async()=>{try{await r.mutateAsync({addonId:i,action:Je,input:{}})}catch(e){d(e instanceof Error?e.message:String(e))}};return n(`div`,{className:`flex flex-col gap-1 min-w-[9rem] max-w-[16rem]`,children:[h?n(`button`,{type:`button`,onClick:()=>void y(),className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-danger border border-danger/40 hover:bg-danger/10`,children:[o(O,{className:`h-3 w-3`}),`Cancel`]}):n(`button`,{type:`button`,onClick:()=>void v(),disabled:f,title:`Permanently delete leftover files on this location. Other locations are not affected.`,className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-danger border border-danger/40 hover:bg-danger/10 disabled:opacity-50`,children:[f?o(g,{className:`h-3 w-3 animate-spin`}):o(F,{className:`h-3 w-3`}),`Discard leftovers`]}),_&&c!==null&&n(`div`,{className:`space-y-0.5`,children:[o(`div`,{className:`h-1 rounded bg-border overflow-hidden`,children:o(`div`,{className:`h-full bg-primary transition-[width] duration-300`,style:{width:`${tt(c)}%`}})}),n(`p`,{className:`text-[10px] text-foreground-subtle leading-tight`,children:[c.phase,c.camerasTotal>0?` · ${c.camerasDone}/${c.camerasTotal} cam`:``,c.dbRowsDeleted>0?` · ${c.dbRowsDeleted} rows`:``,c.detail?` · ${c.detail}`:``]})]}),(u||m&&c?.error)&&o(`p`,{className:`text-[10px] text-danger leading-tight`,children:u??c?.error})]})}t();var at={active:E,readonly:j,drain:M,disabled:f},ot={active:`border-success/30 text-success hover:bg-success/10`,readonly:`border-border text-foreground-subtle hover:text-foreground hover:bg-primary/10`,drain:`border-warning/40 text-warning hover:bg-warning/10`,disabled:`border-danger/40 text-danger hover:bg-danger/10`};function st({mode:e,open:t,onToggle:r}){let i=U(e),a=at[e];return n(`button`,{type:`button`,"aria-expanded":t,"aria-label":`Location state`,onClick:r,title:i.consequence,className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium border ${ot[e]}`,children:[o(a,{className:`h-3 w-3`}),i.label,o(l,{className:`h-3 w-3 ${t?`rotate-180`:``}`})]})}function ct({mode:e,blockedReasons:t,onSelect:r,onDismiss:i}){return s(()=>{let e=e=>{e.key===`Escape`&&i()};return window.addEventListener(`keydown`,e),()=>{window.removeEventListener(`keydown`,e)}},[i]),n(`div`,{className:`w-full basis-full rounded-lg border border-border bg-background p-1`,role:`group`,"aria-label":`Location state choices`,children:[n(`div`,{className:`flex items-center justify-between px-2 py-1`,children:[o(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Location state`}),o(`button`,{type:`button`,"aria-label":`Close location state choices`,onClick:i,className:`text-foreground-subtle hover:text-foreground`,children:o(O,{className:`h-3 w-3`})})]}),H.map(a=>{let s=t[a.mode],c=s!==``,l=at[a.mode],u=a.mode===e;return n(`button`,{type:`button`,role:`radio`,"aria-checked":u,"aria-label":a.label,disabled:c,onClick:()=>{c||(i(),u||r(a.mode))},className:`flex w-full flex-col items-start gap-0.5 rounded px-2 py-1.5 text-left ${c?`cursor-not-allowed opacity-60`:u?`bg-primary/10`:`hover:bg-primary/5`}`,children:[n(`span`,{className:`flex items-center gap-1.5 text-[11px] font-medium text-foreground`,children:[o(l,{className:`h-3 w-3`}),a.label,u&&o(`span`,{className:`text-[9px] uppercase text-primary`,children:`current`})]}),o(`span`,{className:`text-[10px] leading-snug text-foreground-subtle`,children:a.consequence}),c&&o(`span`,{className:`text-[10px] leading-snug text-warning`,children:s})]},a.mode)})]})}t();function lt(e){let t=e.basePath;if(typeof t==`string`&&t.length>0)return t;let n=Object.keys(e);return n.length===0?``:n.join(`, `)}function ut({location:e,onEdit:t,onDelete:r,onSetMode:i,blockedReasons:s,canDelete:c,siblings:l=[],drain:d,ownedBytes:f}){let[p,m]=a(!1),h=lt(e.config),g=B(e);return n(`div`,{className:`flex flex-wrap items-start gap-x-3 gap-y-2 px-3 py-2 border-t border-border first:border-t-0 hover:bg-primary/5`,children:[n(`div`,{className:`flex items-start gap-2 min-w-0 flex-1 basis-72`,children:[o(`div`,{className:`shrink-0 sm:w-32`,"data-testid":`location-badge-slot`,children:o(`span`,{className:`inline-block text-[10px] uppercase tracking-wide text-foreground-subtle bg-background border border-border rounded px-1.5 py-0.5 font-mono`,children:e.providerId})}),n(`div`,{className:`min-w-0 flex-1`,children:[o(`div`,{className:`text-xs font-medium text-foreground truncate`,children:e.displayName}),h&&o(`div`,{className:`text-[10px] text-foreground-subtle truncate font-mono`,children:h}),o(Ue,{location:e,ownedBytes:f??null}),d!==void 0&&o(Pe,{drain:d})]})]}),n(`div`,{className:`flex flex-wrap items-center gap-1 shrink-0`,children:[o(rt,{location:e,siblings:l}),o(st,{mode:g,open:p,onToggle:()=>{m(e=>!e)}}),n(`button`,{onClick:()=>t(e),className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-foreground-subtle border border-border hover:text-foreground hover:bg-primary/10`,children:[o(D,{className:`h-3 w-3`}),`Edit`]}),c&&o(`button`,{onClick:()=>r(e),className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-danger hover:bg-danger/10`,title:`Delete location`,children:o(u,{className:`h-3 w-3`})})]}),p&&o(ct,{mode:g,blockedReasons:s,onSelect:t=>i(e,t),onDismiss:()=>{m(!1)}})]})}t();function dt({type:e,displayName:t,description:i,cardinality:a,locations:s,onAdd:l,onEdit:u,onDelete:d,onSetMode:f,ownedBytesById:p,drainById:m}){let h=r(()=>{let e=[...s];return e.sort((e,t)=>{let n=V(B(e));return n===V(B(t))?e.displayName.localeCompare(t.displayName):n?-1:1}),e},[s]);return n(w,{headingId:`storage-location-${e}`,title:t,icon:_,testId:`storage-location-group-${e}`,actions:a===`multi`?n(y,{size:`sm`,type:`button`,onClick:()=>l(e),children:[o(T,{className:`h-3 w-3`}),`Add`]}):void 0,children:[n(`div`,{className:`${c} space-y-2`,children:[n(`div`,{className:`space-y-0.5`,children:[o(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle bg-background border border-border rounded px-1.5 py-0.5 font-mono`,children:e}),i!==void 0&&i!==``&&o(`p`,{className:`text-[11px] leading-relaxed text-foreground-subtle`,children:i}),a===`single`&&o(`p`,{className:`text-[10px] text-foreground-subtle italic`,children:`Single — one location per type. Edit the existing entry; it can't be split.`})]}),h.length===0&&o(`div`,{className:`py-2 text-center text-[11px] text-foreground-subtle italic`,children:a===`multi`?`No location registered. Click "Add" to register one.`:`No location registered yet — should be auto-seeded at next boot.`})]}),h.length>0&&o(`div`,{className:`border-t border-border`,children:h.map(e=>o(ut,{location:e,cardinality:a,canDelete:de(e,h,a),onEdit:u,onDelete:d,onSetMode:f,blockedReasons:ue(e,h),ownedBytes:p?.get(e.id)??null,drain:Ae(je(e),m?.get(e.id)),siblings:h},e.id))})]})}var ft=[`recordings`,`recordingsLow`];function pt(e){return e.locationIds!==void 0&&e.locationIds.length>0?e.locationIds:e.locationId===null?[]:[e.locationId]}function mt(e,t){if(t===void 0||!ft.includes(e.type))return null;let n=t.find(t=>pt(t).includes(e.id));return n===void 0||!Number.isFinite(n.usedBytes)||n.usedBytes<0?null:n.usedBytes}function ht(e,t){let n=new Map;for(let r of e){let e=mt(r,t);e!==null&&n.set(r.id,e)}return n}export{Oe as a,U as c,Re as i,ce as l,dt as n,Te as o,q as r,pe as s,ht as t,z as u};
1
+ import{I as e,Q as t,S as n,W as r,o as i,q as a,x as o,z as s}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{D as c,_t as l,ct as u,gt as d,ht as f,ot as p,yt as m}from"./player-overlays-9CMoB0XN.js";import{An as h,Ed as g,Fd as _,Fn as v,Gn as y,In as b,Mn as x,Pn as S,U as C,Vt as w,Wc as ee,dd as T,jn as te,ld as E,md as D,mu as O,nl as ne,nr as k,wr as A}from"./src-Ca4gNAmP.js";import{t as j}from"./archive-D3wNUMUT.js";import{n as M,t as N}from"./format-size-CI1h5otl.js";import{ei as P}from"./client-1JBRoe76.js";import{I as re}from"./index-B-BE8EYQ.js";var F=m(`eraser`,[[`path`,{d:`M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21`,key:`g5wo59`}],[`path`,{d:`m5.082 11.09 8.828 8.828`,key:`1wx5vj`}]]);t();function I(e){let t=e.trim().toLowerCase();return t.length===0?``:t.replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)}function ie(e,t){let n=I(t);return`${e}:${n.length>0?n:`default`}`}var ae=new Set([`proc`,`sys`,`dev`,`run`,`boot`,`lost+found`,`snap`,`bin`,`sbin`,`lib`,`lib32`,`lib64`,`libx32`,`usr`,`etc`,`var`,`opt`,`tmp`,`srv`,`root`,`System`,`Library`,`Applications`,`cores`,`private`]),oe=[`recordings`,`data`,`mnt`,`media`,`Volumes`];function L(e){let t=oe.findIndex(t=>t===`recordings`?e.startsWith(t):e===t);return t===-1?oe.length:t}function R(e){return e.filter(e=>!e.name.startsWith(`.`)&&!ae.has(e.name)).toSorted((e,t)=>{let n=L(e.name)-L(t.name);return n===0?e.name.localeCompare(t.name):n}).map(e=>e.path)}function se({nodeId:t,value:r,onChange:i}){let c=p().trpcClient,[l,u]=a([]),[d,f]=a(r),[m,h]=a(null),[g,_]=a(null),[v,y]=a(``),b=e(async(e,n)=>{_(null);try{let r=await c.filesystemBrowse.browse.query({nodeId:t,path:e});h(r),f(r.path),n&&i(r.path)}catch(e){_(e instanceof Error?e.message:`browse failed`)}},[c,t,i]);s(()=>{f(r),(async()=>{let e=await c.filesystemBrowse.browse.query({nodeId:t,path:`/`}).then(e=>e).catch(()=>null);if(e){u(R(e.entries)),await b(r.startsWith(`/`)?r:`/`,!1);return}try{let e=await c.filesystemBrowse.listAllowedRoots.query({nodeId:t});u(e),await b(r&&e.some(e=>r.startsWith(e))?r:e[0]??``,!1)}catch(e){u([]),h(null),_(e instanceof Error&&/not available on node/i.test(e.message)?`This node can't be browsed remotely. Enter the path manually.`:e instanceof Error?e.message:`browse unavailable`)}})()},[t]);let x=async()=>{if(!(!v.trim()||!m)){_(null);try{let e=`${m.path.replace(/\/+$/,``)}/${v.trim()}`;await c.filesystemBrowse.createDir.mutate({nodeId:t,path:e}),y(``),await b(m.path,!0)}catch(e){_(e instanceof Error?e.message:`create failed`)}}},S=e=>`${(e/1e9).toFixed(1)} GB`;return n(`div`,{className:`space-y-2 rounded border border-border p-2`,children:[o(`div`,{className:`flex flex-wrap gap-1`,children:l.map(e=>o(`button`,{onClick:()=>void b(e,!0),className:`rounded border border-border px-2 py-0.5 text-[10px] font-mono hover:border-primary/40`,children:e},e))}),o(`input`,{value:d,onChange:e=>{f(e.target.value),i(e.target.value)},placeholder:`/absolute/path`,className:`w-full rounded border border-border bg-background px-2 py-1 text-[11px] font-mono`}),m&&n(`div`,{className:`text-[10px] text-foreground-subtle`,children:[S(m.freeBytes),` free / `,S(m.totalBytes)]}),o(`div`,{className:`max-h-40 space-y-0.5 overflow-y-auto`,children:m?.entries.map(e=>n(`button`,{onClick:()=>void b(e.path,!0),className:`block w-full truncate rounded px-2 py-1 text-left text-xs hover:bg-primary/5`,children:[`📁 `,e.name]},e.path))}),n(`div`,{className:`flex gap-1`,children:[o(`input`,{value:v,onChange:e=>y(e.target.value),placeholder:`new folder`,className:`flex-1 rounded border border-border bg-background px-2 py-1 text-xs`}),o(`button`,{onClick:()=>void x(),className:`rounded border border-border px-2 py-1 text-xs hover:border-primary/40`,children:`Create`})]}),g&&o(`div`,{className:`text-[10px] text-red-400`,children:g})]})}t();function z({initialType:e,onClose:t,onCreated:s}){let c=i(),l=p().trpcClient,[u,f]=a(`picker`),[m,_]=a(e),[y,S]=a(null),[w,T]=a({}),[E,D]=a(``),[O,k]=a(null),[A,j]=a(!1),[M,N]=a(`hub`),[P,F]=a(null),[I,ae]=a(!1),oe=e=>T(t=>({...t,basePath:e})),L=typeof w.basePath==`string`?w.basePath:``,R=ee(),z=r(()=>{let e=R.data;return Array.isArray(e)?e:[]},[R.data]),B=R.isLoading,V=ne({onSuccess:()=>{c.invalidateQueries({queryKey:[[`storage`,`listLocations`]]}),s()}}),H=r(()=>O!==null&&O.length>0?O:ie(m,E),[m,E,O]),U=async()=>{if(y){ae(!0),F(null);try{F(await(e=>l.storage.testConfig.query(e))({providerId:y.providerId,config:w}))}catch(e){F({ok:!1,error:e instanceof Error?e.message:String(e)})}finally{ae(!1)}}};return o(h,{open:!0,onOpenChange:e=>!e&&t(),children:n(x,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,children:[n(v,{children:[o(b,{children:{picker:`Add Location — Pick provider`,config:`Add Location — ${y?.displayName??``}`,naming:`Add Location — Name`}[u]}),n(`p`,{className:`text-xs text-foreground-subtle`,children:[`Type: `,o(`span`,{className:`font-mono`,children:m})]})]}),n(te,{children:[u===`picker`&&n(`div`,{className:`space-y-2`,children:[B&&o(`div`,{className:`text-xs text-foreground-subtle animate-pulse`,children:`Loading providers…`}),!B&&z.length===0&&n(`div`,{className:`rounded border border-border bg-background px-3 py-4 text-center text-xs text-foreground-subtle`,children:[`No storage-provider supports type "`,m,`". Install a compatible addon (e.g.`,` `,o(`code`,{className:`font-mono`,children:`remote-storage`}),`) to enable it.`]}),z.map(e=>n(`button`,{onClick:()=>{S(e),f(`config`)},className:`flex w-full items-center gap-3 rounded-lg border border-border bg-background p-3 text-left hover:border-primary/40 hover:bg-primary/5 transition-colors`,children:[n(`div`,{className:`flex-1 min-w-0`,children:[o(`p`,{className:`text-xs font-semibold text-foreground`,children:e.displayName}),o(`p`,{className:`text-[10px] text-foreground-subtle font-mono mt-0.5 truncate`,children:e.providerId})]}),o(d,{className:`h-4 w-4 text-foreground-subtle flex-shrink-0`})]},e.providerId))]}),u===`config`&&y&&n(`div`,{className:`space-y-3`,children:[o(re,{schema:y.configSchema,values:w,onChange:T}),y.nodeLocal&&n(`div`,{className:`space-y-2`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Node`}),o(C,{selectedNodeId:M,onSelect:N,alwaysShow:!0}),o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Path`}),o(se,{nodeId:M,value:L,onChange:oe})]}),P&&o(`div`,{className:`rounded border px-3 py-2 text-[11px] ${P.ok?`border-success/30 bg-success/5 text-success`:`border-danger/30 bg-danger/5 text-danger`}`,children:P.ok?`Connection OK.`:`Test failed: ${P.error??`unknown error`}`}),n(`div`,{className:`flex items-center justify-between pt-1`,children:[o(`button`,{onClick:()=>f(`picker`),className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Back`}),n(`div`,{className:`flex items-center gap-2`,children:[o(`button`,{onClick:()=>{U()},disabled:I,className:`rounded border border-border px-3 py-1.5 text-xs text-foreground hover:bg-primary/10 disabled:opacity-50`,children:I?`Testing…`:`Test connection`}),o(`button`,{onClick:()=>f(`naming`),className:`rounded bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90`,children:`Next`})]})]})]}),u===`naming`&&y&&n(`div`,{className:`space-y-3`,children:[n(`div`,{className:`space-y-1`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Display name`}),o(`input`,{type:`text`,value:E,onChange:e=>D(e.target.value),placeholder:`${m} on ${y.displayName}`,className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`})]}),n(`div`,{className:`space-y-1`,children:[n(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:[`Id`,` `,o(`span`,{className:`lowercase tracking-normal text-foreground-subtle`,children:`— auto-derived; override if needed`})]}),o(`input`,{type:`text`,value:H,onChange:e=>k(e.target.value),placeholder:ie(m,E),className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground font-mono focus:outline-none focus:ring-1 focus:ring-primary`}),n(`p`,{className:`text-[10px] text-foreground-subtle`,children:[`Format: `,o(`code`,{className:`font-mono`,children:`<type>:<slug>`}),`. Lowercase letters, digits, and hyphens only.`]})]}),n(`label`,{className:`flex items-start gap-2 rounded border border-border bg-background px-3 py-2 cursor-pointer hover:bg-primary/5`,children:[o(`input`,{type:`checkbox`,checked:A,onChange:e=>j(e.target.checked),className:`accent-primary mt-0.5`}),n(`div`,{className:`space-y-0.5`,children:[o(`div`,{className:`text-xs font-medium text-foreground`,children:`Enable immediately`}),o(`div`,{className:`text-[10px] text-foreground-subtle`,children:`A disabled location receives no writes until enabled — reads and age-based retention still work, nothing new lands here. Leave this off to register the disk now and start using it later.`})]})]}),V.isError&&o(`div`,{className:`rounded border border-danger/30 bg-danger/5 text-danger px-3 py-2 text-[11px]`,children:V.error instanceof Error?V.error.message:String(V.error)}),n(`div`,{className:`flex items-center justify-between pt-1`,children:[o(`button`,{onClick:()=>f(`config`),className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Back`}),n(`div`,{className:`flex items-center gap-2`,children:[o(`button`,{onClick:()=>{_(e),S(null),T({}),D(``),k(null),j(!1),F(null),f(`picker`)},className:`rounded px-3 py-1.5 text-xs text-foreground-subtle border border-border hover:text-foreground`,children:`Reset`}),n(`button`,{onClick:()=>{y&&V.mutate({id:H,type:m,displayName:E.trim(),providerId:y.providerId,config:w,enabled:A,nodeId:y.nodeLocal?M:void 0})},disabled:V.isPending||E.trim().length===0,className:`inline-flex items-center gap-1.5 rounded bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50`,children:[V.isPending?o(g,{className:`h-3.5 w-3.5 animate-spin`}):null,V.isPending?`Saving…`:`Save`]})]})]})]})]})]})})}function B(e){return P(e)}function V(e){return e===`active`}var H=[{mode:`active`,label:`Active`,consequence:`New data is written here, and the oldest is deleted when the disk fills up.`,requiresConfirmation:!1},{mode:`readonly`,label:`Read-only`,consequence:`Nothing new is written and nothing is ever deleted. A frozen archive that stays on the timeline.`,requiresConfirmation:!1},{mode:`drain`,label:`Drain`,consequence:`Nothing new is written. The oldest data here is deleted, a little at a time, until this location is empty.`,requiresConfirmation:!1},{mode:`disabled`,label:`Disabled`,consequence:`Nothing is written and nothing is read: the footage stored here disappears from the timeline until you turn it back on. Nothing is deleted.`,requiresConfirmation:!0}];function U(e){return H.find(t=>t.mode===e)??H[0]}function ce(e){return U(e).requiresConfirmation}var le=`Blocked: this is the last location of its type still accepting writes — new data would have nowhere to go.`;function W(e,t,n){return V(n)||!V(B(e))||t.some(t=>t.id!==e.id&&V(B(t)))?{blocked:!1,reason:``}:{blocked:!0,reason:le}}function ue(e,t){return{active:W(e,t,`active`).reason,readonly:W(e,t,`readonly`).reason,drain:W(e,t,`drain`).reason,disabled:W(e,t,`disabled`).reason}}function de(e,t,n){return!(n===`single`||t.length<=1||V(B(e))&&t.filter(e=>V(B(e))).length<=1)}function fe(e){return e===`active`?{enabled:!0,readOnly:!1}:e===`readonly`?{enabled:!1,readOnly:!1}:{enabled:!1,readOnly:!0}}function pe(e,t){let n={...t.config??e.config};t.mode!==void 0&&(fe(t.mode).readOnly?n.readOnly=!0:delete n.readOnly);let r={id:e.id,type:e.type,displayName:t.displayName??e.displayName,providerId:e.providerId,config:n,isSystem:e.isSystem},i=t.nodeId??e.nodeId,a=i===void 0?r:{...r,nodeId:i};return t.mode===void 0?e.enabled===void 0?a:{...a,enabled:e.enabled}:{...a,mode:t.mode,enabled:fe(t.mode).enabled}}var me=1024**3;function G(e){return typeof e==`number`&&Number.isFinite(e)?e:null}function he(e){let t=G(e.minFreeGb);if(t!==null)return{unit:`gb`,value:t};let n=G(e.minFreePercent);return n===null?{unit:`percent`,value:null}:{unit:`percent`,value:n}}function ge(e,t){return G(e[t===`gb`?`minFreeGb`:`minFreePercent`])}function _e(e,t){let n={...e};return delete n.minFreePercent,delete n.minFreeGb,t.value===null?n:t.unit===`gb`?{...n,minFreeGb:t.value}:{...n,minFreePercent:t.value}}function ve(e,t){let n=e.trim();if(n===``)return null;let r=Number(n);return Number.isFinite(r)?t===`percent`?Math.min(100,Math.max(0,r)):Math.max(0,r):null}function ye(e,t,n=10){if(e.unit===`gb`&&e.value!==null)return e.value*me;let r=e.value??n;return t===null||t<=0?null:t*Math.min(100,Math.max(0,r))/100}function be(e){return e===`gb`?`GB to keep free`:`% of the volume to keep free`}function xe(e,t=10){return`CamStack keeps ${e.value===null?`${String(t)}% of the volume (the default)`:e.unit===`gb`?`${String(e.value)} GB`:`${String(e.value)}% of the volume`} free on this disk. As it fills up, the OLDEST data stored here is deleted to hold that much free space; if it cannot be held any more, new data goes to another location of the same type instead of filling the disk. Leave the value empty for the default (${String(t)}%).`}function Se(e){return G(e.maxUsedGb)}function Ce(e,t){let n=G(e.maxUsedGb);return n===null||n<0?null:{bytes:n*me,reason:t===`drain`?`drain`:`legacy`}}t();function we({config:e,defaultPercent:t=10,onChange:r}){let[i,s]=a(()=>he(e).unit),c=ge(e,i),l={unit:i,value:c},u=Se(e),d=t=>{s(t),r(_e(e,{unit:t,value:null}))},f=t=>{r(_e(e,{unit:i,value:ve(t,i)}))};return n(`div`,{className:`space-y-1`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Protected free space`}),n(`div`,{className:`flex items-center gap-2`,children:[n(`select`,{"aria-label":`Protection unit`,value:i,onChange:e=>{d(e.target.value===`gb`?`gb`:`percent`)},className:`rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`,children:[o(`option`,{value:`percent`,children:`% of the volume`}),o(`option`,{value:`gb`,children:`GB of free space`})]}),o(`input`,{type:`number`,"aria-label":be(i),min:0,max:i===`percent`?100:void 0,value:c===null?``:c,placeholder:i===`percent`?String(t):`default`,onChange:e=>{f(e.target.value)},className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`})]}),o(`p`,{className:`text-[10px] text-foreground-subtle`,children:xe(l,t)}),u!==null&&n(`p`,{className:`text-[10px] text-foreground-subtle italic`,children:[`This location also stores a maximum used size of `,u,` GB. That limit is managed by the system now and is no longer editable here; it is kept as it is.`]})]})}t();function Te({location:e,onClose:t,onSaved:s}){let c=i(),l=p().trpcClient,[u,d]=a({...e.config}),[f,m]=a(e.displayName),[_,w]=a(null),[T,E]=a(!1),D=ee(),O=r(()=>{let t=D.data;return Array.isArray(t)?t.find(t=>t.providerId===e.providerId)??null:null},[D.data,e.providerId]),k=ne({onSuccess:()=>{c.invalidateQueries({queryKey:[[`storage`,`listLocations`]]}),s()}}),A=O?.shouldSaveDiskSpace===!0,j=O?.nodeLocal===!0,[M,N]=a(typeof e.nodeId==`string`?e.nodeId:`hub`),P=typeof u.basePath==`string`?u.basePath:``,F=e=>d(t=>({...t,basePath:e})),I=O?.minFreePercent??10,ie=async()=>{E(!0),w(null);try{w(await(e=>l.storage.testConfig.query(e))({providerId:e.providerId,locationId:e.id,config:u}))}catch(e){w({ok:!1,error:e instanceof Error?e.message:String(e)})}finally{E(!1)}};return o(h,{open:!0,onOpenChange:e=>!e&&t(),children:n(x,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,children:[n(v,{children:[n(b,{children:[`Edit `,e.displayName]}),n(`p`,{className:`text-xs text-foreground-subtle`,children:[o(`span`,{className:`font-mono`,children:e.id}),` ·`,` `,O?.displayName??e.providerId]})]}),n(te,{className:`space-y-3`,children:[n(`div`,{className:`space-y-1`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Display name`}),o(`input`,{type:`text`,value:f,onChange:e=>m(e.target.value),className:`w-full rounded border border-border bg-background px-2 py-1.5 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-primary`})]}),O&&o(re,{schema:O.configSchema,values:u,onChange:d}),j&&n(`div`,{className:`space-y-2`,children:[o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Node`}),o(C,{selectedNodeId:M,onSelect:N,alwaysShow:!0}),o(`label`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Path`}),o(se,{nodeId:M,value:P,onChange:F})]}),A&&o(we,{config:u,defaultPercent:I,onChange:d}),!O&&!D.isLoading&&n(`div`,{className:`rounded border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-[11px] text-amber-700 dark:text-amber-300`,children:[`Provider `,o(`code`,{className:`font-mono`,children:e.providerId}),` is no longer registered. Field schema unavailable; raw config is preserved on save.`]}),_&&o(`div`,{className:`rounded border px-3 py-2 text-[11px] ${_.ok?`border-success/30 bg-success/5 text-success`:`border-danger/30 bg-danger/5 text-danger`}`,children:_.ok?`Connection OK.`:`Test failed: ${_.error??`unknown error`}`}),k.isError&&o(`div`,{className:`rounded border border-danger/30 bg-danger/5 text-danger px-3 py-2 text-[11px]`,children:k.error instanceof Error?k.error.message:String(k.error)})]}),n(S,{className:`justify-between`,children:[o(y,{variant:`outline`,size:`sm`,onClick:t,children:`Cancel`}),n(`div`,{className:`flex items-center gap-2`,children:[o(y,{variant:`outline`,size:`sm`,onClick:()=>{ie()},disabled:T||!O,children:T?`Testing…`:`Test connection`}),n(y,{size:`sm`,onClick:()=>{k.mutate(pe(e,{displayName:f.trim(),config:u,nodeId:j?M:e.nodeId}))},disabled:k.isPending||f.trim().length===0,children:[k.isPending?o(g,{className:`h-3.5 w-3.5 animate-spin`}):null,k.isPending?`Saving…`:`Save`]})]})]})]})})}function Ee(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function K(e){return typeof e==`number`&&Number.isFinite(e)?e:null}function De(e){if(!Ee(e))return null;let t=e.locationId;if(typeof t!=`string`||t.length===0)return null;let n=K(e.startedAtMs),r=K(e.startBytes),i=K(e.bytesRemaining);return n===null||r===null||i===null||typeof e.drained!=`boolean`?null:{locationId:t,startedAtMs:n,startBytes:r,bytesRemaining:i,drained:e.drained,estimatedEmptyAtMs:K(e.estimatedEmptyAtMs)}}function Oe(e){let t=new Map;if(!Array.isArray(e))return t;for(let n of e){let e=De(n);e!==null&&t.set(e.locationId,e)}return t}function ke(e){if(e.startBytes<=0||e.bytesRemaining>e.startBytes)return null;let t=e.startBytes-e.bytesRemaining;return Math.min(1,Math.max(0,t/e.startBytes))}function Ae(e,t){return e?t===void 0?{kind:`unreported`}:t.drained||t.bytesRemaining<=0?{kind:`drained`}:{kind:`draining`,bytesRemaining:t.bytesRemaining,drainedFraction:ke(t),estimatedEmptyAtMs:t.estimatedEmptyAtMs}:{kind:`idle`}}function je(e){return e.mode===`drain`?!0:e.config.readOnly===!0}function Me(e){return`${(e/1024**3).toFixed(e>=100*1024**3?0:1)} GB`}function Ne(e){return new Date(e).toLocaleString(void 0,{dateStyle:`medium`,timeStyle:`short`})}function Pe({drain:e}){if(e.kind===`idle`)return null;if(e.kind===`unreported`)return o(`span`,{"data-testid":`drain-unreported`,className:`text-[10px] text-foreground-subtle`,title:`This location is draining. The hub has not reported how much is left yet — either the drain has just been set, or this hub predates the drain-progress report.`,children:`Draining · remaining not reported yet`});if(e.kind===`drained`)return o(`span`,{"data-testid":`drain-done`,className:`text-[10px] text-success`,title:`Everything that was here has been moved. The location is still in drain mode — it receives no new writes until you change that.`,children:`Drained · nothing left to move`});let t=e.drainedFraction===null?null:Math.round(e.drainedFraction*100);return n(`span`,{"data-testid":`drain-progress`,className:`flex items-center gap-1.5 text-[10px] text-foreground-subtle tabular-nums`,children:[t!==null&&o(`span`,{"data-testid":`drain-bar`,className:`inline-block h-1 w-10 rounded-full bg-border overflow-hidden`,role:`progressbar`,"aria-label":`Drain progress`,"aria-valuenow":t,"aria-valuemin":0,"aria-valuemax":100,children:o(`span`,{className:`block h-full bg-warning`,style:{width:`${t}%`}})}),n(`span`,{children:[Me(e.bytesRemaining),` left`,t!==null&&` · ${t}% moved`]}),e.estimatedEmptyAtMs===null?o(`span`,{"data-testid":`drain-eta-absent`,title:`The drain is paced by how fast this class of data is still growing, and that has not been observed yet. There is no date to give.`,children:`· empty time not known yet`}):n(`span`,{"data-testid":`drain-eta`,children:[`· empty `,Ne(e.estimatedEmptyAtMs)]})]})}var Fe=[`ok`,`warn`,`critical`];function Ie(e){return e>=.95?`critical`:e>=.85?`warn`:`ok`}function Le(e){return Math.min(1,Math.max(0,e))}function q(e){let{availableBytes:t,totalBytes:n}=e;if(n===null||n<=0||t===null)return null;let r=Le((n-t)/n);return{fraction:r,severity:Ie(r),recordingsFraction:Le(e.usedBytes/n),freeBytes:t,totalBytes:n}}function Re(e){if(e.length===0)return null;let t=0,n=0,r=0,i=0,a=null;for(let o of e){let e=q(o);if(e===null){n+=1;continue}t+=1,r+=e.totalBytes,i+=e.freeBytes,(a===null||Fe.indexOf(e.severity)>Fe.indexOf(a))&&(a=e.severity)}return{knownVolumes:t,unknownVolumes:n,totalBytes:r,freeBytes:i,severity:a}}var ze=`Capacity unknown — nothing measured this volume from here. A remote share, a location on another node, or a mount that did not answer.`;function J(e){return Math.min(1,Math.max(0,e))}function Y(e){return Math.round(J(e)*1e3)/10}function X(e){return`${String(Math.round(J(e)*100))}%`}function Be(e,t,n={}){let{totalBytes:r,availableBytes:i}=e;if(r===null||r<=0||i===null){let e=n.ownedBytes===void 0||n.ownedBytes===null||!Number.isFinite(n.ownedBytes)||n.ownedBytes<0?null:N(n.ownedBytes);return{kind:`unknown`,owned:e,summary:e===null?ze:`${ze} CamStack holds ${e} here.`}}let a=Math.min(Math.max(i,0),r),o=r-a,s=t===null||t<=0?0:t,c=Math.min(s,a),l=s>a,u=a-c,d=n.ownedBytes,f=d==null||!Number.isFinite(d)||d<0?null:Math.min(d,o),p=o-(f??0),m=q({usedBytes:f??o,availableBytes:a,totalBytes:r}),h=m===null?J(o/r):m.fraction,g=m===null?Ie(h):m.severity,_=f===null?null:m===null?J(f/r):m.recordingsFraction,v=n.cap??null,y=v===null?null:J((c+v.bytes)/r),b=v!==null&&f!==null&&f>v.bytes,x={total:N(r),freeTotal:N(a),freeTotalPercent:X(a/r),used:N(p),usedPercent:X(p/r),free:N(u),freePercent:X(u/r),reserve:s===0?null:`${N(s)} kept free`,owned:f===null||_===null?null:`${N(f)} of recordings (${X(_)})`,cap:v===null?null:v.reason===`drain`?`${N(v.bytes)} drain target (managed by CamStack)`:`${N(v.bytes)} cap (managed by CamStack)`},S=[`${x.freeTotal} free of ${x.total} (${x.freeTotalPercent} free)`,x.owned===null?null:`CamStack holds ${x.owned}`,x.reserve===null?null:l?`${x.reserve} — less free than that right now, the oldest data here is being deleted`:x.reserve,x.cap===null?null:b?`${x.cap} — already below what is stored here, the oldest is being deleted`:x.cap].filter(e=>e!==null).join(` · `),C=[];return c>0&&C.push({segment:`reserve`,label:`Kept free`,value:N(s),alarming:l}),x.owned!==null&&C.push({segment:`owned`,label:`CamStack`,value:x.owned,alarming:!1}),C.push({segment:`used`,label:f===null?`Used`:`Other data`,value:`${x.used} (${x.usedPercent})`,alarming:g===`critical`}),C.push({segment:`free`,label:`Free`,value:`${x.free} (${x.freePercent})`,alarming:l}),v!==null&&C.push({segment:`cap`,label:v.reason===`drain`?`Drain target`:`Cap`,value:`${N(v.bytes)} · managed by CamStack`,alarming:b}),{kind:`known`,severity:g,legend:C,segments:{reservePercent:Y(c/r),ownedPercent:f===null?null:Y(f/r),usedPercent:Y(p/r),freePercent:Y(u/r),capPercent:y===null?null:Y(y)},labels:x,reserveExceedsFree:l,capBelowOwned:b,summary:S}}var Ve={ok:`bg-primary`,warn:`bg-amber-500`,critical:`bg-danger`},Z={reserve:`bg-danger/40`,owned:`bg-foreground/50`,free:`bg-border`,cap:`bg-foreground`};function He(e,t){return e===`used`?Ve[t]:Z[e]}function Ue({location:e,ownedBytes:t}){let r=e.capacity??null,i=ye(he(e.config),r?.totalBytes??null),a=Be({totalBytes:r?.totalBytes??null,availableBytes:r?.availableBytes??null},i,{ownedBytes:t??null,cap:Ce(e.config,B(e))});return a.kind===`unknown`?o(`div`,{className:`text-[10px] text-foreground-subtle italic`,title:a.summary,children:a.owned===null?`Capacity unknown`:`Capacity unknown · CamStack holds ${a.owned}`}):n(`div`,{className:`mt-1 space-y-1`,title:a.summary,children:[n(`div`,{className:`relative flex h-2 w-full max-w-md overflow-hidden rounded-full bg-border`,children:[a.segments.reservePercent>0&&o(`div`,{className:Z.reserve,style:{width:`${String(a.segments.reservePercent)}%`}}),a.segments.ownedPercent!==null&&o(`div`,{className:Z.owned,style:{width:`${String(a.segments.ownedPercent)}%`}}),o(`div`,{className:Ve[a.severity],style:{width:`${String(a.segments.usedPercent)}%`}}),o(`div`,{className:Z.free,style:{width:`${String(a.segments.freePercent)}%`}}),a.segments.capPercent!==null&&o(`div`,{className:`absolute inset-y-0 w-px ${a.capBelowOwned?`bg-danger`:Z.cap}`,style:{left:`${String(a.segments.capPercent)}%`}})]}),o(`ul`,{className:`flex flex-wrap items-center gap-x-3 gap-y-0.5`,children:a.legend.map(e=>n(`li`,{className:`flex items-center gap-1`,children:[o(`span`,{"aria-hidden":`true`,className:`inline-block h-2 w-2 shrink-0 rounded-sm ${He(e.segment,a.severity)}`}),n(`span`,{className:`text-[10px] tabular-nums ${e.alarming?`text-danger`:`text-foreground-subtle`}`,children:[e.label,` `,e.value]})]},e.segment))})]})}var We={eventMedia:[`events`],recordingsLow:[`low`],recordings:[`high`,`mid`]},Ge={eventMedia:`pipeline-analytics`,recordingsLow:`recorder`,recordings:`recorder`},Ke=`storage.discardLocation`,qe=`storage.discardLocationStatus`,Je=`storage.discardLocationCancel`;function Ye(e){return We[e]??null}function Xe(e){return Ge[e]??null}function Ze(e){return!V(B(e))}function Qe(e,t){return Ye(e.type)===null||!Ze(e)?!1:t.some(t=>t.id!==e.id&&V(B(t)))}var $e=new Set([`idle`,`scanning`,`wiping-fs`,`wiping-db`,`cascading`,`complete`,`cancelled`,`error`]);function Q(e){return typeof e==`number`&&Number.isFinite(e)?e:null}function $(e){return typeof e==`string`?e:null}function et(e){if(typeof e!=`object`||!e)return null;let t=e;if(typeof t.running!=`boolean`)return null;let n=t.phase;if(typeof n!=`string`||!$e.has(n))return null;let r=Q(t.camerasTotal),i=Q(t.camerasDone),a=Q(t.foldersRemoved),o=Q(t.dbRowsDeleted),s=Q(t.ownersCascaded);return r===null||i===null||a===null||o===null||s===null?null:{running:t.running,locationId:$(t.locationId),jobId:$(t.jobId),phase:n,camerasTotal:r,camerasDone:i,foldersRemoved:a,dbRowsDeleted:o,ownersCascaded:s,detail:$(t.detail)??``,startedAtMs:Q(t.startedAtMs),finishedAtMs:Q(t.finishedAtMs),error:$(t.error)}}function tt(e){if(e.phase===`complete`)return 100;if(e.phase===`cancelled`||e.phase===`error`||e.phase===`idle`)return e.camerasTotal>0?Math.min(99,Math.round(e.camerasDone/e.camerasTotal*100)):0;if(e.camerasTotal>0){let t=e.camerasDone/e.camerasTotal*85;return e.phase===`wiping-db`?Math.min(95,Math.round(t+8)):e.phase===`cascading`?Math.min(99,Math.round(t+12)):Math.min(85,Math.round(t))}return e.phase===`scanning`?2:5}t();var nt=2e3;function rt({location:e,siblings:t}){return Qe(e,t)?o(it,{location:e}):null}function it({location:e}){let t=k(),r=A(),i=Xe(e.type),[c,l]=a(null),[u,d]=a(null),[f,p]=a(!1);if(s(()=>{if(i===null)return;let e=!1,t=async()=>{try{let t=await r.mutateAsync({addonId:i,action:qe,input:{}});if(e)return;l(et(t))}catch{}};t();let n=setInterval(()=>void t(),nt);return()=>{e=!0,clearInterval(n)}},[i,e.id]),i===null)return null;let m=c!==null&&c.locationId===e.id,h=m&&c.running,_=m&&c.phase!==`idle`,v=async()=>{if(await t({title:`Discard leftovers on "${e.displayName}"?`,message:`Permanently deletes leftover files and records on this location. Other locations are not affected. This cannot be undone and may take hours.`,confirmLabel:`Discard leftovers`,variant:`danger`})){d(null),p(!0);try{await r.mutateAsync({addonId:i,action:Ke,input:{locationId:e.id}}),l(et(await r.mutateAsync({addonId:i,action:qe,input:{}})))}catch(e){d(e instanceof Error?e.message:String(e))}finally{p(!1)}}},y=async()=>{try{await r.mutateAsync({addonId:i,action:Je,input:{}})}catch(e){d(e instanceof Error?e.message:String(e))}};return n(`div`,{className:`flex flex-col gap-1 min-w-[9rem] max-w-[16rem]`,children:[h?n(`button`,{type:`button`,onClick:()=>void y(),className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-danger border border-danger/40 hover:bg-danger/10`,children:[o(O,{className:`h-3 w-3`}),`Cancel`]}):n(`button`,{type:`button`,onClick:()=>void v(),disabled:f,title:`Permanently delete leftover files on this location. Other locations are not affected.`,className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-danger border border-danger/40 hover:bg-danger/10 disabled:opacity-50`,children:[f?o(g,{className:`h-3 w-3 animate-spin`}):o(F,{className:`h-3 w-3`}),`Discard leftovers`]}),_&&c!==null&&n(`div`,{className:`space-y-0.5`,children:[o(`div`,{className:`h-1 rounded bg-border overflow-hidden`,children:o(`div`,{className:`h-full bg-primary transition-[width] duration-300`,style:{width:`${tt(c)}%`}})}),n(`p`,{className:`text-[10px] text-foreground-subtle leading-tight`,children:[c.phase,c.camerasTotal>0?` · ${c.camerasDone}/${c.camerasTotal} cam`:``,c.dbRowsDeleted>0?` · ${c.dbRowsDeleted} rows`:``,c.detail?` · ${c.detail}`:``]})]}),(u||m&&c?.error)&&o(`p`,{className:`text-[10px] text-danger leading-tight`,children:u??c?.error})]})}t();var at={active:E,readonly:j,drain:M,disabled:f},ot={active:`border-success/30 text-success hover:bg-success/10`,readonly:`border-border text-foreground-subtle hover:text-foreground hover:bg-primary/10`,drain:`border-warning/40 text-warning hover:bg-warning/10`,disabled:`border-danger/40 text-danger hover:bg-danger/10`};function st({mode:e,open:t,onToggle:r}){let i=U(e),a=at[e];return n(`button`,{type:`button`,"aria-expanded":t,"aria-label":`Location state`,onClick:r,title:i.consequence,className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium border ${ot[e]}`,children:[o(a,{className:`h-3 w-3`}),i.label,o(l,{className:`h-3 w-3 ${t?`rotate-180`:``}`})]})}function ct({mode:e,blockedReasons:t,onSelect:r,onDismiss:i}){return s(()=>{let e=e=>{e.key===`Escape`&&i()};return window.addEventListener(`keydown`,e),()=>{window.removeEventListener(`keydown`,e)}},[i]),n(`div`,{className:`w-full basis-full rounded-lg border border-border bg-background p-1`,role:`group`,"aria-label":`Location state choices`,children:[n(`div`,{className:`flex items-center justify-between px-2 py-1`,children:[o(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle`,children:`Location state`}),o(`button`,{type:`button`,"aria-label":`Close location state choices`,onClick:i,className:`text-foreground-subtle hover:text-foreground`,children:o(O,{className:`h-3 w-3`})})]}),H.map(a=>{let s=t[a.mode],c=s!==``,l=at[a.mode],u=a.mode===e;return n(`button`,{type:`button`,role:`radio`,"aria-checked":u,"aria-label":a.label,disabled:c,onClick:()=>{c||(i(),u||r(a.mode))},className:`flex w-full flex-col items-start gap-0.5 rounded px-2 py-1.5 text-left ${c?`cursor-not-allowed opacity-60`:u?`bg-primary/10`:`hover:bg-primary/5`}`,children:[n(`span`,{className:`flex items-center gap-1.5 text-[11px] font-medium text-foreground`,children:[o(l,{className:`h-3 w-3`}),a.label,u&&o(`span`,{className:`text-[9px] uppercase text-primary`,children:`current`})]}),o(`span`,{className:`text-[10px] leading-snug text-foreground-subtle`,children:a.consequence}),c&&o(`span`,{className:`text-[10px] leading-snug text-warning`,children:s})]},a.mode)})]})}t();function lt(e){let t=e.basePath;if(typeof t==`string`&&t.length>0)return t;let n=Object.keys(e);return n.length===0?``:n.join(`, `)}function ut({location:e,onEdit:t,onDelete:r,onSetMode:i,blockedReasons:s,canDelete:c,siblings:l=[],drain:d,ownedBytes:f}){let[p,m]=a(!1),h=lt(e.config),g=B(e);return n(`div`,{className:`flex flex-wrap items-start gap-x-3 gap-y-2 px-3 py-2 border-t border-border first:border-t-0 hover:bg-primary/5`,children:[n(`div`,{className:`flex items-start gap-2 min-w-0 flex-1 basis-72`,children:[o(`div`,{className:`shrink-0 sm:w-32`,"data-testid":`location-badge-slot`,children:o(`span`,{className:`inline-block text-[10px] uppercase tracking-wide text-foreground-subtle bg-background border border-border rounded px-1.5 py-0.5 font-mono`,children:e.providerId})}),n(`div`,{className:`min-w-0 flex-1`,children:[o(`div`,{className:`text-xs font-medium text-foreground truncate`,children:e.displayName}),h&&o(`div`,{className:`text-[10px] text-foreground-subtle truncate font-mono`,children:h}),o(Ue,{location:e,ownedBytes:f??null}),d!==void 0&&o(Pe,{drain:d})]})]}),n(`div`,{className:`flex flex-wrap items-center gap-1 shrink-0`,children:[o(rt,{location:e,siblings:l}),o(st,{mode:g,open:p,onToggle:()=>{m(e=>!e)}}),n(`button`,{onClick:()=>t(e),className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-foreground-subtle border border-border hover:text-foreground hover:bg-primary/10`,children:[o(D,{className:`h-3 w-3`}),`Edit`]}),c&&o(`button`,{onClick:()=>r(e),className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] font-medium text-danger hover:bg-danger/10`,title:`Delete location`,children:o(u,{className:`h-3 w-3`})})]}),p&&o(ct,{mode:g,blockedReasons:s,onSelect:t=>i(e,t),onDismiss:()=>{m(!1)}})]})}t();function dt({type:e,displayName:t,description:i,cardinality:a,locations:s,onAdd:l,onEdit:u,onDelete:d,onSetMode:f,ownedBytesById:p,drainById:m}){let h=r(()=>{let e=[...s];return e.sort((e,t)=>{let n=V(B(e));return n===V(B(t))?e.displayName.localeCompare(t.displayName):n?-1:1}),e},[s]);return n(w,{headingId:`storage-location-${e}`,title:t,icon:_,testId:`storage-location-group-${e}`,actions:a===`multi`?n(y,{size:`sm`,type:`button`,onClick:()=>l(e),children:[o(T,{className:`h-3 w-3`}),`Add`]}):void 0,children:[n(`div`,{className:`${c} space-y-2`,children:[n(`div`,{className:`space-y-0.5`,children:[o(`span`,{className:`text-[10px] uppercase tracking-wide text-foreground-subtle bg-background border border-border rounded px-1.5 py-0.5 font-mono`,children:e}),i!==void 0&&i!==``&&o(`p`,{className:`text-[11px] leading-relaxed text-foreground-subtle`,children:i}),a===`single`&&o(`p`,{className:`text-[10px] text-foreground-subtle italic`,children:`Single — one location per type. Edit the existing entry; it can't be split.`})]}),h.length===0&&o(`div`,{className:`py-2 text-center text-[11px] text-foreground-subtle italic`,children:a===`multi`?`No location registered. Click "Add" to register one.`:`No location registered yet — should be auto-seeded at next boot.`})]}),h.length>0&&o(`div`,{className:`border-t border-border`,children:h.map(e=>o(ut,{location:e,cardinality:a,canDelete:de(e,h,a),onEdit:u,onDelete:d,onSetMode:f,blockedReasons:ue(e,h),ownedBytes:p?.get(e.id)??null,drain:Ae(je(e),m?.get(e.id)),siblings:h},e.id))})]})}var ft=[`recordings`,`recordingsLow`];function pt(e){return e.locationIds!==void 0&&e.locationIds.length>0?e.locationIds:e.locationId===null?[]:[e.locationId]}function mt(e,t){if(t===void 0||!ft.includes(e.type))return null;let n=t.find(t=>pt(t).includes(e.id));return n===void 0||!Number.isFinite(n.usedBytes)||n.usedBytes<0?null:n.usedBytes}function ht(e,t){let n=new Map;for(let r of e){let e=mt(r,t);e!==null&&n.set(r.id,e)}return n}export{Oe as a,U as c,Re as i,ce as l,dt as n,Te as o,q as r,pe as s,ht as t,z as u};
@@ -16,4 +16,4 @@ const __mfImport = (src) =>
16
16
  await __mfHostInit.__tla;
17
17
  const { initHost } = __mfHostInit;
18
18
  await initHost();
19
- })().then(() => __mfImport("./index-V6k1XLI7.js"));
19
+ })().then(() => __mfImport("./index-B-BE8EYQ.js"));
@@ -1 +1 @@
1
- import{I as e,Q as t,S as n,W as r,x as i}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{ku as a}from"./src-Ca4gNAmP.js";import{$ as o}from"./index-V6k1XLI7.js";function s({staleNodeId:e,shownNodeName:t}){return e===null?null:n(`div`,{role:`status`,className:`flex items-start gap-2 rounded-md border border-warning/30 bg-warning/5 px-3 py-2 text-[11px] text-foreground-subtle`,children:[i(a,{className:`mt-0.5 h-3.5 w-3.5 shrink-0 text-warning`}),n(`span`,{children:[`This link named node `,i(`span`,{className:`font-medium text-foreground`,children:e}),`, which is not in this cluster. Showing`,` `,i(`span`,{className:`font-medium text-foreground`,children:t}),` instead.`]})]})}t();var c=`node`;function l(e){return[...e].toSorted((e,t)=>e.isHub===t.isHub?e.isOnline===t.isOnline?e.name.localeCompare(t.name):e.isOnline?-1:1:e.isHub?-1:1)}function u(e,t){let n=l(e),r=n[0]??null;if(r===null)return{node:null,outcome:`empty`,staleNodeId:null};if(t===null||t.length===0)return{node:r,outcome:`default`,staleNodeId:null};let i=n.find(e=>e.id===t);return i===void 0?{node:r,outcome:`stale`,staleNodeId:t}:{node:i,outcome:`requested`,staleNodeId:null}}function d(t){let[n,i]=o(),a=n.get(c);return{selection:r(()=>u(t,a),[t,a]),selectNode:e(e=>{let t=new URLSearchParams(n);t.set(c,e),i(t,{replace:!0})},[n,i]),requestedNodeId:a}}export{s as n,d as t};
1
+ import{I as e,Q as t,S as n,W as r,x as i}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{ku as a}from"./src-Ca4gNAmP.js";import{$ as o}from"./index-B-BE8EYQ.js";function s({staleNodeId:e,shownNodeName:t}){return e===null?null:n(`div`,{role:`status`,className:`flex items-start gap-2 rounded-md border border-warning/30 bg-warning/5 px-3 py-2 text-[11px] text-foreground-subtle`,children:[i(a,{className:`mt-0.5 h-3.5 w-3.5 shrink-0 text-warning`}),n(`span`,{children:[`This link named node `,i(`span`,{className:`font-medium text-foreground`,children:e}),`, which is not in this cluster. Showing`,` `,i(`span`,{className:`font-medium text-foreground`,children:t}),` instead.`]})]})}t();var c=`node`;function l(e){return[...e].toSorted((e,t)=>e.isHub===t.isHub?e.isOnline===t.isOnline?e.name.localeCompare(t.name):e.isOnline?-1:1:e.isHub?-1:1)}function u(e,t){let n=l(e),r=n[0]??null;if(r===null)return{node:null,outcome:`empty`,staleNodeId:null};if(t===null||t.length===0)return{node:r,outcome:`default`,staleNodeId:null};let i=n.find(e=>e.id===t);return i===void 0?{node:r,outcome:`stale`,staleNodeId:t}:{node:i,outcome:`requested`,staleNodeId:null}}function d(t){let[n,i]=o(),a=n.get(c);return{selection:r(()=>u(t,a),[t,a]),selectNode:e(e=>{let t=new URLSearchParams(n);t.set(c,e),i(t,{replace:!0})},[n,i]),requestedNodeId:a}}export{s as n,d as t};
package/dist/index.html CHANGED
@@ -117,7 +117,7 @@
117
117
  })
118
118
  })()
119
119
  </script>
120
- <script type="module" crossorigin src="./assets/mf-entry-bootstrap-0-0ee3f006.js"></script>
120
+ <script type="module" crossorigin src="./assets/mf-entry-bootstrap-0-50b2491c.js"></script>
121
121
  <link rel="modulepreload" crossorigin href="./assets/__vite-browser-external-DF8Eb0aa.js">
122
122
  <link rel="modulepreload" crossorigin href="./assets/client-1JBRoe76.js">
123
123
  <link rel="modulepreload" crossorigin href="./assets/player-overlays-9CMoB0XN.js">
@@ -126,7 +126,7 @@
126
126
  <link rel="modulepreload" crossorigin href="./assets/mount-DD88fyzH.js">
127
127
  <link rel="modulepreload" crossorigin href="./assets/notification-schedule-field-Bnhm4lQH.js">
128
128
  <link rel="stylesheet" crossorigin href="./assets/index-C6r9IlW4.css">
129
- <link rel="modulepreload" crossorigin href="./assets/hostInit-D4-UgrFJ.js"><link rel="modulepreload" crossorigin href="./assets/index-V6k1XLI7.js"><link rel="modulepreload" crossorigin href="./assets/remoteEntry-nN8yf3LF.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-D4evVvwt.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_client__loadShare__.js-8kv6pj9W.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.js-DX7sYWrs.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_dom__loadShare__.js-BsvPG3BX.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-DtoGixA1.js"></head>
129
+ <link rel="modulepreload" crossorigin href="./assets/hostInit-D4-UgrFJ.js"><link rel="modulepreload" crossorigin href="./assets/index-B-BE8EYQ.js"><link rel="modulepreload" crossorigin href="./assets/remoteEntry-nN8yf3LF.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf-localSharedImportMap___mfe_internal__admin_ui_host-D4evVvwt.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_client__loadShare__.js-8kv6pj9W.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_trpc_mf_1_react_mf_2_query__loadShare__.js-DX7sYWrs.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_dom__loadShare__.js-BsvPG3BX.js"><link rel="modulepreload" crossorigin href="./assets/_virtual_mf___mfe_internal__admin_ui_host__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-DtoGixA1.js"></head>
130
130
  <body>
131
131
  <div id="root"></div>
132
132
  <!-- Lock the app shell at 1x zoom. iOS Safari ignores `user-scalable=no`
@@ -52765,7 +52765,7 @@ var AdminUIAddon = class extends BaseAddon {
52765
52765
  capability: adminUiCapability,
52766
52766
  provider: {
52767
52767
  getStaticDir: async () => ({ staticDir: resolveAdminUiDistDir() }),
52768
- getVersion: async () => ({ version: "1.2.399" })
52768
+ getVersion: async () => ({ version: "1.2.400" })
52769
52769
  }
52770
52770
  }];
52771
52771
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-admin-ui",
3
- "version": "1.2.399",
3
+ "version": "1.2.400",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Admin UI — Vite frontend build and server-side addon",
@@ -1 +0,0 @@
1
- import{D as e,K as t,L as n,Q as r,S as i,W as a,b as o,o as s,q as c,x as l,z as u}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{V as d,lt as f}from"./player-overlays-9CMoB0XN.js";import{$u as p,Ed as m,Gn as h,Hn as g,It as _,Lr as v,Od as y,Tt as b,Un as x,Ur as S,Vt as ee,ad as C,bf as w,dd as T,fd as te,ku as ne,ur as re,wt as ie,zn as E}from"./src-Ca4gNAmP.js";import{t as ae}from"./settings-2-DEwPBiUG.js";import{$ as D,Y as O,d as k,f as A,h as j,l as M,m as N,o as P,u as F}from"./index-V6k1XLI7.js";import{t as I}from"./AdminPage-LJSPjGaV.js";import{t as L}from"./NodeAddonsSettingsPanel-DNm2sNWd.js";import{t as R}from"./AddonAssetIcon-DRk5tFmQ.js";r();function z(e){let{ids:t,answered:n,legacyGroupParam:r=null,groupFirst:i}=e,[o]=D(),s=o.get(M),c=r===null?null:o.get(r),l=o.get(F),u=a(()=>N({ids:t,requested:s,answered:n,legacyGroup:c,...i===void 0?{}:{groupFirst:i}}),[t,s,n,c,i]),d=a(()=>(e,t)=>`?${j(o,e,r,t??null).toString()}`,[o,r]);return{...u,requestedSection:l,hrefFor:d}}var B=`inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-full border py-1 pl-1 pr-2.5 text-xs transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40`,V=`border-primary bg-primary/10 text-primary font-medium`,H=`border-border bg-surface text-foreground-subtle hover:bg-surface-hover hover:text-foreground`,oe={ok:`bg-success`,off:`bg-foreground-subtle/60`,error:`bg-danger`,unknown:`border border-foreground-subtle bg-transparent`};function U({label:e,groups:t,selectedId:n,hrefFor:r}){let a=t.length>1;return l(`nav`,{"aria-label":e,className:a?`flex flex-col gap-2 sm:grid sm:grid-cols-[max-content_minmax(0,1fr)] sm:items-start sm:gap-x-3`:`flex flex-col`,children:t.map(e=>i(`div`,{role:`group`,"aria-label":e.label,className:a?`flex min-w-0 flex-col gap-1 sm:col-span-2 sm:grid sm:grid-cols-subgrid sm:items-start`:`min-w-0`,children:[a&&l(`span`,{className:`text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle sm:pt-2`,children:e.label}),i(`div`,{className:`flex min-w-0 flex-wrap items-center gap-1.5`,children:[e.entries.map(e=>l(W,{entry:e,current:e.id===n,href:r(e.id)},e.id)),e.install&&i(O,{to:A(e.install.capability),className:`inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-full border py-1 pl-1 pr-2.5 text-xs transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 border-dashed border-border pl-2 text-foreground-subtle hover:border-primary/50 hover:text-foreground`,children:[l(T,{className:`h-3.5 w-3.5 shrink-0`,"aria-hidden":!0}),l(`span`,{className:`truncate`,children:e.install.label})]})]})]},e.id))})}function W({entry:e,current:t,href:n}){let r=e.state===`ok`;return i(O,{to:n,"aria-current":t?`true`:void 0,className:`${B} ${t?V:H}`,"data-testid":`integration-pill-${e.id}`,children:[l(R,{addonId:e.id,fallback:e.fallbackIcon,tileClassName:``,size:`sm`}),l(`span`,{className:`truncate`,children:e.name}),e.showState!==!1&&l(G,{entry:e,quiet:r})]})}function G({entry:e,quiet:t}){return i(o,{children:[l(`span`,{className:`h-1.5 w-1.5 shrink-0 rounded-full ${oe[e.state]}`,"aria-hidden":!0}),l(`span`,{className:t?`sr-only`:`shrink-0 text-[10px] font-normal text-foreground-subtle`,children:e.stateLabel})]})}r();var K=600,q=e(null);function J({id:e,title:r,icon:a,actions:s,framed:c=!1,children:u}){let f=n(q),p=t(null),m=`integration-section-${e}`;se(p,f,e);let h=i(o,{children:[f===null?null:l(O,{to:f.sectionHref(e),"aria-label":`Link to ${r}`,title:`Link to ${r}`,className:`rounded p-0.5 text-foreground-subtle/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40`,children:l(y,{className:`h-3 w-3`,"aria-hidden":!0})}),s]});return c?l(`div`,{ref:p,className:`scroll-mt-4`,children:l(ee,{headingId:m,title:r,icon:a,actions:h,children:u})}):i(`section`,{ref:p,"aria-labelledby":m,className:`scroll-mt-4 space-y-2`,children:[i(`header`,{className:`flex min-h-7 flex-wrap items-center gap-2 px-0.5`,children:[a!==void 0&&l(a,{className:`h-3.5 w-3.5 shrink-0 text-foreground-subtle`,"aria-hidden":!0}),l(`h2`,{id:m,className:`${d} min-w-0 flex-1`,children:r}),h]}),u]})}function se(e,t,n){let r=t?.requestedSection===n,i=t?.integrationId;u(()=>{if(!r)return;let t=()=>e.current?.scrollIntoView?.({block:`start`});t();let n=!1,i=()=>{n=!0},a=[`wheel`,`touchmove`,`keydown`];for(let e of a)window.addEventListener(e,i,{once:!0,passive:!0});let o=window.setTimeout(()=>{n||t()},K);return()=>{window.clearTimeout(o);for(let e of a)window.removeEventListener(e,i)}},[r,i,e])}r();function ce({icon:e,title:t,subtitle:n,groups:r,isLoading:s,emptyTitle:c,emptyDescription:u,legacyGroupParam:d,trailing:f,embedded:p=!1}){let m=a(()=>r.flatMap(e=>e.entries),[r]),h=a(()=>m.map(e=>e.id),[m]),g=a(()=>{let e={};for(let t of r){let n=t.entries[0];n!==void 0&&(e[t.id]=n.id)}return e},[r]),_=z({ids:h,answered:!s,legacyGroupParam:d??null,groupFirst:g}),v=m.find(e=>e.id===_.selectedId)??null,y=a(()=>v===null?null:{integrationId:v.id,requestedSection:_.requestedSection,sectionHref:e=>_.hrefFor(v.id,e)},[v,_]),b=s?null:l(U,{label:`${t} integrations`,groups:r,selectedId:_.selectedId,hrefFor:e=>_.hrefFor(e)}),x=s?l(E,{children:l(`div`,{className:`p-6 text-sm text-foreground-subtle`,children:`Loading…`})}):i(o,{children:[_.staleId!==null&&l(ue,{staleId:_.staleId,shownName:v?.name??null}),v!==null&&y!==null?l(q.Provider,{value:y,children:l(`div`,{role:`region`,"aria-label":v.name,"data-testid":`integration-body`,children:v.render()},v.id)}):l(le,{groups:r,title:c,description:u}),f]});return p?i(`div`,{className:`space-y-3`,"data-testid":`integration-hub-embedded`,children:[b,x]}):l(I,{icon:e,title:t,subtitle:n,nav:b,children:x})}function le({groups:e,title:t,description:n}){let r=e.find(e=>e.install!==void 0)?.install;return l(E,{children:l(x,{title:t,description:n,action:r?l(O,{to:A(r.capability),children:i(h,{variant:`primary`,size:`sm`,children:[l(T,{className:`mr-1 h-3.5 w-3.5`}),r.label]})}):void 0})})}function ue({staleId:e,shownName:t}){return i(`div`,{role:`status`,className:`flex items-start gap-2 rounded-md border border-warning/30 bg-warning/5 px-3 py-2 text-[11px] text-foreground-subtle`,children:[l(ne,{className:`mt-0.5 h-3.5 w-3.5 shrink-0 text-warning`,"aria-hidden":!0}),i(`span`,{children:[`This link named `,l(`span`,{className:`font-mono text-foreground`,children:e}),`, which is not installed here.`,t!==null&&i(o,{children:[` `,`Showing `,l(`span`,{className:`font-medium text-foreground`,children:t}),` instead.`]})]})]})}var Y=[[[`addons`,`listCapabilityProviders`]],[[`addons`,`list`]]],X=`text-[10px]`;function de({isActive:e}){return l(re,{variant:e?`success`:`default`,className:X,children:e?`Enabled`:`Disabled`})}function Z({capName:e,addonId:t,isActive:n}){let r=s(),a=S({onSuccess:()=>{for(let e of Y)r.invalidateQueries({queryKey:e})}}),o=a.isPending;return i(h,{size:`sm`,variant:n?`secondary`:`primary`,disabled:o,onClick:()=>a.mutate({capName:e,addonId:t,enabled:!n}),children:[o?l(m,{className:`h-3 w-3 animate-spin`}):l(n?f:te,{className:`h-3 w-3`}),n?`Disable`:`Enable`]})}function fe({addonId:e,displayName:t}){let n=s(),r=v({onSuccess:()=>{for(let e of Y)n.invalidateQueries({queryKey:e})}});return l(_,{label:`Restart`,icon:C,size:`sm`,title:`Restart "${t}"?`,description:`Restarting the addon bounces its subprocess — the provider will briefly drop and reconnect automatically.`,confirmLabel:`Restart`,triggerVariant:`ghost`,confirmVariant:`danger`,disabled:r.isPending,action:()=>r.mutateAsync({addonId:e})})}r();var Q=`max-h-72`;function pe({addonId:e,displayName:t,capName:n,isActive:r,badges:a,primaryAction:o,error:s=null,lead:c,panels:u=[],settingsContent:d}){let f=u.filter(e=>e.placement!==`end`),p=u.filter(e=>e.placement===`end`);return i(`div`,{className:`flex flex-col gap-4`,children:[l(me,{addonId:e,displayName:t,capName:n,isActive:r,badges:a,primaryAction:o}),s!==null&&l(g,{message:s}),c,l($,{panels:f}),l(J,{id:k,title:`Settings`,icon:ae,children:d===void 0?l(L,{nodeId:`hub`,addonIds:[e],level:`global`,capability:n}):d}),l(ge,{addonId:e}),l($,{panels:p})]})}function $({panels:e}){return l(o,{children:e.map(e=>l(J,{id:e.id,title:e.title,icon:e.icon,children:e.content},e.id))})}function me({addonId:e,displayName:t,capName:n,isActive:r,badges:a,primaryAction:o}){return i(`div`,{className:`flex flex-wrap items-center justify-between gap-2 rounded-lg border border-border bg-surface px-2 py-1.5 sm:px-3`,"data-testid":`integration-status-strip`,children:[i(`div`,{className:`flex min-w-0 flex-wrap items-center gap-1.5`,children:[l(de,{isActive:r}),a,l(`span`,{className:`break-all font-mono text-[11px] text-foreground-subtle`,children:e})]}),i(`div`,{className:`flex flex-wrap items-center justify-end gap-1`,children:[o,l(Z,{capName:n,addonId:e,isActive:r}),l(fe,{addonId:e,displayName:t})]})]})}var he=[{kind:`logs`,label:`Logs`,icon:p},{kind:`events`,label:`Events`,icon:w}];function ge({addonId:e}){let[t,n]=c(`logs`);return l(J,{id:P,title:`Activity`,framed:!0,actions:l(`div`,{role:`group`,"aria-label":`Activity stream`,className:`flex items-center gap-1`,children:he.map(({kind:e,label:r,icon:a})=>i(`button`,{type:`button`,"aria-pressed":t===e,onClick:()=>n(e),className:`inline-flex items-center gap-1 rounded px-2 py-0.5 text-[11px] font-medium transition-colors ${t===e?`bg-primary/10 text-primary`:`text-foreground-subtle hover:bg-surface-hover hover:text-foreground`}`,children:[l(a,{className:`h-3 w-3`,"aria-hidden":!0}),r]},e))}),children:t===`logs`?l(b,{addonId:e,maxHeight:Q,showFilters:!1}):l(ie,{addonId:e,category:`addon.*`,maxHeight:Q})})}export{X as n,ce as r,pe as t};
@@ -1 +0,0 @@
1
- import{I as e,K as t,Q as n,S as r,W as i,a,b as o,o as s,q as c,t as l,x as u,z as d}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{_t as f,ct as p,gt as m,ot as h,yt as g}from"./player-overlays-9CMoB0XN.js";import{$u as _,Ad as v,Al as y,An as b,Bl as x,Dl as S,Ed as C,Fl as w,Fn as T,Ft as E,G as D,Gd as O,Gn as k,Hl as A,Hn as j,Ht as ee,Il as te,In as M,Jl as N,Ju as P,L as ne,Ll as F,Md as I,Ml as L,Mn as R,Mr as z,N as re,Nl as ie,Od as ae,Ol as oe,P as se,Pl as ce,Pn as B,Ql as V,Rl as le,Tn as ue,Un as de,Vl as fe,Vu as pe,Wn as me,X as he,Yu as H,Z as ge,cf as _e,dd as ve,dn as U,fu as W,jd as ye,jl as be,jn as G,jr as xe,kl as Se,md as Ce,mu as we,nr as K,ql as Te,qu as q,rr as Ee,ur as De,va as Oe,wd as ke,wu as Ae,z as je,zl as Me}from"./src-Ca4gNAmP.js";import{t as Ne}from"./history-B0sqAabA.js";import{t as Pe}from"./monitor-smartphone-J97h7fMI.js";import{_ as J,a as Fe,b as Ie,c as Le,d as Re,f as ze,g as Be,h as Y,i as Ve,l as He,m as Ue,n as We,o as Ge,p as Ke,s as qe,t as Je,u as Ye,v as X,y as Xe}from"./SessionsList-JKR36vEz.js";import{a as Ze,gi as Qe,hi as $e,i as et,mi as tt,o as nt,s as rt}from"./client-1JBRoe76.js";import{t as it}from"./useTranslation-Bc5yH9V9.js";import{$ as at,C as ot,R as st,X as ct,Y as lt,at as ut,c as dt,p as ft,rt as pt,s as mt,tt as ht}from"./index-V6k1XLI7.js";import{t as gt}from"./AdminSectionedPage-Dmz5o8uT.js";import{t as _t}from"./AdminPage-LJSPjGaV.js";import{n as vt,r as yt,t as bt}from"./AddonIntegrationBody-BokvIE2T.js";import{n as xt,t as St}from"./addon-display-names-BkwMrExv.js";var Ct=g(`book-open`,[[`path`,{d:`M12 7v14`,key:`1akyts`}],[`path`,{d:`M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z`,key:`ruj8y`}]]),wt=g(`key-square`,[[`path`,{d:`M12.4 2.7a2.5 2.5 0 0 1 3.4 0l5.5 5.5a2.5 2.5 0 0 1 0 3.4l-3.7 3.7a2.5 2.5 0 0 1-3.4 0L8.7 9.8a2.5 2.5 0 0 1 0-3.4z`,key:`165ttr`}],[`path`,{d:`m14 7 3 3`,key:`1r5n42`}],[`path`,{d:`m9.4 10.6-6.814 6.814A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814`,key:`1ubxi2`}]]);n();function Tt(){let e=K(),t=W.useUtils(),n=Xe(),{data:r,isLoading:a}=w(),o=i(()=>r??[],[r]),s=W.useQueries(e=>o.map(t=>e.userManagement.listExternalIdentities({userId:t.id}))),l=W.auth.listLoginMethods.useQuery(),[d,f]=c(null),p=fe({onSuccess:()=>void t.userManagement.listExternalIdentities.invalidate(),onError:e=>f(`Remove failed: ${e.message}`)}),m=i(()=>new Map((l.data??[]).flatMap(e=>e.kind===`redirect`?[[e.id,e.label]]:[])),[l.data]),h=o.flatMap((e,t)=>s[t]?.data??[]),g=h.map(e=>({...Re(e,n),kind:m.get(e.provider)??e.provider})),_=async t=>{let n=h.find(e=>Ye(e)===t.id);n!==void 0&&await e({title:`Remove sign-in link`,message:`"${t.ownerName}" will no longer be able to sign in with this ${t.kind} account.`,confirmLabel:`Remove`,variant:`danger`})&&(f(null),p.mutate({userId:n.userId,provider:n.provider,subject:n.subject}))};return u(J,{notice:X(d),onDismissNotice:()=>{f(null)},id:`external-identities`,title:`Sign-in links`,icon:ut,description:`External accounts (single sign-on) each user linked to their CamStack account. Only the owner can link one, from their profile; an admin can remove it.`,children:u(Y,{rows:g,label:`sign-in links`,isLoading:a||s.some(e=>e.isLoading),loadError:s.find(e=>e.isError)?.error?.message??null,emptyText:`No user has linked a sign-in account.`,expiresNone:`—`,action:{label:`Remove`,icon:Ie,isAvailable:()=>!0,onRun:e=>void _(e),busyId:null,disabled:p.isPending}})})}n();function Et(){let e=K(),t=s(),n=Xe(),r=L(),[a,o]=c(null),l=F({onSuccess:()=>void t.invalidateQueries({queryKey:[[`userManagement`,`listApiKeys`]]}),onError:e=>o(`Revoke failed: ${e.message}`)}),d=i(()=>(r.data??[]).map(e=>He(e,n)),[r.data,n]),f=async t=>{await e({title:`Revoke "${t.name}"?`,message:`Any client using this key loses access immediately. This cannot be undone.`,confirmLabel:`Revoke`,variant:`danger`})&&(o(null),l.mutate({id:t.id}))};return u(J,{notice:X(a),onDismissNotice:()=>{o(null)},id:`legacy-api-keys`,title:`Legacy API keys`,icon:Ne,description:`Keys from before scoped tokens. Nothing creates them any more; revoke them and issue a scoped token instead.`,children:u(Y,{rows:d,label:`legacy API keys`,isLoading:r.isLoading,loadError:r.isError?r.error?.message??`Request failed`:null,emptyText:`No legacy API keys.`,action:{label:`Revoke`,icon:p,isAvailable:()=>!0,onRun:e=>void f(e),busyId:l.isPending?l.variables?.id??null:null,disabled:l.isPending}})})}n();function Dt(e){if(e.type===`category`){let t=e.target??``;return t===`device`?`all devices`:`all ${t}`}if(e.type===`device`)return`device:[${(e.targets??[]).join(`,`)}]`;let t=e;return`${t.type}:${t.target??``}`}function Ot(){let e=K(),t=s(),n=ce(),[r,a]=c(null),o=le({onSuccess:()=>void t.invalidateQueries({queryKey:[[`userManagement`,`listOauthSessions`]]}),onError:e=>a(`Revoke failed: ${e.message}`)}),l=i(()=>(n.data??[]).map(e=>ze(e,e.scopes.map(Dt).join(`, `))),[n.data]),d=async t=>{await e({title:`Revoke the "${t.name}" link?`,message:`${t.name} loses access for ${t.ownerName} on its next call. Linking it again needs the account-link flow from the start.`,confirmLabel:`Revoke`,variant:`danger`})&&(a(null),o.mutate({id:t.id}))},f=o.isPending?o.variables?.id??null:null;return u(J,{notice:X(r),onDismissNotice:()=>{a(null)},id:`linked-apps`,title:`Linked apps`,icon:ae,description:`External integrations (Alexa, …) linked to this hub through OAuth. Revoking one stops that integration on its next call.`,children:u(Y,{rows:l,label:`linked apps`,isLoading:n.isLoading,loadError:n.isError?n.error?.message??`Request failed`:null,emptyText:`No linked apps — an integration appears here once it completes the account-link flow.`,expiresNone:`—`,action:{label:`Revoke`,icon:p,isAvailable:e=>e.status===`active`,onRun:e=>void d(e),busyId:f,disabled:o.isPending}})})}n();var kt={one:`session`,many:`sessions`};function At(){let e=K(),t=W.useUtils(),n=Xe(),r=W.auth.listSessions.useQuery(),[a,o]=c(null),s=()=>void t.auth.listSessions.invalidate(),l=W.auth.revokeSessions.useMutation({onSuccess:s,onError:e=>o(`Sign out failed: ${e.message}`)}),d=Be(`login-session`,kt,s),f=i(()=>(r.data??[]).map(e=>Ke(e,n,{client:e=>Fe(e).label,method:Ge})),[r.data,n]),p=async t=>{await e({title:`Sign out this session`,message:`${t.ownerName} on ${t.name} will have to sign in again.`,confirmLabel:`Sign out`,variant:`danger`})&&(o(null),l.mutate({id:t.id}))};return u(J,{notice:X(a,d.outcome),onDismissNotice:()=>{o(null),d.dismiss()},id:`login-sessions`,title:`Login sessions`,icon:Pe,description:`Every browser and app signed in to this hub, for every user. Signing one out ends it at once; it has to sign in again. Your own current session is marked and never included in a bulk sign-out.`,children:u(Y,{rows:f,label:`sessions`,isLoading:r.isLoading,loadError:r.isError?r.error?.message??`Request failed`:null,emptyText:`No active sessions.`,action:{label:`Sign out`,icon:ht,isAvailable:e=>e.status===`active`,onRun:e=>void p(e),busyId:l.isPending&&l.variables!==void 0&&`id`in l.variables?l.variables.id:null,disabled:l.isPending||d.busy},bulk:{revoke:d,noun:kt,isSelectable:e=>e.status===`active`}})})}n();function jt({open:e,users:t,onClose:n,onSubmit:i,submitting:a}){let{user:o}=st(),s=o?.isAdmin===!0?null:o?.scopes??[],[l,d]=c(t[0]?.id??``),[f,p]=c(``),[m,h]=c([{type:`capability`,target:``,access:[`view`,`create`]}]),[g,_]=c(``),v=se(m);return u(je,{open:e,onClose:n,title:`Create scoped token`,icon:q,width:`md`,testId:`create-scoped-token-dialog`,footer:r(k,{type:`button`,variant:`primary`,size:`sm`,onClick:()=>{let e=parseInt(g,10),t=Number.isFinite(e)&&e>0?Date.now()+e*864e5:void 0;i({userId:l,name:f.trim(),scopes:m,expiresAt:t})},disabled:!(l!==``&&f.trim().length>0&&m.length>0&&v===null&&!a),children:[a?u(C,{className:`h-3.5 w-3.5 animate-spin`,"aria-hidden":!0}):u(q,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),`Issue token`]}),children:r(`div`,{className:`space-y-3`,children:[r(`div`,{className:`space-y-1`,children:[u(U,{htmlFor:`scoped-token-user`,children:`On behalf of user`}),u(Te,{id:`scoped-token-user`,value:l,onChange:e=>d(e.target.value),options:t.map(e=>({value:e.id,label:e.username}))})]}),u(D,{label:`Name`,required:!0,children:u(V,{id:`scoped-token-name`,value:f,onChange:e=>p(e.target.value),placeholder:`cloudflare-tunnel-route`,autoFocus:!0})}),r(`div`,{className:`space-y-1`,children:[u(U,{children:`Scopes`}),u(re,{value:m,onChange:h,clampToParent:s}),v&&u(`p`,{className:`text-[10px] text-danger`,children:v}),u(`p`,{className:`text-[10px] text-foreground-subtle`,children:`Each scope picks a (cap or addon) target + one or more access flavours. The token can access ONLY what's listed.`})]}),r(`div`,{className:`space-y-1`,children:[u(U,{htmlFor:`scoped-token-days`,children:`Expires in (days)`}),u(V,{id:`scoped-token-days`,type:`number`,min:1,value:g,onChange:e=>_(e.target.value),placeholder:`30`}),u(`p`,{className:`text-[10px] text-foreground-subtle`,children:`Leave blank for non-expiring.`})]})]})})}var Mt={scoped:`newScoped`,admin:`newAdmin`};function Nt({token:e,label:t,onClose:n}){return u(b,{open:!0,onOpenChange:e=>!e&&n(),children:r(R,{width:`md`,scroll:`body`,children:[u(T,{children:u(M,{children:`Token: ${t}`})}),r(G,{className:`space-y-3`,children:[u(`div`,{className:`rounded-md border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning`,children:`Copy this token now. Once you close this dialog the secret cannot be displayed again.`}),u(`div`,{className:`select-all break-all rounded-md border border-border bg-background p-2 font-mono text-[11px] text-foreground`,children:e})]}),r(B,{children:[u(E,{value:e,label:`Copy`,srLabel:`token`}),u(k,{type:`button`,variant:`primary`,size:`sm`,onClick:n,children:`Done`})]})]})})}n();function Pt(e){return e.type===`device`?`device:[${ge(e.selector)}]`:`${e.type}:${e.target??``}`}function Ft(){let e=K(),t=s(),n=Xe(),{data:a,isLoading:o}=w(),l=i(()=>a??[],[a]),d=W.useQueries(e=>l.map(t=>e.userManagement.listScopedTokens({userId:t.id}))),[f,m]=c(null),[h,g]=c(null),{state:_,openNew:v,close:y}=ne({newKey:Mt.scoped}),b=()=>void t.invalidateQueries({queryKey:[[`userManagement`,`listScopedTokens`]]}),x=S({onSuccess:({token:e,record:t})=>{b(),y(),g({token:e,label:t.name})},onError:e=>m(`Create failed: ${e.message}`)}),C=Me({onSuccess:b,onError:e=>m(`Revoke failed: ${e.message}`)}),T=Date.now(),E=l.flatMap((e,t)=>(d[t]?.data??[]).map(t=>Ue(t,e.id,n,t.scopes.map(Pt).join(`, `),T))),D=d.find(e=>e.isError)?.error?.message??null,O=async t=>{await e({title:`Revoke "${t.name}"?`,message:`${t.ownerName}'s scoped token stops working on its next use. This cannot be undone.`,confirmLabel:`Revoke`,variant:`danger`})&&(m(null),C.mutate({id:t.id}))};return r(J,{notice:X(f),onDismissNotice:()=>{m(null)},id:`scoped-tokens`,title:`Scoped tokens`,icon:ye,description:`Tokens issued on behalf of a user for one narrow surface — an addon, an integration, a capability or some devices. Every token must be scoped, so revoking one has a predictable blast radius.`,actions:r(k,{type:`button`,variant:`primary`,size:`sm`,onClick:v,disabled:l.length===0,title:l.length===0?`Create a user first`:`Issue a scoped token`,children:[u(q,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),`New token`]}),children:[u(Y,{rows:E,label:`scoped tokens`,isLoading:o||d.some(e=>e.isLoading),loadError:D,emptyText:`No scoped tokens.`,action:{label:`Revoke`,icon:p,isAvailable:()=>!0,onRun:e=>void O(e),busyId:C.isPending?C.variables?.id??null:null,disabled:C.isPending}}),u(jt,{open:_.mode===`new`,users:l.map(e=>({id:e.id,username:e.username})),onClose:y,onSubmit:e=>x.mutate({userId:e.userId,name:e.name,scopes:e.scopes,...e.expiresAt===void 0?{}:{expiresAt:e.expiresAt}}),submitting:x.isPending}),h!==null&&u(Nt,{token:h.token,label:h.label,onClose:()=>g(null)})]})}n();var It={one:`admin token`,many:`admin tokens`},Lt=7,Rt=90,zt=80;function Bt(e){let t=Number(e);return Number.isInteger(t)&&t>=1&&t<=Rt?t:null}function Vt(){let{user:e}=st();return e?.isAdmin?u(Ht,{}):null}function Ht(){let e=K(),t=W.useUtils(),n=W.auth.listAdminTokens.useQuery(),a=Xe(),{requestProof:o,dialog:s}=Ve(`Creating an admin token`),{state:l,openNew:f,close:m}=ne({newKey:Mt.admin}),[h,g]=c(``),[_,v]=c(String(Lt)),[y,b]=c(null),[x,S]=c(null),[w,T]=c(!1),[E,D]=c(null),O=()=>{t.auth.listAdminTokens.invalidate(),t.auth.listAuthAudit.invalidate()},A=W.auth.createAdminToken.useMutation({onSuccess:({token:e,record:t})=>{O(),g(``),v(String(Lt)),S(null),m(),D({token:e,label:t.name})},onError:e=>{b(`Create failed: ${e.message}`),S(`Create failed: ${e.message}`)}}),j=W.auth.revokeAdminToken.useMutation({onSuccess:O,onError:e=>b(`Revoke failed: ${e.message}`)}),ee=Be(`admin-token`,It,O);d(()=>{l.mode===`closed`&&(g(``),v(String(Lt)),S(null),T(!1))},[l.mode]);let te=Date.now(),M=i(()=>(n.data??[]).map(e=>Le(e,a,te)),[n.data,a]),N=h.trim(),F=Bt(_),I=N.length>0&&N.length<=zt&&F!==null&&!A.isPending&&!w,L=async()=>{if(!I||F===null)return;b(null),S(null),T(!0);let e=await o();T(!1),e!==null&&A.mutate({name:N,expiresInDays:F,reauthToken:e})},R=async t=>{await e({title:`Revoke admin token "${t.name}"?`,message:`Every script using it loses access immediately. This cannot be undone.`,confirmLabel:`Revoke`,variant:`danger`})&&(b(null),j.mutate({id:t.id}))};return r(`div`,{"data-testid":`admin-tokens`,children:[s,u(J,{id:`admin-tokens`,title:`Admin tokens`,icon:P,tone:`danger`,notice:X(y,ee.outcome),onDismissNotice:()=>{b(null),ee.dismiss()},description:r(`div`,{className:`space-y-1`,children:[r(`p`,{children:[`An admin token acts as a `,u(`strong`,{children:`full administrator`}),` for scripts, e.g.`,` `,u(`code`,{className:`font-mono`,children:`node scripts/hub/hub.mjs --token-file <file> …`}),`. Anyone holding it can do anything an admin can.`]}),u(`p`,{children:`It expires in at most 90 days and every use is audited. Creating one asks you to confirm it is you.`})]}),actions:r(k,{type:`button`,variant:`danger`,size:`sm`,onClick:f,children:[u(P,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),`New admin token`]}),children:u(Y,{rows:M,label:`admin tokens`,isLoading:n.isLoading,loadError:n.isError?n.error?.message??`Request failed`:null,emptyText:`No admin tokens.`,action:{label:`Revoke`,icon:p,isAvailable:e=>e.status===`active`||e.status===`expired`,onRun:e=>void R(e),busyId:j.isPending?j.variables?.id??null:null,disabled:j.isPending||ee.busy},bulk:{revoke:ee,noun:It,isSelectable:e=>e.status===`active`||e.status===`expired`}})}),u(je,{open:l.mode===`new`,onClose:m,title:`Create admin token`,icon:P,error:x,width:`md`,testId:`admin-token-form-dialog`,footer:r(k,{type:`button`,variant:`danger`,size:`sm`,disabled:!I,onClick:()=>void L(),children:[A.isPending||w?u(C,{className:`h-3.5 w-3.5 animate-spin`,"aria-hidden":!0}):u(P,{className:`h-3.5 w-3.5`,"aria-hidden":!0}),`Create admin token`]}),children:r(`div`,{className:`space-y-3`,children:[r(`div`,{className:`space-y-1`,children:[u(U,{htmlFor:`admin-token-name`,children:`Token name`}),u(V,{id:`admin-token-name`,value:h,maxLength:zt,onChange:e=>g(e.target.value),placeholder:`nightly-backup`,autoFocus:!0})]}),r(`div`,{className:`space-y-1`,children:[u(U,{htmlFor:`admin-token-days`,children:`Expires in (days)`}),u(V,{id:`admin-token-days`,type:`number`,min:1,max:Rt,value:_,onChange:e=>v(e.target.value),state:F===null?`error`:`default`}),F===null?r(`p`,{className:`text-[11px] text-danger`,children:[`Expiry must be a whole number of days, 1 to `,Rt,`.`]}):r(`p`,{className:`text-[11px] text-foreground-subtle`,children:[`Whole number of days, 1 to `,Rt,`.`]})]})]})}),E&&u(Nt,{token:E.token,label:E.label,onClose:()=>D(null)})]})}var Ut={"share-link":`share links`,"admin-token":`admin tokens`,"login-session":`login sessions`};function Z(e,t){let n=e[t];return typeof n==`string`&&n.length>0?n:null}function Wt(e,t){let n=Z(e,`tokenId`);return Z(e,`name`)??(n===null?void 0:t.get(n))??n??`?`}function Gt(e){return e.filter(e=>e!==null)}function Kt(e){let t=Z(e,`prefix`);return t===null?null:`Prefix ${t}…`}function qt(e,t){let n=e.detail??{};switch(e.action){case`admin-token-created`:{let e=n.expiresAt;return{title:`Admin token "${Wt(n,t)}" created`,details:Gt([Kt(n),typeof e==`number`?`Expires ${qe(e)}`:null])}}case`admin-token-used`:{let e=Z(n,`path`);return{title:`Admin token "${Wt(n,t)}" used`,details:Gt([Kt(n),e===null?null:`Call ${e}`])}}case`admin-token-revoked`:return{title:`Admin token "${Wt(n,t)}" revoked`,details:Gt([Kt(n)])};case`credentials-revoked`:{let e=Z(n,`kind`)??`credential`,t=n.revoked,r=n.skipped,i=Ut[e]??`${e}s`;return{title:`${typeof t==`number`?String(t):`?`} ${i} revoked in bulk`,details:Gt([typeof r==`number`&&r>0?`${String(r)} skipped`:null,Z(n,`ids`)===null?null:`Ids ${Z(n,`ids`)??``}`])}}case`second-factor-skipped`:{let e=Z(n,`method`),t=Z(n,`factors`);return{title:`Second factor skipped on a trusted network`,details:Gt([e===null?null:`Signed in with ${Ge(e)}`,t===null?null:`Skipped: ${t.split(`,`).join(`, `)}`])}}default:return{title:e.action,details:Object.entries(n).map(([e,t])=>`${e}: ${String(t)}`)}}}n();var Jt=100;function Yt(){let{user:e}=st();return e?.isAdmin?u(Zt,{}):null}function Xt(){let{data:e}=w(),{data:t}=W.auth.listAdminTokens.useQuery();return i(()=>{let n=new Map;for(let t of e??[])n.set(t.id,t.username);let r=new Map;for(let e of t??[])r.set(e.id,e.name);return{userNameById:n,tokenNameById:r}},[e,t])}function Zt(){let e=W.auth.listAuthAudit.useQuery({limit:Jt}),{userNameById:t,tokenNameById:n}=Xt(),i=e.data??[];return r(`div`,{"data-testid":`auth-audit`,className:`rounded-lg border border-border bg-surface overflow-hidden`,children:[r(`div`,{className:`px-4 py-2.5 border-b border-border flex items-center gap-2`,children:[u(_,{className:`h-3.5 w-3.5 text-foreground-subtle`}),u(`div`,{className:`text-xs font-semibold text-foreground`,children:`Authentication audit`}),r(`span`,{className:`ml-auto text-[10px] text-foreground-subtle`,children:[`Latest `,Jt,`, newest first`]})]}),u(`div`,{className:`p-4`,children:e.isLoading?r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Loading audit trail…`]}):e.isError?r(`div`,{className:`text-[11px] text-danger`,children:[`Could not load the audit trail: `,e.error?.message??`Request failed`]}):i.length===0?r(`div`,{className:`rounded border border-dashed border-border px-3 py-4 text-xs text-foreground-subtle text-center space-y-1`,children:[u(`div`,{children:`No authentication events recorded yet.`}),u(`div`,{className:`text-[11px] text-foreground-subtle/90`,children:`This trail records admin-token create/use/revoke, trusted-network 2FA skips, and credential revokes (including single share-link revoke). Browser password logins are not listed here.`})]}):u(`ul`,{className:`divide-y divide-border rounded-md border border-border max-h-[420px] overflow-y-auto`,children:i.map(e=>u(Qt,{record:e,userName:e.userId===void 0?null:t.get(e.userId)??e.userId,tokenNameById:n},e.id))})})]})}function Qt({record:e,userName:t,tokenNameById:n}){let i=qt(e,n);return r(`li`,{"data-testid":`auth-audit-row`,className:`px-3 py-2 text-xs space-y-0.5`,children:[r(`div`,{className:`flex flex-wrap items-baseline gap-x-2`,children:[u(`span`,{className:`font-medium text-foreground`,children:i.title}),u(`span`,{className:`ml-auto text-[10px] text-foreground-subtle`,children:qe(e.at)})]}),r(`div`,{className:`flex flex-wrap gap-x-3 text-[11px] text-foreground-subtle`,children:[t!==null&&u(`span`,{className:`text-foreground`,children:t}),e.ip!==void 0&&u(`span`,{className:`font-mono`,children:e.ip}),i.details.map(e=>u(`span`,{className:`font-mono`,children:e},e))]})]})}var Q={loginSessions:`login-sessions`,linkedApps:`linked-apps`,scopedTokens:`scoped-tokens`,adminTokens:`admin-tokens`,shareLinks:`share-links`,legacyApiKeys:`legacy-api-keys`,externalIdentities:`external-identities`,audit:`auth-audit`},$t={state:`ok`,stateLabel:`available`,showState:!1};function $(e,t,n,r){return{id:e,name:t,fallbackIcon:n,...$t,render:r}}function en(){let e=L(),t=(e.data??[]).length>0;return u(yt,{embedded:!0,icon:wt,title:`Tokens & sessions`,subtitle:`Every credential this hub honours — who holds it, what it can reach, when it was last used and when it expires. Pick a kind; revoke one, or select several and revoke them after typing the count.`,groups:[{id:`sessions`,label:`Sessions`,entries:[$(Q.loginSessions,`Login sessions`,Pe,()=>u(At,{})),$(Q.linkedApps,`Linked apps`,ae,()=>u(Ot,{}))]},{id:`tokens`,label:`Tokens`,entries:[$(Q.scopedTokens,`Scoped tokens`,ye,()=>u(Ft,{})),$(Q.adminTokens,`Admin tokens`,P,()=>u(Vt,{})),$(Q.shareLinks,`Share links`,H,()=>u(We,{})),...t?[$(Q.legacyApiKeys,`Legacy API keys`,Ne,()=>u(Et,{}))]:[]]},{id:`identity`,label:`Identity`,entries:[$(Q.externalIdentities,`Sign-in links`,ut,()=>u(Tt,{})),$(Q.audit,`Audit`,_,()=>u(Yt,{}))]}],isLoading:e.isLoading,emptyTitle:`No credential kinds`,emptyDescription:`This hub offers no credential kinds.`})}n();var tn=[`oidcConnections`,`listConnections`],nn=[`oidcConnections`,`connectionFormSchema`],rn={displayName:`OpenID Connect`,icon:`shield-check`,issuerUrl:``,clientId:``,clientSecret:``,redirectUri:``,scopes:`openid profile email`,usernameClaim:`preferred_username`},an=/^[a-z0-9][a-z0-9-]{0,63}$/;function on(e){return e instanceof Error?e.message:String(e)}function sn(e,t){if(t)return typeof e==`string`&&e.length>0?e:void 0;if($e(e))return tt;if(typeof e==`string`&&e.length>0)return e;Qe(e)}function cn(e){return typeof e!=`object`||!e?!1:Array.isArray(Reflect.get(e,`sections`))}function ln({addonId:n}){let f=h(),m=s(),g=K(),_=ne({editKey:`oidcEdit`,newKey:`oidcNew`}),[v,y]=c(!1),[b,x]=c(null),[S,C]=c(``),[w,T]=c({...rn}),E=t(null),D=a({queryKey:[...tn,n],queryFn:async()=>f.trpcClient.oidcConnections.listConnections.query({})}),O=a({queryKey:nn,queryFn:async()=>f.trpcClient.oidcConnections.connectionFormSchema.query({})}),A=D.data??[],j=cn(O.data)?O.data:void 0,te=_.state,M=te.mode===`edit`?A.find(e=>e.id===te.id)??null:null;d(()=>{if(_.state.mode===`edit`&&M!==null){let e=`edit:${M.id}`;if(E.current===e)return;E.current=e,x(null),C(M.id),T(M.formValues===void 0?{...rn}:{...M.formValues});return}if(_.state.mode===`new`){if(E.current===`new`)return;E.current=`new`,x(null),C(``),T({...rn})}},[M,_.state]);let N=e(async()=>{await m.invalidateQueries({queryKey:tn})},[m]),P=l({mutationFn:async e=>f.trpcClient.oidcConnections.deleteConnection.mutate({id:e}),onSuccess:async()=>N()}),F=e(async e=>{await g({title:`Delete OIDC connection?`,message:`Remove “${e.displayName}” (${e.id}). The login button and IdP credentials for this id are dropped.`,confirmLabel:`Delete`,cancelLabel:`Cancel`,variant:`danger`})&&(await P.mutateAsync(e.id),_.state.mode===`edit`&&_.state.id===e.id&&(_.close(),E.current=null))},[g,P,_]),I=e(async()=>{let e=w.displayName,t=typeof e==`string`&&e.trim()!==``?e.trim():``;if(t===``){x(`Display name is required.`);return}let n=_.state.mode===`new`,r=n?S.trim():M?.id??``;if(n&&!an.test(r)){x(`Instance id must be a lowercase slug (e.g. authentik, google).`);return}if(r===``){x(`That connection is no longer available.`);return}let i=sn(w.clientSecret,n),a=w.icon,o=w.issuerUrl,s=w.clientId,c=w.redirectUri,l=w.scopes,u=w.usernameClaim,d=u===`email`||u===`sub`?u:`preferred_username`;y(!0),x(null);try{n&&await f.trpcClient.oidcConnections.createConnection.mutate({id:r,displayName:t}),await f.trpcClient.oidcConnections.updateConnection.mutate({id:r,displayName:t,icon:typeof a==`string`?a:`shield-check`,issuerUrl:typeof o==`string`?o:``,clientId:typeof s==`string`?s:``,...i===void 0?{}:{clientSecret:i},redirectUri:typeof c==`string`?c:``,scopes:typeof l==`string`?l:`openid profile email`,usernameClaim:d}),await N(),_.close(),E.current=null}catch(e){x(on(e))}finally{y(!1)}},[S,M?.id,_,w,N,f]),L=i(()=>[{key:`id`,header:`Id`,sortValue:e=>e.id,render:e=>u(`span`,{className:`font-mono text-xs text-foreground`,children:e.id})},{key:`name`,header:`Name`,sortValue:e=>e.displayName,render:e=>u(`span`,{className:`truncate font-medium text-foreground`,children:e.displayName})},{key:`issuer`,header:`Issuer`,sortValue:e=>e.issuerUrl,render:e=>u(`span`,{className:`truncate text-xs text-foreground-muted`,children:e.issuerUrl===``?`—`:e.issuerUrl})},{key:`status`,header:`Status`,notSortableReason:`Badge`,render:e=>u(De,{variant:e.configured?`success`:`warning`,children:e.configured?`Configured`:`Not configured`})},{key:`actions`,header:``,align:`right`,notSortableReason:`Row actions`,nowrap:!0,render:e=>r(`div`,{className:`flex justify-end gap-1`,children:[u(k,{type:`button`,size:`sm`,variant:`secondary`,onClick:t=>{t.stopPropagation(),_.openEdit(e.id)},children:`Edit`}),u(k,{type:`button`,size:`sm`,variant:`ghost`,"aria-label":`Delete ${e.displayName}`,title:`Delete`,onClick:t=>{t.stopPropagation(),F(e)},children:u(p,{className:`h-3.5 w-3.5 text-danger`})})]})}],[_,F]),R=_.state.mode===`new`,z=R||M!==null,re=R?`new`:M===null?`closed`:`edit:${M.id}:${M.configured?`cfg`:`empty`}`;return r(`div`,{className:`flex min-h-0 flex-col gap-3`,"data-testid":`oidc-settings-connections`,children:[r(`div`,{className:`flex flex-wrap items-center justify-between gap-2`,children:[r(`p`,{className:`text-sm text-foreground-muted`,children:[`Each row is one IdP. Redirect URI:`,` `,u(`span`,{className:`font-mono text-xs`,children:`/addon/${n}/<id>/callback`})]}),r(k,{type:`button`,size:`sm`,"data-testid":`oidc-connections-add`,onClick:()=>_.openNew(),children:[u(ve,{className:`h-3.5 w-3.5`}),`Add connection`]})]}),D.isError?u(`p`,{className:`text-sm text-danger`,children:`Could not load connections.`}):null,D.isPending?u(me,{className:`h-32 w-full`}):u(ee,{columns:L,rows:A,rowKey:e=>e.id,pageSize:25,mobileMode:`scroll`,emptyMessage:`No OIDC connections yet. Add one to register an IdP.`,onRowClick:e=>_.openEdit(e.id),defaultSort:{columnKey:`name`,dir:`asc`}}),u(je,{open:z,onClose:()=>{_.close(),x(null),E.current=null},title:R?`Add OIDC connection`:`Edit ${M?.displayName??`connection`}`,icon:q,width:`xl`,testId:`oidc-connection-editor`,error:b,footer:u(k,{type:`button`,size:`sm`,disabled:v||R&&S.trim()===``,onClick:()=>void I(),children:v?`Saving…`:`Save`}),children:z?r(o,{children:[R?r(`div`,{children:[u(U,{htmlFor:`oidc-connection-id`,children:`Instance id`}),u(V,{id:`oidc-connection-id`,value:S,onChange:e=>C(e.target.value),placeholder:`authentik`,autoComplete:`off`}),u(`p`,{className:`mt-1 text-xs text-foreground-muted`,children:`URL slug — appears in /addon/${n}/<id>/callback. Stable; renaming is not supported after save.`})]}):r(`p`,{className:`text-sm text-foreground-muted`,children:[`Instance id: `,u(`span`,{className:`font-mono`,children:M?.id})]}),j===void 0?u(`p`,{className:`text-sm text-foreground-muted`,children:`Loading form…`}):u(ue,{schema:j,values:w,onChange:T,bare:!0},re)]}):null})]})}n();var un=3e4,dn=[{capName:`auth-provider`,stageBadge:null},{capName:`user-passkeys`,stageBadge:`Second factor`}],fn={title:`Authentication`,subtitle:`Login providers (local password, OIDC, SAML) and second-factor providers (passkeys / WebAuthn). They run in parallel — enable or disable each independently; disabling one only removes that method, local password login is unaffected.`,emptyTitle:`No authentication providers registered`,emptyDescription:`Local auth should always be available — if this list is empty the local-auth builtin failed to register. Check the Addons page for load errors.`,installLabel:`Add provider`};function pn(e){return e===`local-auth`?{headline:`Username + password (local).`,icon:wt,bullets:[`Users live in the hub’s SQLite database — no IdP roundtrip.`,`TOTP / 2FA is self-enrolled: each user enables it from their own profile page (My access → Two-factor authentication).`,`Scoped tokens, admin tokens and sessions are managed from Identity → Tokens & sessions.`],setupSteps:[`A fresh hub has one account, admin / changeme — the first sign-in asks for a new password. Add users from Users → New user.`,`Hand out either passwords or scoped tokens — both validate through this provider.`]}:e.startsWith(`auth-oidc`)?{headline:`OpenID Connect (Google, Microsoft, Okta, Keycloak, …).`,icon:pt,bullets:[`Three-leg redirect flow with PKCE S256 + nonce.`,`id_token is signature-verified against the IdP’s JWKS — no implicit trust.`,`Explicit linking only: an IdP account signs in only the CamStack user who linked it from their profile (My access → Linked sign-in accounts). No auto-created users, no email matching.`,`The linked user’s role, scopes and two-factor authentication apply.`],setupSteps:[`Register a web app at your IdP and copy the client_id + client_secret.`,"Choose a stable instance id (slug). Set the IdP redirect URI to `https://<this-hub>/addon/auth-oidc/<id>/callback`.",`In Settings below, Add connection — enter the id, issuer, and credentials; Save.`,`Each user then links their IdP account from My access → Linked sign-in accounts.`]}:e.startsWith(`auth-saml`)?{headline:`SAML 2.0 — enterprise SSO (Azure AD, Okta, OneLogin).`,icon:pt,bullets:[`Heavier handshake than OIDC — exchange metadata XML with your IdP.`,`Group → role mapping via SAML assertions.`]}:e.startsWith(`auth-webauthn`)||e.startsWith(`auth-passkey`)?{headline:`Passkeys / WebAuthn — passwordless second factor.`,icon:pe,bullets:[`Enroll a YubiKey, Touch ID, or platform passkey per user.`,`Resistant to phishing — the browser binds the credential to your hub origin.`]}:e.startsWith(`auth-totp`)?{headline:`TOTP — second factor.`,icon:pe,bullets:[`Time-based codes from an authenticator app (RFC 6238).`,`Compatible with Google Authenticator, Aegis, 1Password, Bitwarden, etc.`]}:{headline:`Authentication provider.`,icon:q,bullets:[`Generic authentication provider. No additional hints registered.`]}}function mn(){let e=z({capName:dn[0].capName},{refetchInterval:un}),t=z({capName:dn[1].capName},{refetchInterval:un}),n=xe(void 0,{refetchInterval:un}),r=i(()=>St(n.data??[]),[n.data]),a=i(()=>{let n=[],i=[e.data??[],t.data??[]];for(let[e,t]of dn.entries())for(let a of i[e]??[])n.push({addonId:a.addonId,displayName:r.get(a.addonId)??a.addonId,isActive:a.isActive,capName:t.capName,stageBadge:t.stageBadge});return n},[e.data,t.data,r]),o=[{id:`authentication`,label:`Authentication providers`,install:{capability:dn[0].capName,label:fn.installLabel},entries:a.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:q,...xt(e.isActive),render:()=>u(bt,{addonId:e.addonId,displayName:e.displayName,capName:e.capName,isActive:e.isActive,badges:e.stageBadge===null?null:u(De,{variant:`info`,className:vt,children:e.stageBadge}),settingsContent:e.addonId.startsWith(`auth-oidc`)?u(ln,{addonId:e.addonId}):void 0,panels:[{id:`about`,title:`About`,icon:pn(e.addonId).icon,content:u(hn,{addonId:e.addonId})}]})}))}];return u(yt,{embedded:!0,icon:q,title:fn.title,subtitle:fn.subtitle,groups:o,isLoading:e.isLoading||t.isLoading,emptyTitle:fn.emptyTitle,emptyDescription:fn.emptyDescription})}function hn({addonId:e}){let t=pn(e),n=t.icon;return r(`div`,{className:`space-y-3 rounded-lg border border-border bg-surface p-2 sm:p-4`,children:[r(`div`,{className:`flex items-start gap-2.5`,children:[u(n,{className:`h-4 w-4 mt-0.5 text-primary shrink-0`}),u(`div`,{className:`text-xs text-foreground font-medium`,children:t.headline})]}),u(`ul`,{className:`text-xs text-foreground-subtle space-y-1.5 pl-6 list-disc`,children:t.bullets.map((e,t)=>u(`li`,{children:e},t))}),t.setupSteps&&t.setupSteps.length>0&&r(`div`,{className:`rounded-md border border-border bg-surface-hover/40 px-3 py-2.5 space-y-1.5`,children:[r(`div`,{className:`flex items-center gap-1.5 text-[10px] font-semibold text-foreground-subtle uppercase tracking-wide`,children:[u(Ct,{className:`h-3 w-3`}),`Setup steps`]}),u(`ol`,{className:`text-xs text-foreground space-y-0.5 pl-4 list-decimal`,children:t.setupSteps.map((e,t)=>u(`li`,{children:e},t))})]}),e===`local-auth`&&r(`div`,{className:`rounded-md border border-info/30 bg-info/5 px-3 py-2 flex items-start gap-2 text-[11px] text-foreground`,children:[u(I,{className:`h-3.5 w-3.5 mt-0.5 text-info shrink-0`}),r(`span`,{children:[`TOTP / 2FA is self-service: each user enrols from their own profile page —`,` `,u(lt,{to:`/system/my-access`,className:`underline text-primary`,children:`My access`}),` `,`→ “Two-factor authentication” → Enable 2FA. Admins can only remove a user’s active enrollment (lost device) from the`,` `,u(lt,{to:`/system/identity?tab=users`,className:`underline text-primary`,children:`Users`}),` `,`page via Actions → “Remove 2FA”.`]})]})]})}function gn({userId:e,username:t,onClose:n}){let i=K(),a=W.useUtils(),o=ie({userId:e}),s=W.auth.listLoginMethods.useQuery(),c=fe({onSuccess:()=>void a.userManagement.listExternalIdentities.invalidate()}),l=new Map((s.data??[]).flatMap(e=>e.kind===`redirect`?[[e.id,e.label]]:[])),d=o.data??[],f=async(n,r)=>{await i({title:`Remove sign-in link`,message:`"${t}" will no longer be able to sign in with this ${r} account.`,confirmLabel:`Remove`,variant:`danger`})&&c.mutate({userId:e,provider:n.provider,subject:n.subject})};return u(b,{open:!0,onOpenChange:e=>!e&&n(),children:r(R,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,"data-testid":`external-identities-modal`,children:[u(T,{children:r(M,{className:`flex items-center gap-2`,children:[u(ae,{className:`h-4 w-4 text-primary`}),r(`span`,{children:[`Linked sign-in accounts`,r(`span`,{className:`mt-0.5 block text-[11px] font-normal text-foreground-subtle`,children:[`User: `,u(`span`,{className:`font-mono`,children:t})]})]})]})}),u(G,{className:`space-y-3`,children:o.isLoading?r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Loading…`]}):d.length===0?u(`p`,{className:`text-xs text-foreground-subtle`,children:`No linked accounts. Only the user can link one, from their own profile page.`}):u(`ul`,{className:`divide-y divide-border rounded-md border border-border`,children:d.map(e=>{let t=l.get(e.provider),n=t??e.provider;return r(`li`,{className:`flex items-center gap-3 px-3 py-2 text-xs`,children:[r(`div`,{className:`flex-1 min-w-0`,children:[r(`div`,{className:`font-medium text-foreground truncate`,children:[n,t===void 0&&u(`span`,{className:`ml-2 rounded bg-foreground-subtle/15 px-1.5 py-0.5 text-[10px] font-normal text-foreground-subtle`,children:`provider unavailable`})]}),r(`div`,{className:`text-[11px] text-foreground-subtle truncate`,children:[e.label??e.subject,` ·`,` `,new Date(e.linkedAt).toLocaleDateString()]})]}),r(`button`,{onClick:()=>void f(e,n),disabled:c.isPending,className:`inline-flex items-center gap-1 rounded px-2 py-1 text-[10px] text-danger hover:bg-danger/10 disabled:opacity-50`,children:[u(Ie,{className:`h-3 w-3`}),`Remove`]})]},`${e.provider}|${e.subject}`)})})}),u(B,{children:u(k,{onClick:n,variant:`secondary`,children:`Close`})})]})})}function _n({userId:e,username:t,onClose:n}){let i=s(),a=K(),c=be({userId:e}),l=c.data,d=()=>i.invalidateQueries({queryKey:[[`userManagement`,`getTotpStatus`]]}),f=y({onSuccess:()=>{d(),n()}}),p=c.isLoading?`loading`:l?.enabled?`enrolled`:`not_enrolled`;return u(b,{open:!0,onOpenChange:e=>!e&&n(),children:r(R,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,children:[u(T,{children:r(M,{className:`flex items-center gap-2`,children:[u(pe,{className:`h-4 w-4 text-primary`}),r(`span`,{children:[`Two-factor authentication`,r(`span`,{className:`mt-0.5 block text-[11px] font-normal text-foreground-subtle`,children:[`User: `,u(`span`,{className:`font-mono`,children:t})]})]})]})}),r(G,{className:`space-y-4`,children:[p===`loading`&&r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Checking status…`]}),p===`not_enrolled`&&r(`div`,{className:`rounded-md border border-border bg-surface-hover/30 px-3 py-2.5 flex items-start gap-2.5 text-xs text-foreground-subtle`,children:[u(I,{className:`h-4 w-4 mt-0.5 shrink-0`}),r(`div`,{children:[u(`div`,{className:`font-medium text-foreground`,children:`2FA not enrolled`}),u(`p`,{className:`mt-0.5`,children:`Only the user themselves can enable 2FA, from their own profile page.`})]})]}),p===`enrolled`&&l&&r(o,{children:[r(`div`,{className:`rounded-md border border-emerald-500/30 bg-emerald-500/5 px-3 py-2.5 flex items-start gap-2.5`,children:[u(_e,{className:`h-4 w-4 mt-0.5 text-emerald-500 shrink-0`}),r(`div`,{className:`text-xs text-foreground space-y-0.5`,children:[u(`div`,{className:`font-medium`,children:`2FA is active`}),r(`div`,{className:`text-foreground-subtle`,children:[`Enrolled on`,` `,l.confirmedAt?new Date(l.confirmedAt).toLocaleString():`—`]})]})]}),u(`div`,{className:`text-[11px] text-foreground-subtle`,children:`Removing here clears the server-side secret. Use this only when the user has lost access — they'll have to re-enroll from their profile page afterwards.`}),r(`div`,{className:`flex justify-end gap-2`,children:[u(k,{onClick:n,variant:`secondary`,children:`Close`}),r(`button`,{onClick:async()=>{await a({title:`Remove 2FA`,message:`Remove two-factor authentication for "${t}"? They'll be able to log in with just their password until they re-enroll from their own profile page.`,confirmLabel:`Remove`,variant:`danger`})&&f.mutate({userId:e})},disabled:f.isPending,className:`rounded bg-danger px-3 py-1.5 text-xs font-medium text-white hover:bg-danger/90 disabled:opacity-50 inline-flex items-center gap-1`,children:[f.isPending?u(C,{className:`h-3 w-3 animate-spin`}):u(we,{className:`h-3 w-3`}),`Remove 2FA`]})]})]})]})]})})}n();function vn({userId:e,username:t,onClose:n}){let{user:i,logout:a}=st(),o=i?.id===e,s=K(),l=W.useUtils(),d=W.auth.listSessions.useQuery({userId:e}),[f,p]=c(null),m=W.auth.revokeSessions.useMutation({onSuccess:(e,t)=>{if(o&&`userId`in t){a();return}l.auth.listSessions.invalidate()},onError:e=>p(`Sign out failed: ${e.message}`)}),h=async e=>{await s({title:`Sign out this session`,message:`"${t}" on ${Fe(e.client).label} will have to sign in again.`,confirmLabel:`Sign out`,variant:`danger`})&&(p(null),m.mutate({id:e.id}))},g=async()=>{await s({title:`Sign out everywhere`,message:o?`Every session of your own account ends, this browser included: you will be signed out here too.`:`Every session of "${t}" ends at once; each device has to sign in again.`,confirmLabel:`Sign out everywhere`,variant:`danger`})&&(p(null),m.mutate({userId:e}))},_=m.isPending&&m.variables!==void 0&&`id`in m.variables?m.variables.id:null;return u(b,{open:!0,onOpenChange:e=>!e&&n(),children:r(R,{width:`lg`,placement:`sheet-on-mobile`,scroll:`body`,"data-testid":`user-sessions-modal`,children:[u(T,{children:r(M,{className:`flex items-center gap-2`,children:[u(Pe,{className:`h-4 w-4 text-primary`}),r(`span`,{children:[`Active sessions`,r(`span`,{className:`mt-0.5 block text-[11px] font-normal text-foreground-subtle`,children:[`User: `,u(`span`,{className:`font-mono`,children:t})]})]})]})}),r(G,{className:`space-y-3`,children:[f!==null&&u(`div`,{className:`text-[11px] text-danger`,children:f}),u(Je,{sessions:d.data??[],isLoading:d.isLoading,loadError:d.isError?d.error?.message??`Request failed`:null,emptyText:`No active sessions.`,onRevoke:e=>void h(e),revokingId:_})]}),r(B,{className:`justify-between`,children:[r(`button`,{onClick:()=>void g(),disabled:m.isPending||(d.data??[]).length===0,className:`inline-flex items-center gap-1.5 rounded border border-danger/40 px-2.5 py-1 text-[11px] font-medium text-danger hover:bg-danger/10 disabled:opacity-50`,children:[m.isPending&&_===null?u(C,{className:`h-3 w-3 animate-spin`}):u(ht,{className:`h-3 w-3`}),`Sign out everywhere`]}),u(k,{onClick:n,variant:`secondary`,children:`Close`})]})]})})}function yn({icon:e,label:t,count:n,all:i,total:a}){return r(`div`,{className:`flex items-center gap-2 text-[11px]`,children:[u(e,{className:`h-3 w-3 shrink-0 text-foreground-subtle`}),u(`span`,{className:`min-w-0 flex-1 text-foreground-subtle`,children:t}),u(`span`,{className:n===0?`text-foreground-subtle`:`font-medium text-foreground`,children:n===0?`none`:i?`all ${a}`:`${n} of ${a}`})]})}function bn({scopes:e,isAdmin:t,invalidReason:n}){let i=h(),s=a({queryKey:[`auth`,`previewScope`,e,t],enabled:n===null,queryFn:()=>i.trpcClient.auth.previewScope.query({scopes:[...e],isAdmin:t})}),{data:c}=Oe({projection:`slim`}),l=c?.length??0;if(n!==null)return u(`div`,{className:`rounded-lg border border-border bg-surface p-3`,children:r(`p`,{className:`flex items-center gap-1.5 text-[11px] text-warning`,children:[u(P,{className:`h-3 w-3 shrink-0`}),n]})});if(s.isLoading)return r(`div`,{className:`flex items-center gap-2 rounded-lg border border-border bg-surface p-3 text-[11px] text-foreground-subtle`,children:[u(C,{className:`h-3 w-3 animate-spin`}),`Resolving…`]});if(s.isError||!s.data)return u(`div`,{className:`rounded-lg border border-border bg-surface p-3`,children:u(`p`,{className:`text-[11px] text-danger`,children:`Could not resolve this grant set. Saving is still allowed — the server enforces the same rules either way.`})});let d=s.data;return u(`div`,{className:`space-y-2 rounded-lg border border-border bg-surface p-3`,children:d.isAdmin?r(`p`,{className:`text-[11px] text-foreground`,children:[u(`span`,{className:`font-medium`,children:`Administrator`}),` — unrestricted. Grants are ignored for an admin.`]}):r(o,{children:[u(yn,{icon:O,label:`Devices they can see`,count:d.device.view.deviceCount,all:d.device.view.all,total:l}),u(yn,{icon:Ce,label:`Devices they can operate`,count:d.device.create.deviceCount,all:d.device.create.all,total:l}),u(yn,{icon:p,label:`Devices they can delete`,count:d.device.delete.deviceCount,all:d.device.delete.all,total:l}),r(`div`,{className:`border-t border-border pt-2 text-[10px] text-foreground-subtle`,children:[`System access: `,d.system.length===0?`none`:d.system.join(`, `),d.capabilities.length>0&&r(o,{children:[` · Extra capabilities: `,d.capabilities.map(e=>e.name).join(`, `)]}),d.addons.length>0&&r(o,{children:[` · Addons: `,d.addons.map(e=>e.name).join(`, `)]})]}),d.device.view.deviceCount===0&&u(`p`,{className:`text-[10px] text-warning`,children:`This user will not be able to see any device.`})]})})}n();function xn(e){let t=nt(e);return t===null?{mode:e.length===0?`role`:`advanced`,roleId:null,selector:{kind:`all`},includeLinked:!0,advancedScopes:e}:{mode:`role`,roleId:t.roleId,selector:t.selector,includeLinked:t.includeLinked,advancedScopes:e}}function Sn(e){return e.mode===`advanced`?e.advancedScopes:e.roleId===null?[]:Ze({roleId:e.roleId,selector:e.selector,includeLinked:e.includeLinked})}function Cn({user:e,onClose:t,onSubmit:n,submitting:a,callerScopes:s}){let l=i(()=>xn(e.scopes??[]),[e.scopes]),[d,p]=c(l),[h,g]=c(l.mode===`advanced`),_=i(()=>Sn(d),[d]),v=!e.isAdmin&&d.mode===`role`&&d.roleId===null,y=i(()=>e.isAdmin?null:d.mode===`role`&&d.roleId===null?`Pick what this user can do to see the result.`:se(_),[e.isAdmin,d.mode,d.roleId,_]),x=e=>p({...d,...e});return u(b,{open:!0,onOpenChange:e=>!e&&t(),children:r(R,{width:`lg`,placement:`sheet-on-mobile`,scroll:`body`,className:`md:max-w-2xl`,children:[r(T,{children:[r(M,{children:[`Access — `,e.username]}),u(`p`,{className:`text-[10px] text-foreground-subtle`,children:e.isAdmin?`This user is an administrator — they can do everything, and the settings below are ignored.`:`Choose what they can do, then which devices it applies to.`})]}),r(G,{className:`space-y-4`,children:[v&&r(`div`,{className:`flex items-start gap-2 rounded-lg border border-warning/40 bg-warning/10 p-3`,children:[u(P,{className:`mt-0.5 h-3.5 w-3.5 shrink-0 text-warning`}),r(`p`,{className:`text-[11px] text-foreground`,children:[u(`span`,{className:`font-medium`,children:`No access.`}),` `,r(`span`,{className:`text-foreground-subtle`,children:[e.username,` cannot see or do anything yet. Configure it below.`]})]})]}),r(`section`,{className:`space-y-2`,children:[u(`h3`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:`1 · What can they do`}),u(`div`,{className:`space-y-1.5`,children:et.map(e=>{let t=d.mode===`role`&&d.roleId===e.id;return r(`label`,{className:`flex cursor-pointer items-start gap-2 rounded-lg border p-2.5 transition-colors ${t?`border-primary bg-primary/5`:`border-border hover:bg-foreground-subtle/5`}`,children:[u(`input`,{type:`radio`,name:`access-role`,checked:t,onChange:()=>x({mode:`role`,roleId:e.id}),className:`mt-0.5 border-border focus:ring-primary`}),r(`span`,{className:`min-w-0`,children:[u(`span`,{className:`block text-[12px] font-medium text-foreground`,children:e.label}),u(`span`,{className:`mt-0.5 block text-[10px] text-foreground-subtle`,children:e.description})]})]},e.id)})}),r(`button`,{type:`button`,onClick:()=>{if(!h){p({...d,mode:`advanced`,advancedScopes:_}),g(!0);return}g(!1),p(xn(d.advancedScopes))},className:`inline-flex items-center gap-1 rounded px-1.5 py-1 text-[10px] text-foreground-subtle transition-colors hover:bg-foreground-subtle/10 hover:text-foreground`,children:[u(h?f:m,{className:`h-3 w-3`}),`Advanced — edit raw grants`]}),h&&r(`div`,{className:`rounded-lg border border-border p-2`,children:[u(`p`,{className:`mb-2 text-[10px] text-foreground-subtle`,children:`One row per grant. Anything expressible here overrides the three roles above; close this section to go back to them.`}),u(re,{value:d.advancedScopes,onChange:e=>x({mode:`advanced`,advancedScopes:e}),clampToParent:s,emptyHint:r(o,{children:[`No grants.`,` `,u(`span`,{className:`font-medium text-foreground`,children:e.username}),` cannot call any protected endpoint until one is added.`]})})]})]}),d.mode===`role`&&r(`section`,{className:`space-y-2`,children:[u(`h3`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:`2 · On which devices`}),u(he,{value:d.selector,onChange:e=>x({selector:e}),includeLinked:d.includeLinked,onIncludeLinkedChange:e=>x({includeLinked:e}),clampToDeviceIds:null})]}),r(`section`,{className:`space-y-2`,children:[u(`h3`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:d.mode===`role`?`3 · Result`:`2 · Result`}),u(bn,{scopes:_,isAdmin:e.isAdmin,invalidReason:y})]})]}),r(B,{children:[u(k,{variant:`outline`,size:`sm`,onClick:t,children:`Cancel`}),u(k,{size:`sm`,onClick:()=>n([..._]),disabled:a||y!==null,children:a?u(C,{className:`h-3 w-3 animate-spin`}):`Save access`})]})]})})}n();var wn=8;function Tn(){return{username:``,password:``,isAdmin:!1}}function En(e){return{id:e.id,username:e.username,password:``,isAdmin:e.isAdmin}}function Dn(e){return e instanceof Error?e.message:`Something went wrong`}function On(){let e=s(),{user:t}=st(),{state:n,openNew:i,openEdit:a,close:l}=ne(),[f,m]=c(null),[h,g]=c(null),[_,y]=c(!1),{data:b,isLoading:S,isError:T,error:E,refetch:O}=w(),M=K(),P=Ee(),F=()=>e.invalidateQueries({queryKey:[[`userManagement`,`listUsers`]]}),I=oe(),L=Se({onSuccess:F}),R=A({onSuccess:F}),z=te({onSuccess:F}),re=x({onSuccess:()=>{F(),se(null)}}),[ie,se]=c(null),[ce,B]=c(null),[le,ue]=c(null),[fe,me]=c(null),he=b??[],H=n.mode===`edit`?he.find(e=>e.id===n.id)??null:null,ge=()=>{m(null),g(null),y(!1),I.reset(),R.reset(),l()};d(()=>{if(n.mode===`closed`){m(null),g(null),y(!1);return}if(n.mode===`new`){m(e=>e!==null&&e.id===void 0?e:Tn());return}if(H===null){S||l();return}m(e=>e?.id===H.id?e:En(H))},[n,H,S,l]);let _e=n.mode===`new`&&f!==null&&f.id===void 0||n.mode===`edit`&&f!==null&&H!==null;async function U(e){await M({title:`Delete user`,message:`Permanently delete user "${e.username}"? This cannot be undone — any active sessions will be revoked on next request.`,confirmLabel:`Delete`,variant:`danger`})&&(L.mutate({id:e.id}),n.mode===`edit`&&n.id===e.id&&ge())}async function W(e){let t=await P({title:`Reset password`,message:`Set a new password for "${e.username}".`,label:`New password`,inputType:`password`,placeholder:`At least 8 characters`,confirmLabel:`Continue`,validate:e=>e.length<wn?`Password must be at least ${String(wn)} characters.`:null});t===null||t.length===0||await M({title:`Reset password`,message:`Reset the password for "${e.username}"? They will need to use the new password on their next login.`,confirmLabel:`Reset`})&&z.mutate({id:e.id,newPassword:t})}function ye(e,t){t!==e.isAdmin&&R.mutate({id:e.id,isAdmin:t})}async function be(){if(f!==null){g(null);try{if(f.id===void 0){let e=f.username.trim();if(e===``||f.password.length<wn)return;await I.mutateAsync({username:e,password:f.password,isAdmin:f.isAdmin})}else await R.mutateAsync({id:f.id,isAdmin:f.isAdmin});F(),ge()}catch(e){g(Dn(e))}}}function G(e){return e?new Date(typeof e==`number`?e:String(e)).toLocaleDateString(`en-GB`,{day:`2-digit`,month:`short`,year:`numeric`}):`—`}let xe=I.isPending||R.isPending,we=f!==null&&f.id===void 0&&f.username.trim()!==``&&f.password.length>=wn,Te=f!==null&&f.id!==void 0,De=xe||(f?.id===void 0?!we:!Te),Oe=e=>L.isPending&&L.variables?.id===e.id||R.isPending&&R.variables?.id===e.id||z.isPending&&z.variables?.id===e.id;return r(_t,{embedded:!0,icon:Ae,title:`Users`,subtitle:`Hub accounts, admin flag, access scopes, sessions and sign-in links.`,actions:r(k,{size:`sm`,type:`button`,onClick:()=>{g(null),i()},children:[u(ve,{className:`h-3.5 w-3.5`}),`Create User`]}),children:[T&&u(j,{title:`Could not load users`,message:Dn(E),onRetry:()=>void O()}),S&&r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Loading…`]}),!S&&!T&&he.length===0&&u(de,{icon:Ae,title:`No users yet`,description:`Create a user to grant hub access.`}),!S&&!T&&he.length>0&&u(ee,{columns:[{key:`username`,header:`Username`,sortValue:e=>e.username,render:e=>u(`span`,{className:`font-medium text-foreground`,"data-testid":`user-open-${e.id}`,children:e.username})},{key:`isAdmin`,header:`Admin`,nowrap:!0,sortValue:e=>+!!e.isAdmin,render:e=>u(`span`,{onClick:e=>e.stopPropagation(),children:r(`label`,{className:`inline-flex items-center gap-1.5 text-xs cursor-pointer`,children:[u(`input`,{type:`checkbox`,checked:e.isAdmin,onChange:t=>ye(e,t.target.checked),disabled:Oe(e),className:`rounded border-border focus:ring-primary disabled:opacity-50`}),u(`span`,{className:e.isAdmin?`text-primary font-medium`:`text-foreground-subtle`,children:e.isAdmin?`Admin`:`Regular`})]})})},{key:`scopes`,header:`Access`,render:e=>e.isAdmin?u(`span`,{className:`text-[10px] italic text-foreground-subtle`,children:`everything (admin)`}):u(`span`,{onClick:e=>e.stopPropagation(),children:r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>se(e),title:`Configure access`,children:[u(q,{className:`h-3 w-3 shrink-0 text-foreground-subtle`}),u(kn,{scopes:e.scopes??[]})]})})},{key:`createdAt`,header:`Created`,nowrap:!0,sortValue:e=>e.createdAt,render:e=>u(`span`,{className:`text-foreground-subtle`,children:G(e.createdAt)})},{key:`actions`,header:`Actions`,align:`right`,nowrap:!0,notSortableReason:`Row actions`,render:e=>{let n=Oe(e),i=e.id!==t?.id&&e.totpEnabled;return r(`div`,{className:`flex items-center justify-end gap-1`,onClick:e=>e.stopPropagation(),children:[r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>a(e.id),disabled:n,title:`Edit user`,children:[u(Ce,{className:`h-3 w-3`}),`Edit`]}),i&&r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>B(e),disabled:n,title:`Remove two-factor authentication`,children:[u(pe,{className:`h-3 w-3`}),`Remove 2FA`]}),r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>ue(e),disabled:n,title:`Linked sign-in accounts`,children:[u(ae,{className:`h-3 w-3`}),`Sign-in links`]}),r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>me(e),disabled:n,title:`Active sessions`,children:[u(Pe,{className:`h-3 w-3`}),`Sessions`]}),r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>void W(e),disabled:n,title:`Reset password`,children:[u(v,{className:`h-3 w-3`}),`Reset password`]}),r(k,{size:`sm`,variant:`ghost`,type:`button`,onClick:()=>void U(e),disabled:n,title:`Delete user`,className:`text-danger hover:bg-danger/10`,children:[n?u(C,{className:`h-3 w-3 animate-spin`}):u(p,{className:`h-3 w-3`}),`Delete`]})]})}}],rows:he,rowKey:e=>e.id,minWidthPx:560,maxBodyHeight:`max-h-[60vh]`,mobileMode:`scroll`,onRowClick:e=>a(e.id)}),u(je,{open:_e,onClose:ge,title:f?.id===void 0?`Create User`:`Edit ${f.username||`user`}`,icon:Ae,error:h,testId:`user-form-dialog`,footer:u(k,{size:`sm`,type:`button`,disabled:De,onClick:()=>void be(),children:xe?r(o,{children:[u(C,{className:`h-3.5 w-3.5 animate-spin`}),`Saving…`]}):f?.id===void 0?`Create`:`Save`}),children:f!==null&&r(`div`,{className:`space-y-3`,children:[u(D,{label:`Username`,required:f.id===void 0,children:u(V,{value:f.username,onChange:e=>m({...f,username:e.target.value}),placeholder:`john`,autoFocus:f.id===void 0,disabled:f.id!==void 0,"data-testid":`user-form-username`})}),f.id===void 0&&u(D,{label:`Password`,required:!0,description:`At least ${String(wn)} characters.`,children:u(N,{value:f.password,onValueChange:e=>m({...f,password:e}),shown:_,onToggleShown:()=>y(e=>!e),placeholder:`••••••••`,"data-testid":`user-form-password`})}),u(D,{label:`Admin`,description:`Unrestricted access to every capability and device.`,children:r(`label`,{className:`inline-flex items-center gap-2 text-xs text-foreground cursor-pointer`,children:[u(`input`,{type:`checkbox`,checked:f.isAdmin,onChange:e=>m({...f,isAdmin:e.target.checked}),className:`rounded border-border focus:ring-primary`,"data-testid":`user-form-admin`}),u(`span`,{children:`Admin`})]})}),f.id!==void 0&&u(`p`,{className:`text-[11px] text-foreground-subtle`,children:`To change the password, use Reset password on the user's row. Access scopes are configured from the Access column.`})]})}),ce&&u(_n,{userId:ce.id,username:ce.username,onClose:()=>B(null)}),le&&u(gn,{userId:le.id,username:le.username,onClose:()=>ue(null)}),fe&&u(vn,{userId:fe.id,username:fe.username,onClose:()=>me(null)}),ie&&u(An,{user:ie,onClose:()=>se(null),onSubmit:e=>re.mutate({userId:ie.id,scopes:e}),submitting:re.isPending})]})}function kn({scopes:e}){if(e.length===0)return u(`span`,{className:`font-medium text-warning`,children:`No access`});let t=nt(e);return t===null?r(`span`,{className:`text-foreground-subtle`,children:[`Custom · `,e.length,` grant`,e.length===1?``:`s`]}):r(`span`,{className:`text-foreground-subtle`,children:[u(`span`,{className:`font-medium text-foreground`,children:rt(t.roleId).label}),` ·`,` `,ge(t.selector)]})}function An(e){let{user:t}=st(),n=t?.isAdmin===!0;return u(Cn,{...e,callerScopes:n?null:t?.scopes??[]})}var jn={"api-keys":Q.scopedTokens,"linked-sessions":Q.linkedApps};function Mn(){let{t:e}=it(),[t]=at(),n=jn[t.get(`tab`)??``];if(n!==void 0)return u(ct,{to:ft(`access`,{integration:n}),replace:!0});let r=ot(`/system/identity`,{t:e,isAdmin:!0})??{icon:ke,title:e(`system.identity`,`Identity & Access`)};return u(gt,{icon:r.icon,title:r.title,defaultSection:`users`,sectionsLabel:`${r.title} sections`,sections:[{id:`users`,label:`Users`,content:u(On,{})},{id:dt.access.tab,label:`Tokens & sessions`,content:u(en,{}),ownedParams:mt.concat([Mt.scoped,Mt.admin])},{id:dt.authentication.tab,label:`Authentication`,content:u(mn,{}),ownedParams:mt}]})}export{Mn as IdentityPage};
@@ -1 +0,0 @@
1
- import{I as e,K as t,Q as n,S as r,W as i,a,b as o,o as s,q as c,t as l,x as u,z as d}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{Q as f,ct as p,ot as m,yt as h}from"./player-overlays-9CMoB0XN.js";import{$ as g,An as _,Cd as v,Ci as y,Ed as b,Fn as ee,Gi as x,Gn as S,Hn as C,Ht as te,In as w,L as ne,Mn as T,Nn as E,Q as D,Si as O,Tn as re,Wn as ie,Yu as k,_u as A,bi as j,dd as ae,gi as oe,hi as se,jn as M,jr as N,md as P,mf as F,nr as ce,od as I,ud as le,ur as L,vi as R,xi as z,yi as ue,z as de}from"./src-Ca4gNAmP.js";import{t as B}from"./log-in-DwkWn-LJ.js";import{t as V}from"./monitor-smartphone-J97h7fMI.js";import{Cr as H,Nt as fe,tr as pe}from"./client-1JBRoe76.js";import{t as U}from"./useTranslation-Bc5yH9V9.js";import"./mount-DD88fyzH.js";import{$ as W,C as me,I as he,Z as ge,r as _e,s as ve,t as ye,tt as be}from"./index-V6k1XLI7.js";import{t as xe}from"./AdminSectionedPage-Dmz5o8uT.js";import{t as Se}from"./NodeAddonsSettingsPanel-DNm2sNWd.js";import{t as Ce}from"./BrokerForm-UsE-opKN.js";import{n as G,r as we,t as Te}from"./AddonIntegrationBody-BokvIE2T.js";var Ee=h(`recycle`,[[`path`,{d:`M7 19H4.815a1.83 1.83 0 0 1-1.57-.881 1.785 1.785 0 0 1-.004-1.784L7.196 9.5`,key:`x6z5xu`}],[`path`,{d:`M11 19h8.203a1.83 1.83 0 0 0 1.556-.89 1.784 1.784 0 0 0 0-1.775l-1.226-2.12`,key:`1x4zh5`}],[`path`,{d:`m14 16-3 3 3 3`,key:`f6jyew`}],[`path`,{d:`M8.293 13.596 7.196 9.5 3.1 10.598`,key:`wf1obh`}],[`path`,{d:`m9.344 5.811 1.093-1.892A1.83 1.83 0 0 1 11.985 3a1.784 1.784 0 0 1 1.546.888l3.943 6.843`,key:`9tzpgr`}],[`path`,{d:`m13.378 9.633 4.096 1.098 1.097-4.096`,key:`1oe83g`}]]);n();var K=`kind`,q=`name`,J=`restore`,Y=`restoreId`;function De(e){return{sections:[{id:`broker-identity`,title:`Broker`,columns:1,fields:[{type:`select`,key:K,label:`Kind`,options:e.map(e=>({value:e.key,label:e.label})),disabled:e.length<=1},{type:`text`,key:q,label:`Name`,placeholder:`My Home Assistant`,required:!0},{type:`boolean`,key:J,label:`Restore a broker that was removed, under its original id`,description:`Devices remember the broker id they were discovered under, so restoring the original id reconnects them instead of needing re-discovery. An id that is still in use is refused.`},{type:`text`,key:Y,label:`Original broker id`,placeholder:`ha_004`,required:!0,showWhen:{field:J,equals:!0}}]}]}}function Oe(e){let t=t=>{let n=e[t];return typeof n==`string`?n.trim():``},n=t(K);if(n===``)return{ok:!1,problem:`Pick a kind.`};let r=t(q);if(r===``)return{ok:!1,problem:`Enter a name before saving.`};if(e.restore!==!0)return{ok:!0,identity:{kindKey:n,name:r,restoreId:null}};let i=t(Y);return i===``?{ok:!1,problem:`Enter the original id, or turn restore off to create a new broker.`}:{ok:!0,identity:{kindKey:n,name:r,restoreId:i}}}function ke(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.type===`camstack:broker-oauth`&&typeof t.ok==`boolean`}function Ae(e,t){return e.split(`{{brokerId}}`).join(encodeURIComponent(t))}function je(e,t,n=window){let r=n.location.origin,i=!1,a=e=>{i||(i=!0,n.removeEventListener(`message`,o),window.clearInterval(s),t(e))},o=t=>{t.origin!==r||t.source!==e||ke(t.data)&&a({ok:t.data.ok,...t.data.brokerId===void 0?{}:{brokerId:t.data.brokerId},...t.data.error===void 0?{}:{error:t.data.error}})};n.addEventListener(`message`,o);let s=window.setInterval(()=>{e.closed&&a({ok:!0})},500);return()=>{i=!0,n.removeEventListener(`message`,o),window.clearInterval(s)}}function X(e,t,n){let r=f(_e(Ae(e,t))),i=ye(r);if(i===null){window.location.assign(r);return}je(i,e=>{n?.(e)})}var Me={"home-assistant":`Home Assistant`,mqtt:`MQTT`};function Z(e){return Me[e]??e}function Ne(e){return Array.isArray(e)?e.filter(e=>typeof e==`object`&&!!e&&typeof e.addonId==`string`&&Array.isArray(e.kinds)):[]}var Pe={connected:`success`,connecting:`warning`,disconnected:`default`,"auth-failed":`danger`,unreachable:`danger`,error:`danger`};function Fe({status:e}){return u(L,{variant:Pe[e],className:`text-[10px]`,children:e})}function Ie(e){return Array.isArray(e)?e.filter(e=>typeof e==`object`&&!!e&&typeof e.id==`string`):[]}function Le({title:e,subtitle:t,onClose:n,children:i}){return u(_,{open:!0,onOpenChange:e=>!e&&n(),children:r(T,{width:`md`,placement:`sheet-on-mobile`,scroll:`body`,children:[r(ee,{children:[u(w,{children:e}),t&&u(E,{children:t})]}),u(M,{className:`space-y-3 pr-1`,children:i})]})})}function Re({kinds:e,onClose:t,onDone:n}){let a=s(),o=i(()=>De(e.map(e=>({key:Q(e),label:e.label}))),[e]),[l,d]=c(()=>({[K]:Q(e[0]),[q]:``,[J]:!1,[Y]:``})),[f,p]=c(null),m=e.find(e=>Q(e)===l.kind)??e[0],h=m?.kind??``,g=m?.addonId,_=m?.interactiveConnectPath,v=se({onSuccess:e=>{a.invalidateQueries({queryKey:[[`broker`,`list`]]});let t=typeof e==`object`&&e&&typeof e.id==`string`?e.id:null;t!==null&&_!==void 0&&_.length>0&&X(_,t,()=>{a.invalidateQueries({queryKey:[[`broker`,`list`]]})}),n()}}),y=e=>{let t=Oe(l);if(!t.ok){p(t.problem);return}p(null);let{name:n,restoreId:r}=t.identity;v.mutate({kind:h,name:n,settings:e,...r===null?{}:{id:r},...g===void 0?{}:{addonId:g}})};return r(Le,{title:`Add broker`,subtitle:`Register a message broker for exporters and automations.`,onClose:v.isPending?()=>{}:t,children:[u(he,{schema:o,values:l,onChange:d}),f!==null&&u(C,{message:f}),v.isError&&u(C,{message:v.error instanceof Error?v.error.message:String(v.error)}),h&&u(Ce,{kind:h,...g===void 0?{}:{addonId:g},onSubmit:y,onCancel:t,submitLabel:v.isPending?`Adding…`:`Add broker`,disabled:v.isPending},Q(m))]})}function Q(e){return e===void 0?``:`${e.addonId??``}::${e.kind}`}function ze({broker:e,onClose:t,onDone:n}){let a=s(),o=oe({id:e.id,addonId:e.addonId}),c=O({onSuccess:()=>{a.invalidateQueries({queryKey:[[`broker`,`list`]]}),a.invalidateQueries({queryKey:[[`broker`,`getSettings`],{input:{id:e.id,addonId:e.addonId}}]}),n()}}),l=i(()=>{let e=o.data;return e&&typeof e==`object`?{...e}:{}},[o.data]);return u(Le,{title:`Edit ${e.name}`,subtitle:`${Z(e.kind)} · ${e.id}`,onClose:c.isPending?()=>{}:t,children:o.isLoading?r(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[u(b,{className:`h-3.5 w-3.5 animate-spin`}),` Loading settings…`]}):o.isError?r(`div`,{className:`space-y-3`,children:[u(C,{message:`Failed to load settings: ${o.error instanceof Error?o.error.message:`Unknown error`}`}),u(`div`,{className:`flex justify-end`,children:u(S,{variant:`outline`,size:`sm`,onClick:t,children:`Close`})})]}):r(`div`,{className:`space-y-3`,children:[c.isError&&u(C,{message:c.error instanceof Error?c.error.message:String(c.error)}),u(Ce,{kind:e.kind,addonId:e.addonId,brokerId:e.id,initialValues:l,onSubmit:t=>{c.mutate({id:e.id,settings:t,addonId:e.addonId})},onCancel:t,submitLabel:c.isPending?`Saving…`:`Save`,disabled:c.isPending})]})})}function Be({broker:e,interactiveConnectPath:t,interactiveDisconnectPath:n,onEdit:i}){let a=s(),o=ce(),[l,d]=c(null),f=y({onSuccess:e=>d(e.ok?{ok:!0}:{ok:!1,error:e.error}),onError:e=>d({ok:!1,error:e instanceof Error?e.message:String(e)})}),m=z({onSuccess:()=>{a.invalidateQueries({queryKey:[[`broker`,`list`]]})}}),h=async()=>{await o({title:`Delete broker`,message:`Delete broker "${e.name}"? Integrations that depend on it will stop working.`,confirmLabel:`Delete`,variant:`danger`})&&m.mutate({id:e.id,addonId:e.addonId})},g=t!==void 0&&t.length>0&&e.status!==`connected`,_=n!==void 0&&n.length>0&&e.status===`connected`;return r(`tr`,{className:`border-b border-border last:border-0`,children:[u(`td`,{className:`px-3 py-2 text-xs text-foreground font-medium`,children:e.name}),u(`td`,{className:`px-3 py-2 text-xs text-foreground-subtle`,children:Z(e.kind)}),r(`td`,{className:`px-3 py-2`,children:[u(Fe,{status:e.status}),l&&u(`span`,{className:`ml-2 text-[10px] ${l.ok?`text-success`:`text-danger`}`,children:l.ok?`OK`:l.error??`failed`})]}),u(`td`,{className:`px-3 py-2`,children:r(`div`,{className:`flex items-center justify-end gap-1.5`,children:[g&&r(S,{variant:`primary`,size:`sm`,onClick:()=>{X(t,e.id,()=>{a.invalidateQueries({queryKey:[[`broker`,`list`]]})})},title:`Sign in — opens a popup that closes when done`,children:[u(B,{className:`h-3 w-3`}),` Sign in`]}),_&&r(S,{variant:`outline`,size:`sm`,onClick:()=>{X(n,e.id,()=>{a.invalidateQueries({queryKey:[[`broker`,`list`]]})})},title:`Sign out — clears the linked Google account on this broker`,children:[u(be,{className:`h-3 w-3`}),` Sign out`]}),r(S,{variant:`outline`,size:`sm`,onClick:()=>f.mutate({id:e.id,addonId:e.addonId}),disabled:f.isPending,title:`Test connection`,children:[f.isPending?u(b,{className:`h-3 w-3 animate-spin`}):u(A,{className:`h-3 w-3`}),`Test`]}),r(S,{variant:`outline`,size:`sm`,onClick:()=>i(e),title:`Edit`,children:[u(P,{className:`h-3 w-3`}),` Edit`]}),r(S,{variant:`outline`,size:`sm`,className:`border-danger/40 text-danger hover:bg-danger/10`,onClick:()=>{h()},disabled:m.isPending,title:`Delete`,children:[m.isPending?u(b,{className:`h-3 w-3 animate-spin`}):u(p,{className:`h-3 w-3`}),`Delete`]})]})})]})}function Ve({addonId:e}={}){let t=ue({}),n=j(),a=i(()=>Ie(t.data).filter(t=>e===void 0||t.addonId===e),[t.data,e]),[o,s]=c(!1),[l,d]=c(null),f=i(()=>{let t=new Map;for(let r of Ne(n.data))if(!(e!==void 0&&r.addonId!==e))for(let e of r.kinds){let n=typeof e.interactiveConnectPath==`string`&&e.interactiveConnectPath.length>0?e.interactiveConnectPath:void 0,i=typeof e.interactiveDisconnectPath==`string`&&e.interactiveDisconnectPath.length>0?e.interactiveDisconnectPath:void 0;t.set(`${r.addonId}::${e.kind}`,{addonId:r.addonId,kind:e.kind,label:e.label,...n===void 0?{}:{interactiveConnectPath:n},...i===void 0?{}:{interactiveDisconnectPath:i}})}for(let e of a)[...t.values()].some(t=>t.kind===e.kind)||t.set(`::${e.kind}`,{kind:e.kind,label:Z(e.kind)});return[...t.values()]},[a,n.data,e]),p=i(()=>{let e=new Map;for(let t of f)e.has(t.kind)||e.set(t.kind,{...t.interactiveConnectPath===void 0?{}:{connect:t.interactiveConnectPath},...t.interactiveDisconnectPath===void 0?{}:{disconnect:t.interactiveDisconnectPath}});return e},[f]);return r(`div`,{className:`space-y-3`,children:[u(`div`,{className:`flex items-center justify-end`,children:r(S,{variant:`primary`,size:`sm`,onClick:()=>s(!0),disabled:f.length===0,title:f.length===0?`No broker providers available`:void 0,children:[u(ae,{className:`h-3.5 w-3.5`}),` Add broker`]})}),u(`div`,{className:`rounded-lg border border-border bg-surface overflow-x-auto`,children:r(`table`,{className:`w-full min-w-[36rem] border-collapse`,children:[u(`thead`,{children:r(`tr`,{className:`border-b border-border bg-background/50`,children:[u(`th`,{className:`px-3 py-2 text-left text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle`,children:`Name`}),u(`th`,{className:`px-3 py-2 text-left text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle`,children:`Kind`}),u(`th`,{className:`px-3 py-2 text-left text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle`,children:`Status`}),u(`th`,{className:`px-3 py-2 text-right text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle`,children:`Actions`})]})}),r(`tbody`,{children:[t.isLoading&&u(`tr`,{children:r(`td`,{colSpan:4,className:`px-3 py-6 text-center text-xs text-foreground-subtle`,children:[u(b,{className:`inline h-4 w-4 animate-spin`}),` Loading brokers…`]})}),t.isError&&u(`tr`,{children:r(`td`,{colSpan:4,className:`px-3 py-4 text-center text-xs text-danger`,children:[`Failed to load brokers:`,` `,t.error instanceof Error?t.error.message:`Unknown error`]})}),!t.isLoading&&!t.isError&&a.length===0&&u(`tr`,{children:u(`td`,{colSpan:4,className:`px-3 py-6 text-center text-xs text-foreground-subtle`,children:`No brokers registered yet. Add one to publish device events for downstream automations.`})}),a.map(e=>{let t=p.get(e.kind);return u(Be,{broker:e,...t?.connect===void 0?{}:{interactiveConnectPath:t.connect},...t?.disconnect===void 0?{}:{interactiveDisconnectPath:t.disconnect},onEdit:d},e.id)})]})]})}),o&&u(Re,{kinds:f,onClose:()=>s(!1),onDone:()=>s(!1)}),l&&u(ze,{broker:l,onClose:()=>d(null),onDone:()=>d(null)})]})}function He(e){if(typeof e==`string`)return e;if(typeof e==`object`&&e){let t=e.name;if(typeof t==`string`)return t}return null}function Ue(e){let t=e,n=t?.manifest;if(n?.id===void 0)return null;let r=(n.capabilities??[]).map(He).filter(e=>e!==null);return{addonId:n.id,displayName:n.packageDisplayName??n.name??n.id,capNames:r,processState:t?.process?.state??null}}function We(e,t){return Object.entries(e).filter(([,e])=>e===t).map(([e])=>e)}function Ge(e,t){return t.find(t=>e.capNames.includes(t))??null}function Ke(e){switch(e){case`running`:return{state:`ok`,label:`running`};case`crashed`:case`failed`:return{state:`error`,label:e};case`stopped`:case`disabled`:return{state:`off`,label:`stopped`};default:return{state:`unknown`,label:e??`unknown`}}}n();var qe=[`wasteCollections`,`listSources`],Je=[`wasteCollections`,`listCountries`];function Ye(e){return e instanceof Error?e.message:String(e)}function Xe(e){return typeof e!=`object`||!e?!1:Array.isArray(Reflect.get(e,`sections`))}function Ze(e){if(!Array.isArray(e))return[];let t=[];for(let n of e){if(typeof n!=`object`||!n)continue;let e=Reflect.get(n,`id`),r=Reflect.get(n,`label`),i=Reflect.get(n,`module`);if(typeof e!=`string`||typeof r!=`string`||typeof i!=`string`)continue;let a=Reflect.get(n,`argsJson`),o=Reflect.get(n,`enabled`),s=Reflect.get(n,`formValues`),c=Reflect.get(n,`serviceId`),l=Reflect.get(n,`countryId`);t.push({id:e,label:r,module:i,argsJson:typeof a==`string`?a:`{}`,enabled:typeof o==`boolean`?o:!0,formValues:typeof s==`object`&&s&&!Array.isArray(s)?s:void 0,serviceId:typeof c==`string`?c:void 0,countryId:typeof l==`string`?l:void 0})}return t}function Qe(e){return Array.isArray(e)?e.flatMap(e=>{if(typeof e!=`object`||!e)return[];let t=Reflect.get(e,`id`),n=Reflect.get(e,`label`);return typeof t!=`string`||typeof n!=`string`?[]:[{id:t,label:n}]}):[]}function $e(e){return Array.isArray(e)?e.flatMap(e=>{if(typeof e!=`object`||!e)return[];let t=Reflect.get(e,`id`),n=Reflect.get(e,`label`),r=Reflect.get(e,`module`);if(typeof t!=`string`||typeof n!=`string`||typeof r!=`string`)return[];let i=Reflect.get(e,`title`);return[{id:t,label:n,title:typeof i==`string`&&i.length>0?i:n,module:r}]}):[]}function et(e,t,n){return{sections:[{id:`picker`,title:`Country & service`,columns:2,fields:[{type:`select`,key:`countryId`,label:`Country`,options:[{value:``,label:`Select country…`},...e.map(e=>({value:e.id,label:e.label}))]},{type:`select`,key:`serviceId`,label:`Service`,options:[{value:``,label:n===``?`Pick a country first`:`Select service…`},...t.map(e=>({value:e.id,label:e.label}))]}]}]}}function tt({addonId:n,capName:f}){let h=m(),g=s(),_=ce(),v=ne({editKey:`wasteEdit`,newKey:`wasteNew`}),[y,b]=c(!1),[ee,x]=c(null),[C,w]=c({countryId:``,serviceId:``}),[T,E]=c({label:`New source`,enabled:!0}),D=t(null),O=typeof C.countryId==`string`?C.countryId:``,k=typeof C.serviceId==`string`?C.serviceId:``,A=e((e,t)=>h.trpcClient.addons.custom.mutate({addonId:n,action:e,input:t}),[n,h]),j=a({queryKey:[...qe,n],queryFn:async()=>Ze(await A(`listSources`,{}))}),oe=a({queryKey:[...Je,n],queryFn:async()=>Qe(await A(`listCountries`,{}))}),se=a({queryKey:[`wasteCollections`,`listServices`,n,O],enabled:O!==``,queryFn:async()=>$e(await A(`listServices`,{countryId:O}))}),M=a({queryKey:[`wasteCollections`,`serviceFormSchema`,n,k],enabled:k!==``,queryFn:async()=>A(`serviceFormSchema`,{serviceId:k})}),N=j.data??[],P=oe.data??[],F=se.data??[],I=Xe(M.data)?M.data:void 0,le=i(()=>et(P,O===``?[]:F,O),[P,F,O]),R=v.state,z=R.mode===`edit`?N.find(e=>e.id===R.id)??null:null;d(()=>{if(v.state.mode===`edit`&&z!==null){let e=`edit:${z.id}`;if(D.current===e)return;D.current=e,x(null),w({countryId:z.countryId??``,serviceId:z.serviceId??``}),E(z.formValues===void 0?{label:z.label,enabled:z.enabled}:{...z.formValues});return}if(v.state.mode===`new`){if(D.current===`new`)return;D.current=`new`,x(null),w({countryId:``,serviceId:``}),E({label:`New source`,enabled:!0})}},[z,v.state]),d(()=>{if(v.state.mode!==`new`||k===``)return;let e=F.find(e=>e.id===k);e&&E(t=>{let n=t.label;return typeof n==`string`&&n!==``&&n!==`New source`?t:{...t,label:e.title}})},[v.state.mode,k,F]);let ue=e(e=>{w(t=>{let n=typeof e.countryId==`string`?e.countryId:``;return n===(typeof t.countryId==`string`?t.countryId:``)?e:{countryId:n,serviceId:``}})},[]),B=e(async()=>{await g.invalidateQueries({queryKey:qe})},[g]),V=l({mutationFn:async e=>A(`deleteSource`,{id:e}),onSuccess:async()=>B()}),H=e(async e=>{await _({title:`Delete waste source?`,message:`Remove “${e.label}” (${e.id}). Its calendar under System → Calendars is dropped.`,confirmLabel:`Delete`,cancelLabel:`Cancel`,variant:`danger`})&&(await V.mutateAsync(e.id),v.state.mode===`edit`&&v.state.id===e.id&&(v.close(),D.current=null))},[_,V,v]),fe=e(async()=>{let e=T.label,t=typeof e==`string`&&e.trim()!==``?e.trim():``;if(t===``){x(`Label is required.`);return}if(k===``){x(`Pick a country and a service.`);return}let n=v.state.mode===`new`,r=z?.id;if(!n&&(r===void 0||r===``)){x(`That source is no longer available.`);return}let i=T.enabled,a={...T};delete a.label,delete a.enabled,b(!0),x(null);try{n?await A(`createSource`,{label:t,serviceId:k,args:a,enabled:typeof i==`boolean`?i:!0}):r!==void 0&&await A(`updateSource`,{id:r,label:t,serviceId:k,args:a,enabled:typeof i==`boolean`?i:!0}),await B(),v.close(),D.current=null}catch(e){x(Ye(e))}finally{b(!1)}},[A,z,v,T,B,k]),pe=i(()=>[{key:`label`,header:`Label`,sortValue:e=>e.label,render:e=>u(`span`,{className:`text-sm text-foreground`,children:e.label})},{key:`module`,header:`Module`,sortValue:e=>e.module,render:e=>u(`span`,{className:`font-mono text-xs text-foreground-muted`,children:e.module})},{key:`enabled`,header:`Enabled`,sortValue:e=>+!!e.enabled,render:e=>u(L,{variant:e.enabled?`success`:`default`,children:e.enabled?`On`:`Off`})},{key:`actions`,header:``,notSortableReason:`actions`,render:e=>u(S,{type:`button`,size:`sm`,variant:`ghost`,"aria-label":`Delete ${e.label}`,"data-testid":`waste-source-delete-${e.id}`,onClick:t=>{t.stopPropagation(),H(e)},children:u(p,{className:`h-3.5 w-3.5 text-danger`})})}],[H]),U=v.state.mode===`new`,W=U||z!==null,me=`${k}:${U?`new`:z?.id??`closed`}`;return r(`div`,{className:`flex min-h-0 flex-col gap-4`,"data-testid":`waste-settings-sources`,children:[r(`div`,{className:`flex flex-wrap items-center justify-between gap-2`,children:[u(`p`,{className:`text-sm text-foreground-muted`,children:`Pick a country and a service — configuration fields come from that HACS source.`}),r(S,{type:`button`,size:`sm`,"data-testid":`waste-sources-add`,onClick:()=>v.openNew(),children:[u(ae,{className:`h-3.5 w-3.5`}),`Add source`]})]}),j.isError?u(`p`,{className:`text-sm text-danger`,children:`Could not load sources.`}):null,j.isPending?u(ie,{className:`h-32 w-full`}):u(te,{columns:pe,rows:N,rowKey:e=>e.id,pageSize:25,mobileMode:`scroll`,emptyMessage:`No waste sources yet. Add one to register a municipal service.`,onRowClick:e=>v.openEdit(e.id),defaultSort:{columnKey:`label`,dir:`asc`}}),u(de,{open:W,onClose:()=>{v.close(),x(null),D.current=null},title:U?`Add waste source`:`Edit ${z?.label??`source`}`,icon:Ee,width:`xl`,testId:`waste-source-editor`,error:ee,footer:u(S,{type:`button`,size:`sm`,disabled:y||k===``,onClick:()=>void fe(),children:y?`Saving…`:`Save`}),children:W?r(o,{children:[u(`div`,{"data-testid":`waste-country-service`,children:u(re,{schema:le,values:C,onChange:ue},`picker:${O}:${F.length}`)}),k!==``&&I!==void 0?u(re,{schema:I,values:T,onChange:E,bare:!0},me):k!==``&&M.isPending?u(`p`,{className:`text-sm text-foreground-muted`,children:`Loading fields…`}):u(`p`,{className:`text-sm text-foreground-muted`,children:`Choose a country and service to see the required fields.`})]}):null}),u(Se,{nodeId:`hub`,addonIds:[n],level:`global`,capability:f,hideSectionChrome:!0})]})}n();var $=1e4,nt={"device-export":`exporters`,exporters:`exporters`,email:`email`,broker:`brokers`,brokers:`brokers`,"message-brokers":`brokers`,calendar:`calendars`,calendars:`calendars`,"calendar-source":`calendars`},rt=[{sectionId:`exporters`,kind:`device-export`,label:`Exporters`,icon:k,installLabel:`Add export`,emptyTitle:`No device exporters installed`,emptyDescription:`Install an export addon to publish devices to Alexa, HomeKit, Google Home or Home Assistant.`},{sectionId:`email`,kind:`email`,label:`Email`,icon:v,installLabel:`Add email provider`,emptyTitle:`No email providers installed`,emptyDescription:`Install an SMTP or Gmail addon to send notification email from this hub.`},{sectionId:`brokers`,kind:`broker`,label:`Brokers`,icon:I,installLabel:`Add broker addon`,emptyTitle:`No message brokers installed`,emptyDescription:`Install a broker addon to connect Home Assistant, Homematic, MQTT, Matter and similar systems.`},{sectionId:`calendars`,kind:`calendar`,label:`Calendars`,icon:F,installLabel:`Add calendar source`,emptyTitle:`No calendar sources installed`,emptyDescription:`Install a calendar-source addon (Google Calendar, waste collections, …) to feed System → Calendars.`}];function it(e,t,n){let i=We(fe,e.kind),a=t.flatMap(t=>{let a=Ge(t,i);if(a===null)return[];let s=Ke(t.processState);return[{id:t.addonId,name:t.displayName,fallbackIcon:e.icon,state:s.state,stateLabel:s.label,render:()=>u(Te,{addonId:t.addonId,displayName:t.displayName,capName:a,isActive:t.processState===`running`,badges:r(o,{children:[u(ct,{processState:t.processState}),u(lt,{kind:e.kind,addonId:t.addonId})]}),lead:e.kind===`device-export`?u(g,{addonId:t.addonId}):void 0,panels:st(e.kind,t.addonId,e=>n(`/devices/${e}`)),settingsContent:t.addonId===`waste-collections`?u(tt,{addonId:t.addonId,capName:a}):void 0})}]});return{id:e.sectionId,label:e.label,entries:a,install:{capability:i[0]??e.kind,label:e.installLabel}}}function at({descriptor:e,rows:t,isLoading:n}){let r=ge(),a=i(()=>it(e,t,r),[e,t,r]);return u(we,{embedded:!0,icon:e.icon,title:e.label,subtitle:null,groups:[a],isLoading:n,emptyTitle:e.emptyTitle,emptyDescription:e.emptyDescription})}function ot(){let{t:e}=U(),t=N(void 0,{refetchInterval:$}),[n,r]=W(),a=me(`/system/integrations`,{t:e,isAdmin:!0})??{icon:le,title:`Connectivity`};d(()=>{let e=n.get(`tab`);if(e===null)return;let t=nt[e];t===void 0||t===e||r(e=>{let n=new URLSearchParams(e);return n.set(`tab`,t),n},{replace:!0})},[n,r]);let o=i(()=>(t.data??[]).map(Ue).filter(e=>e!==null),[t.data]),s=i(()=>rt.map(e=>({id:e.sectionId,label:e.label,ownedParams:ve,content:u(at,{descriptor:e,rows:o,isLoading:t.isLoading})})),[t.isLoading,o]);return u(xe,{icon:a.icon,title:a.title,subtitle:`External systems this hub talks to — exporters, email, brokers and calendar sources.`,defaultSection:`exporters`,sectionsLabel:`Connectivity sections`,sections:s})}function st(e,t,n){return e===`device-export`?[{id:`devices`,title:`Devices`,icon:V,placement:`end`,content:u(D,{addonId:t,onOpenDevice:n,showSetup:!1})}]:e===`broker`?[{id:`connections`,title:`Connections`,icon:I,content:u(Ve,{addonId:t})}]:[]}function ct({processState:e}){let{state:t,label:n}=Ke(e);return u(L,{variant:t===`ok`?`success`:t===`error`?`danger`:`default`,className:`${G} capitalize`,children:n})}function lt({kind:e,addonId:t}){return e===`broker`?u(ut,{addonId:t}):e===`device-export`?u(dt,{addonId:t}):null}function ut({addonId:e}){let{data:t}=R({addonId:e},{refetchInterval:$,retry:!1}),n=H.safeParse(t);if(!n.success)return null;let{brokerCount:r,connectedCount:i}=n.data;return u(L,{variant:`info`,className:G,children:`${r} connection${r===1?``:`s`}${i>0?` · ${i} connected`:``}`})}function dt({addonId:e}){let{data:t}=x({addonId:e},{refetchInterval:$,retry:!1}),n=pe.safeParse(t);if(!n.success)return null;let{linkState:r,exposedDeviceCount:i}=n.data;return u(L,{variant:r===`linked`?`success`:r===`error`?`danger`:`default`,className:G,children:`${r} · ${i} exposed`})}export{ot as IntegrationsPage};
@@ -1 +0,0 @@
1
- import{Q as e,S as t,W as n,q as r,x as i}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{vt as a,yt as o}from"./player-overlays-9CMoB0XN.js";import{$u as s,Ed as c,Ff as l,Mr as u,Tt as d,ff as f,jr as p,ll as m,sl as h,su as g}from"./src-Ca4gNAmP.js";import{t as _}from"./layers-Bg1NrUAX.js";import{t as v}from"./useTranslation-Bc5yH9V9.js";import{C as y,c as b,s as x}from"./index-V6k1XLI7.js";import{t as S}from"./AdminSectionedPage-Dmz5o8uT.js";import{t as C}from"./AdminPage-LJSPjGaV.js";import{r as w,t as T}from"./AddonIntegrationBody-BokvIE2T.js";import{n as E,t as D}from"./addon-display-names-BkwMrExv.js";var O=o(`file-text`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10 9H8`,key:`b1mrlr`}],[`path`,{d:`M16 13H8`,key:`t4e002`}],[`path`,{d:`M16 17H8`,key:`z1uh3a`}]]),k=o(`timer-reset`,[[`path`,{d:`M10 2h4`,key:`n1abiw`}],[`path`,{d:`M12 14v-4`,key:`1evpnu`}],[`path`,{d:`M4 13a8 8 0 0 1 8-7 8 8 0 1 1-5.3 14L4 17.6`,key:`1ts96g`}],[`path`,{d:`M9 17H4v5`,key:`8t5av`}]]);e();var A=5e3,j=`log-destination`,M={"winston-logging":`Rotated log files on this node’s disk.`,"console-logging":`stdout / stderr of the node process.`,"loki-logging":`Pushes to a Grafana Loki instance. Off until enabled with a URL.`,"hub-forwarder":`Agent-only — forwards this node’s entries to the hub.`};function N(){let e=u({capName:`log-destination`},{refetchInterval:A}),t=p(void 0,{refetchInterval:A}),r=n(()=>D(t.data??[]),[t.data]),a=n(()=>{let e=new Map;for(let n of t.data??[]){let t=n.description,r=n.id;typeof r==`string`&&typeof t==`string`&&t.length>0&&e.set(r,t)}return e},[t.data]),o=n(()=>(e.data??[]).map(e=>({addonId:e.addonId,displayName:r.get(e.addonId)??e.addonId,isActive:e.isActive,caption:a.get(e.addonId)??M[e.addonId]??``})),[e.data,r,a]);return i(w,{embedded:!0,icon:s,title:`Destinations`,subtitle:`Every registered log destination. Entries are fanned to all enabled destinations at once — enabling one does not disable another.`,groups:[{id:j,label:`Log destinations`,install:{capability:j,label:`Add destination`},entries:o.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:O,...E(e.isActive),render:()=>i(T,{addonId:e.addonId,displayName:e.displayName,capName:j,isActive:e.isActive,badges:e.caption.length>0?i(`span`,{className:`text-[11px] text-foreground-subtle`,children:e.caption}):null})}))}],isLoading:e.isLoading,emptyTitle:`No log destinations registered`,emptyDescription:`This should not happen — the console and rotated-file destinations are builtins. If the list is empty, the node is still booting.`})}e();var P=[12e4,3e5,6e5,18e5],F={"request-census":`Counts every tRPC call by TRANSPORT PLANE — http, ws, mesh — and by procedure, caller, address and user-agent. Subscriptions are counted apart from calls, so a long-lived stream cannot pass for a storm. Arm it, then restart: the deadline is persisted, so the window is still running when clients reconnect.`};function I(){let{t:e}=v(),n=h({},{refetchInterval:5e3}),a=m(),[o,s]=r(null),u=(e,t)=>{s(e),a.mutate({patch:{diagnostics:[{id:e,armMs:t}]}},{onSettled:()=>{s(null),n.refetch()}})},d=n.data?.activeWindows??[],f=n.data?.channels??[],p=n.data?.activeChannels??[],g=(e,t,r)=>{s(e),a.mutate({patch:{channels:[{channel:e,armMs:t,deviceIds:r}]}},{onSettled:()=>{s(null),n.refetch()}})};return t(C,{embedded:!0,icon:l,title:e(`logging.diagnostics.title`,`Diagnostics`),subtitle:e(`logging.diagnostics.subtitle`,`Diagnostics are armed for a window, never switched on. Each carries a deadline and expires by itself — one left running is an incident of its own.`),children:[n.isLoading?t(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[i(c,{className:`h-3.5 w-3.5 animate-spin`}),e(`common.loading`,`Loading…`)]}):i(`div`,{className:`rounded-lg border border-border bg-surface divide-y divide-border`,children:d.map(e=>i(R,{window:e,busy:o===e.id,onArm:t=>u(e.id,t)},e.id))}),i(`h3`,{className:`mt-6 text-sm font-medium text-foreground`,children:e(`logging.diagnostics.channels`,`Component channels`)}),i(`p`,{className:`mb-2 max-w-3xl text-[11px] text-foreground-subtle`,children:e(`logging.diagnostics.channelsSubtitle`,`Declared by the addons that own them — this list is not maintained by hand. Arming a channel opens a window with a deadline; it closes by itself.`)}),f.length===0?i(`p`,{className:`text-[11px] text-foreground-subtle`,children:e(`logging.diagnostics.noChannels`,`No addon on this cluster declares a log channel, or none could be reached right now.`)}):i(`div`,{className:`rounded-lg border border-border bg-surface divide-y divide-border`,children:f.map(e=>i(L,{channel:e,state:p.find(t=>t.channel===e.name),busy:o===e.name,onArm:(t,n)=>g(e.name,t,n)},e.name))})]})}function L({channel:e,state:n,busy:o,onArm:s}){let{t:c}=v(),[l,u]=r(``),d=n?.armed===!0,f=()=>{let e=l.split(/[\s,+]+/).filter(e=>/^\d+$/.test(e)).map(e=>Number(e));return e.length>0?e:null};return t(`div`,{className:`flex flex-col gap-2 p-3 lg:flex-row lg:items-center lg:justify-between`,children:[t(`div`,{className:`min-w-0`,children:[t(`div`,{className:`flex flex-wrap items-center gap-2`,children:[i(`span`,{className:`text-sm font-medium text-foreground`,children:e.name}),d?t(`span`,{className:`inline-flex items-center gap-1 rounded bg-warning/10 px-1.5 py-0.5 text-[10px] font-semibold text-warning`,children:[i(k,{className:`h-3 w-3`}),c(`logging.diagnostics.expiresIn`,`expires in {{left}}`,{left:z(n?.remainingMs??0)})]}):i(`span`,{className:`rounded border border-border px-1.5 py-0.5 text-[10px] text-foreground-subtle`,children:c(`logging.diagnostics.disarmed`,`disarmed`)}),d&&n?.deviceIds!==null&&n?.deviceIds!==void 0&&t(`span`,{className:`inline-flex items-center gap-1 rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-semibold text-primary`,children:[i(a,{className:`h-3 w-3`}),n.deviceIds.join(`, `)]}),!e.perDevice&&i(`span`,{className:`rounded border border-border px-1.5 py-0.5 text-[10px] text-foreground-subtle`,children:c(`logging.diagnostics.notPerCamera`,`not filterable per camera`)})]}),i(`p`,{className:`mt-0.5 max-w-2xl text-[11px] text-foreground-subtle`,children:e.description})]}),t(`div`,{className:`flex shrink-0 flex-wrap items-center gap-1`,children:[e.perDevice&&i(`input`,{type:`text`,inputMode:`numeric`,value:l,disabled:o,onChange:e=>u(e.target.value),placeholder:c(`logging.diagnostics.camerasPlaceholder`,`all cameras`),className:`w-28 rounded border border-border bg-surface px-2 py-1 text-[11px] text-foreground`}),P.map(e=>i(`button`,{type:`button`,disabled:o,onClick:()=>s(e,f()),className:`rounded border border-border px-2 py-1 text-[11px] text-foreground-subtle hover:text-foreground disabled:opacity-50`,children:z(e)},e)),i(`button`,{type:`button`,disabled:o||!d,onClick:()=>s(0,null),className:`rounded bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground disabled:opacity-40`,children:c(`logging.diagnostics.disarm`,`Disarm`)})]})]})}function R({window:e,busy:n,onArm:r}){let{t:a}=v();return t(`div`,{className:`flex flex-col gap-2 p-3 lg:flex-row lg:items-center lg:justify-between`,children:[t(`div`,{className:`min-w-0`,children:[t(`div`,{className:`flex items-center gap-2`,children:[i(`span`,{className:`text-sm font-medium text-foreground`,children:e.id}),e.armed?t(`span`,{className:`inline-flex items-center gap-1 rounded bg-warning/10 px-1.5 py-0.5 text-[10px] font-semibold text-warning`,children:[i(k,{className:`h-3 w-3`}),a(`logging.diagnostics.expiresIn`,`expires in {{left}}`,{left:z(e.remainingMs)})]}):i(`span`,{className:`rounded border border-border px-1.5 py-0.5 text-[10px] text-foreground-subtle`,children:a(`logging.diagnostics.disarmed`,`disarmed`)}),e.armed&&!e.persisted&&i(`span`,{className:`text-[10px] text-warning`,children:a(`logging.diagnostics.notPersisted`,`not persisted — will not survive a restart`)})]}),i(`p`,{className:`mt-0.5 max-w-2xl text-[11px] text-foreground-subtle`,children:F[e.id]??``})]}),t(`div`,{className:`flex shrink-0 flex-wrap items-center gap-1`,children:[P.map(e=>i(`button`,{type:`button`,disabled:n,onClick:()=>r(e),className:`rounded border border-border px-2 py-1 text-[11px] text-foreground-subtle hover:text-foreground disabled:opacity-50`,children:z(e)},e)),i(`button`,{type:`button`,disabled:n||!e.armed,onClick:()=>r(0),className:`rounded bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground disabled:opacity-40`,children:a(`logging.diagnostics.disarm`,`Disarm`)})]})]})}function z(e){let t=Math.max(0,Math.round(e/1e3)),n=Math.floor(t/60),r=t%60;return n===0?`${r}s`:r===0?`${n}m`:`${n}m ${r}s`}e();var B=[`debug`,`info`,`warn`,`error`],V=``,H=``;function U(){let{t:e}=v(),[a,o]=r(V),[s,l]=r(H),{nodes:u}=g(),d=n(()=>u.map(e=>({id:e.id,label:e.name||e.id})),[u]),f=h({...a===V?{}:{scopeNodeId:a},...s===H?{}:{scopeComponent:s}},{refetchInterval:15e3}),p=m(),y=f.data,b=p.isPending||f.isFetching,x=y?.channels??[],S=(e,t)=>{p.mutate({...e.nodeId===null?{}:{scopeNodeId:e.nodeId},...e.component===null?{}:{scopeComponent:e.component},patch:{level:t}},{onSuccess:()=>void f.refetch()})};return i(C,{embedded:!0,icon:_,title:e(`logging.levels.title`,`Levels`),subtitle:e(`logging.levels.subtitle`,`Cluster → node, most specific wins. The floor gates what reaches the log destinations; the in-memory viewer keeps every line either way.`),actions:t(`div`,{className:`flex flex-wrap items-center gap-3`,children:[t(`label`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[e(`logging.levels.scope`,`Scope`),t(`select`,{className:`rounded border border-border bg-surface px-2 py-1 text-xs text-foreground disabled:opacity-50`,value:a,disabled:s!==H,onChange:e=>o(e.target.value),children:[i(`option`,{value:V,children:e(`logging.levels.cluster`,`Cluster`)}),d.map(e=>i(`option`,{value:e.id,children:e.label},e.id))]})]}),t(`label`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[e(`logging.levels.component`,`Component`),t(`select`,{className:`rounded border border-border bg-surface px-2 py-1 text-xs text-foreground`,value:s,onChange:e=>l(e.target.value),children:[i(`option`,{value:H,children:e(`logging.levels.noComponent`,`None`)}),x.map(e=>i(`option`,{value:e.name,children:e.name},e.name))]})]})]}),children:f.isLoading||y===void 0?t(`div`,{className:`flex items-center gap-2 text-xs text-foreground-subtle`,children:[i(c,{className:`h-3.5 w-3.5 animate-spin`}),e(`common.loading`,`Loading…`)]}):t(`div`,{className:`space-y-3`,children:[i(G,{level:y.effective.level,source:y.effective.levelSource,persisted:y.persisted}),i(`div`,{className:`rounded-lg border border-border bg-surface divide-y divide-border`,children:y.explicit.layers.map(e=>i(K,{layer:e,inForce:e.scope===y.effective.levelSource,busy:b,onSet:t=>S(e,t),onClear:()=>S(e,null)},`${e.scope}:${e.nodeId??e.component??`cluster`}`))}),s!==H&&i(`p`,{className:`text-[11px] text-foreground-subtle`,children:x.find(e=>e.name===s)?.description??``}),i(`p`,{className:`text-[11px] text-foreground-subtle`,children:e(`logging.levels.componentNote`,`A component is a log channel an addon declared. Its level is cluster-wide and beats both the node and the cluster layer.`)}),i(W,{days:y.opsLogRetentionDays,busy:b,onSet:e=>p.mutate({patch:{opsLogRetentionDays:e}},{onSuccess:()=>void f.refetch()})})]})})}function W({days:e,busy:n,onSet:r}){let{t:a}=v();return t(`div`,{className:`rounded-lg border border-border bg-surface p-3 space-y-1`,children:[t(`label`,{className:`flex items-center justify-between gap-3`,children:[i(`span`,{className:`text-xs font-medium`,children:a(`logging.levels.opsLogRetention`,`Retention audit trail`)}),t(`span`,{className:`flex items-center gap-1.5`,children:[i(`input`,{type:`number`,min:0,max:3650,step:1,defaultValue:e,disabled:n,onBlur:t=>{let n=Number.parseInt(t.target.value,10);Number.isFinite(n)&&n!==e&&r(n)},className:`w-20 rounded border border-border bg-surface px-2 py-1 text-xs`}),i(`span`,{className:`text-[11px] text-foreground-subtle`,children:a(`common.days`,`days`)})]})]}),i(`p`,{className:`text-[11px] text-foreground-subtle`,children:a(`logging.levels.opsLogRetentionNote`,`How long the record of what retention deleted is kept. Cluster-wide, and deliberately longer than any camera window — it is the history of the sweep, not of the camera, and it is what makes a storage leak explainable after the fact. 0 keeps it forever.`)})]})}function G({level:e,source:n,persisted:r}){let{t:a}=v();return t(`div`,{className:`rounded-lg border border-border bg-surface p-3`,children:[t(`div`,{className:`flex items-center gap-2`,children:[i(`span`,{className:`text-xs text-foreground-subtle`,children:a(`logging.levels.effective`,`In force here`)}),i(`span`,{className:`rounded bg-primary/10 px-2 py-0.5 text-xs font-semibold text-primary`,children:e}),i(`span`,{className:`text-[11px] text-foreground-subtle`,children:n==="default"?a(`logging.levels.fromDefault`,`nothing is set anywhere — this is the default`):a(`logging.levels.fromLayer`,`decided by the {{source}} layer`,{source:n})})]}),!r&&i(`p`,{className:`mt-1 text-[11px] text-warning`,children:a(`logging.levels.notPersisted`,`The settings store could not be read or written — what you see is live, but it would not survive a restart.`)})]})}function K({layer:e,inForce:n,busy:r,onSet:a,onClear:o}){let{t:s}=v();return t(`div`,{className:`flex flex-col gap-2 p-3 sm:flex-row sm:items-center sm:justify-between`,children:[t(`div`,{className:`min-w-0`,children:[t(`div`,{className:`flex items-center gap-2`,children:[i(`span`,{className:`text-sm font-medium text-foreground`,children:e.scope===`cluster`?s(`logging.levels.cluster`,`Cluster`):e.scope===`component`?e.component??``:e.nodeId??``}),n&&t(`span`,{className:`inline-flex items-center gap-1 rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-semibold text-primary`,children:[i(f,{className:`h-3 w-3`}),s(`logging.levels.inForce`,`in force`)]})]}),i(`p`,{className:`mt-0.5 text-[11px] text-foreground-subtle`,children:e.level===null?s(`logging.levels.inherits`,`inherits — nothing set at this layer`):s(`logging.levels.setHere`,`set here: {{level}}`,{level:e.level})})]}),t(`div`,{className:`flex shrink-0 items-center gap-1`,children:[B.map(t=>i(`button`,{type:`button`,disabled:r,onClick:()=>a(t),className:`rounded px-2 py-1 text-[11px] font-medium transition-colors disabled:opacity-50 ${e.level===t?`bg-primary text-primary-foreground`:`border border-border text-foreground-subtle hover:text-foreground`}`,children:t},t)),i(`button`,{type:`button`,disabled:r||e.level===null,onClick:o,className:`rounded border border-border px-2 py-1 text-[11px] text-foreground-subtle hover:text-foreground disabled:opacity-40`,children:s(`logging.levels.clear`,`Clear`)})]})]})}function q(){return i(C,{embedded:!0,children:i(`div`,{className:`rounded-lg border border-border bg-surface overflow-hidden`,children:i(d,{limit:500,maxHeight:`max-h-[calc(100vh-310px)]`,showScope:!0,showFilters:!0})})})}function J(){let{t:e}=v(),t=y(`/system/logs`,{t:e,isAdmin:!0})??{icon:s,title:e(`system.logs`,`Logs`)};return i(S,{icon:t.icon,title:t.title,defaultSection:`viewer`,sectionsLabel:`${t.title} sections`,sections:[{id:`viewer`,label:`Viewer`,content:i(q,{})},{id:b.logging.tab,label:`Destinations`,content:i(N,{}),ownedParams:x},{id:`levels`,label:`Levels`,content:i(U,{})},{id:`diagnostics`,label:`Diagnostics`,content:i(I,{})}]})}export{J as LogsPage};
@@ -1 +0,0 @@
1
- import{I as e,K as t,Q as n,S as r,W as i,b as a,o,q as s,x as c,z as l}from"./_virtual_mf___mfe_internal__admin_ui_host__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-DhP-Lgiq.js";import{D as u,L as d,Z as f,ct as p,lt as m,ot as h,ut as g,yt as _}from"./player-overlays-9CMoB0XN.js";import{$d as v,Ao as y,Bo as b,Bt as x,Ed as S,El as C,Fo as ee,Gn as w,Go as T,Gu as E,Hn as D,Ho as O,Ht as k,Io as te,It as ne,Kd as re,Ko as ie,Ku as ae,Lo as oe,Mo as se,Mr as A,Po as ce,Ql as le,Qo as ue,Ro as de,Rt as fe,Ru as pe,Uo as me,Vo as he,Vt as j,Wo as ge,Xo as _e,Zo as ve,Zu as M,_u as ye,ad as be,dd as xe,fd as Se,ff as Ce,gd as N,jo as we,jr as P,ku as F,mu as Te,ql as Ee,qo as De,td as Oe,un as ke,ur as I,va as Ae,wd as je,zn as Me,zo as Ne}from"./src-Ca4gNAmP.js";import{t as L}from"./boxes-PrddLfOG.js";import{t as Pe}from"./log-in-DwkWn-LJ.js";import{Gn as Fe,Kn as Ie,Un as Le,yn as Re}from"./client-1JBRoe76.js";import{t as ze}from"./useTranslation-Bc5yH9V9.js";import"./mount-DD88fyzH.js";import{C as Be,c as R,rt as z,s as B,tt as Ve}from"./index-V6k1XLI7.js";import{t as He}from"./AdminSectionedPage-Dmz5o8uT.js";import{t as V}from"./AdminPage-LJSPjGaV.js";import{t as Ue}from"./NodeAddonsSettingsPanel-DNm2sNWd.js";import{n as We,r as H,t as U}from"./AddonIntegrationBody-BokvIE2T.js";import{n as W,t as G}from"./addon-display-names-BkwMrExv.js";var Ge=_(`file-up`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M12 12v6`,key:`3ahymv`}],[`path`,{d:`m15 15-3-3-3 3`,key:`15xj92`}]]);n();var K=5e3,q=`network-access`,Ke={connected:!1,endpoint:null};function qe(){let e=o(),t=A({capName:`network-access`},{refetchInterval:K}),n=P(void 0,{refetchInterval:K}),r=i(()=>G(n.data??[]),[n.data]),a=i(()=>(t.data??[]).map(e=>({addonId:e.addonId,displayName:r.get(e.addonId)??e.addonId,isActive:e.isActive})),[t.data,r]),[l,u]=s(null),[d,f]=s({}),p=(t,n)=>{let r=n?.addonId;e.invalidateQueries({queryKey:[[`networkAccess`,`getStatus`],{input:{addonId:r},type:`query`}]}),u(null),f(e=>{if(r===void 0||!(r in e))return e;let t={...e};return delete t[r],t})},m=(e,t)=>{u(null);let n=t?.addonId;if(n===void 0)return;let r=e instanceof Error?e.message:String(e);f(e=>({...e,[n]:r}))},h=ve({onSuccess:p,onError:m}),g=ue({onSuccess:p,onError:m});return c(H,{embedded:!0,icon:z,title:`Remote Access`,subtitle:`Public-facing tunnels that expose this hub to the internet (Cloudflare Tunnel, ngrok, …).`,groups:[{id:q,label:`Remote access`,install:{capability:q,label:`Add tunnel`},entries:a.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:z,...W(e.isActive),render:()=>c(U,{addonId:e.addonId,displayName:e.displayName,capName:q,isActive:e.isActive,badges:c(Ye,{addonId:e.addonId}),error:d[e.addonId]??null,primaryAction:c(Xe,{addonId:e.addonId,busyId:l,onStart:e=>{u(e),h.mutate({addonId:e})},onStop:e=>{u(e),g.mutate({addonId:e})}})})}))}],isLoading:t.isLoading,emptyTitle:`No remote-access providers installed`,emptyDescription:`Install a tunnel addon (e.g. @camstack/addon-cloudflare) to expose this hub publicly.`})}function Je(e){let{data:t}=_e({addonId:e},{refetchInterval:K,retry:!1}),n=t?Le.safeParse(t):null;return n?.success?n.data:Ke}function Ye({addonId:e}){let t=Je(e);return r(a,{children:[c(I,{variant:t.connected?`success`:`default`,className:We,children:t.connected?`Connected`:`Disconnected`}),t.endpoint&&r(`a`,{href:t.endpoint.url,target:`_blank`,rel:`noopener noreferrer`,className:`flex min-w-0 items-center gap-1 break-all text-[11px] text-success hover:underline`,children:[c(re,{className:`h-3 w-3`}),t.endpoint.url]})]})}function Xe({addonId:e,busyId:t,onStart:n,onStop:i}){let a=Je(e),o=t===e;return a.connected?r(w,{size:`sm`,variant:`secondary`,disabled:o,onClick:()=>i(e),children:[o?c(S,{className:`h-3 w-3 animate-spin`}):c(m,{className:`h-3 w-3`}),`Stop`]}):r(w,{size:`sm`,variant:`primary`,disabled:o,onClick:()=>n(e),children:[o?c(S,{className:`h-3 w-3 animate-spin`}):c(Se,{className:`h-3 w-3`}),`Start`]})}n();var Ze=3e4,J=`turn-provider`;function Qe(e){let t=[];for(let n of e)typeof n.urls==`string`?t.push(n.urls):t.push(...n.urls);return t}function $e(){let e=A({capName:`turn-provider`},{refetchInterval:Ze}),t=P(void 0,{refetchInterval:Ze}),n=C(),a=n.data??[],o=i(()=>G(t.data??[]),[t.data]),s=i(()=>(e.data??[]).map(e=>({addonId:e.addonId,displayName:o.get(e.addonId)??e.addonId,isActive:e.isActive})),[e.data,o]);return c(H,{embedded:!0,icon:N,title:`TURN Servers`,subtitle:`ICE servers used for WebRTC NAT traversal. Multiple providers can coexist — the WebRTC layer concatenates servers from all enabled providers per session.`,groups:[{id:J,label:`TURN providers`,install:{capability:J,label:`Add provider`},entries:s.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:N,...W(e.isActive),render:()=>c(U,{addonId:e.addonId,displayName:e.displayName,capName:J,isActive:e.isActive,panels:[{id:`ice-servers`,title:`Discovered ICE servers`,icon:N,content:c(et,{addonId:e.addonId,enabled:e.isActive})}]})}))}],isLoading:e.isLoading,emptyTitle:`No TURN providers installed`,emptyDescription:`Install a TURN addon (e.g. @camstack/addon-cloudflare) to enable WebRTC behind restrictive NATs.`,trailing:c(j,{headingId:`turn-combined-ice`,title:`Combined ICE server list — all enabled providers`,icon:N,actions:c(w,{size:`sm`,variant:`secondary`,onClick:()=>n.refetch(),disabled:n.isFetching,children:`Refresh`}),children:c(`div`,{className:u,children:a.length===0?c(`div`,{className:`text-xs text-foreground-subtle`,children:`No servers from enabled providers.`}):c(`div`,{className:`flex flex-col gap-3`,children:a.map((e,t)=>{let n=Array.isArray(e.urls)?e.urls:[e.urls];return r(`div`,{className:`rounded border border-border bg-surface`,children:[(e.username||e.credential)&&r(`div`,{className:`px-3 py-1.5 border-b border-border text-[10px] font-mono text-foreground-subtle flex items-center gap-3`,children:[e.username&&r(`span`,{children:[c(`span`,{className:`text-foreground-subtle`,children:`user:`}),` `,c(`span`,{className:`text-foreground`,children:e.username})]}),e.credential&&r(`span`,{children:[c(`span`,{className:`text-foreground-subtle`,children:`credential:`}),` `,c(`span`,{className:`text-foreground`,children:`••••••••`})]})]}),c(`ul`,{className:`divide-y divide-border`,children:n.map(e=>c(`li`,{className:`px-3 py-1.5 font-mono text-xs text-foreground break-all`,children:e},e))})]},t)})})})})})}function et({addonId:e,enabled:t}){let n=C({addonId:e},{enabled:t,retry:!1}),r=i(()=>Qe(n.data??[]),[n.data]),a=t?n.isLoading?`Loading…`:r.length===0?`This provider returned no servers.`:null:`Disabled — enable the provider to fetch its servers.`;return c(`div`,{className:`rounded-lg border border-border bg-surface p-2 sm:p-3`,children:a===null?c(`div`,{className:`flex flex-wrap gap-1`,children:r.map(e=>c(`span`,{className:`inline-block break-all rounded bg-foreground-subtle/10 px-1.5 py-0.5 font-mono text-[10px] text-foreground`,children:e},e))}):c(`div`,{className:`text-xs text-foreground-subtle`,children:a})})}n();var tt={lan:N,wifi:ye,docker:L,vpn:je,loopback:M,other:M},nt={lan:`LAN`,wifi:`Wi-Fi`,docker:`Docker`,vpn:`VPN`,loopback:`Loopback`,other:`Other`},rt=[`lan`,`wifi`,`vpn`,`docker`,`other`,`loopback`];function it(e){let{interfaces:t,disabled:n,hideLoopback:a,ipv4Only:o,emptyMessage:s}=e,l=i(()=>t.filter(e=>!(a&&e.kind===`loopback`||o&&e.family!==`IPv4`)),[t,a,o]),u=i(()=>{let e=new Map;for(let t of l){let n=e.get(t.kind)??[];n.push(t),e.set(t.kind,n)}return rt.filter(t=>e.has(t)).map(t=>({kind:t,items:e.get(t)}))},[l]),d=t=>e.mode===`single`?e.value===t:e.value.includes(t),f=t=>{if(n||t.kind===`loopback`)return;if(e.mode===`single`){e.onChange(e.value===t.address?null:t.address);return}let r=new Set(e.value);r.has(t.address)?r.delete(t.address):r.add(t.address),e.onChange([...r])};return l.length===0?c(`div`,{className:`text-xs text-foreground-subtle italic`,children:s??`No addresses available.`}):c(`div`,{className:`flex flex-col gap-3`,children:u.map(({kind:t,items:i})=>r(`div`,{className:`rounded-md border border-border overflow-hidden`,children:[r(`div`,{className:`flex items-center gap-2 px-3 py-1.5 bg-surface-hover/30 border-b border-border`,children:[c(tt[t]??M,{className:`h-3.5 w-3.5 text-foreground-subtle`}),c(`span`,{className:`text-[11px] font-semibold uppercase tracking-wide text-foreground-subtle`,children:nt[t]??t}),r(`span`,{className:`text-[10px] text-foreground-subtle`,children:[`· `,i.length]})]}),c(`ul`,{className:`divide-y divide-border`,children:i.map(t=>{let i=d(t.address),a=t.kind===`loopback`,o=!t.plausible&&!a;return r(`li`,{className:`flex items-center gap-3 px-3 py-2 ${a?`opacity-60 cursor-not-allowed`:n?`opacity-60`:o?`cursor-pointer hover:bg-surface-hover/30 bg-amber-500/[0.04]`:`cursor-pointer hover:bg-surface-hover/30`}`,onClick:()=>f(t),children:[c(`span`,{className:`flex items-center justify-center h-4 w-4 rounded ${e.mode===`single`?`rounded-full`:`rounded`} border ${i?`border-primary bg-primary text-primary-foreground`:`border-border bg-surface`}`,children:i&&c(Ce,{className:`h-3 w-3`})}),r(`div`,{className:`flex-1 min-w-0 ${o?`text-foreground-subtle`:``}`,children:[r(`div`,{className:`flex items-center gap-2 flex-wrap`,children:[c(`span`,{className:`font-mono text-xs font-semibold ${o?`text-foreground-subtle`:`text-foreground`}`,children:t.name}),c(`span`,{className:`text-[10px] rounded px-1.5 py-0.5 bg-foreground-subtle/10 text-foreground-subtle font-mono`,children:t.family}),t.preferred&&c(`span`,{className:`text-[10px] rounded-full px-2 py-0.5 font-medium bg-primary/15 text-primary`,children:`Auto-preferred`}),a&&c(`span`,{className:`text-[10px] rounded-full px-2 py-0.5 font-medium bg-foreground-subtle/15 text-foreground-subtle`,children:`Always included`}),o&&r(`span`,{className:`inline-flex items-center gap-1 text-[10px] rounded-full px-2 py-0.5 font-medium bg-amber-500/15 text-amber-700 dark:text-amber-300`,title:t.plausibleReason||void 0,children:[c(F,{className:`h-2.5 w-2.5`}),`Unlikely usable`]})]}),r(`div`,{className:`text-xs text-foreground-subtle mt-0.5 font-mono break-all`,children:[t.address,t.cidr&&t.cidr!==t.address?` (${t.cidr})`:``]})]})]},`${t.name}-${t.family}-${t.address}`)})})]},t))})}var Y=[{role:`appConnections`,title:`App connections`,single:!1,help:`Addresses the CamStack apps try when connecting; tick several. Automatic: LAN addresses plus your public tunnel.`},{role:`notificationTaps`,title:`Notification taps`,single:!0,help:`Only where tapping a notification opens; one address. Automatic: the CamStack app link (opens the app if installed, otherwise the web app).`},{role:`publicEndpoint`,title:`Public endpoint`,single:!0,help:`Every link that must work away from home: sign-in, single sign-on and account linking, passkeys, and the buttons, attachments and admin links in notifications; one address. Automatic: your public tunnel, then Tailscale, then the LAN. Changing it invalidates registered passkeys.`},{role:`internal`,title:`Internal`,single:!0,help:`Where other CamStack machines and LAN devices download addons, models and media from; one address. Automatic: the hub's LAN address.`}],at=`A role with nothing ticked is automatic. Add a custom address when the hub is reached at an address it cannot see itself (NAT, reverse proxy, VPN, Docker bridge).`;function X(e,t){return e.inUse.includes(t)}function ot(e,t){if(!e.eligible.includes(t))return{kind:`none`};if(e.kind===`app-link`)return X(e,t)?{kind:`none`}:{kind:`set`,input:{role:t,baseUrl:null,enabled:!0}};if(e.flags.includes(t)||t===`appConnections`&&X(e,t))return{kind:`set`,input:{role:t,baseUrl:e.baseUrl,enabled:!1}};let n={role:t,baseUrl:e.baseUrl,enabled:!0};return t===`publicEndpoint`&&e.baseUrl.startsWith(`http:`)?{kind:`confirm-http`,input:n,reason:Fe}:{kind:`set`,input:n}}function st(e){return Y.map(({role:t,title:n})=>{if(t===`appConnections`){let r=e.roles.appConnections;return{role:t,title:n,inUse:r.resolved.length===0?`nothing reachable`:r.resolved.join(`, `),automatic:r.source!==`flagged`}}let r=e.roles[t];return{role:t,title:n,inUse:r.resolved===null?`nothing reachable`:t===`notificationTaps`&&r.source===`auto`?`CamStack app link`:r.resolved,automatic:r.source!==`flagged`}})}n();var ct={"lan-ipv4":{label:`IPv4`,className:`bg-emerald-500/15 text-emerald-700 dark:text-emerald-300`},"lan-ipv6":{label:`IPv6`,className:`bg-violet-500/15 text-violet-700 dark:text-violet-300`},public:{label:`Public`,className:`bg-blue-500/15 text-blue-700 dark:text-blue-300`},"app-link":{label:`App link`,className:`bg-amber-500/15 text-amber-700 dark:text-amber-300`},custom:{label:`Custom`,className:`bg-sky-500/15 text-sky-700 dark:text-sky-300`},loopback:{label:`Loopback`,className:`bg-foreground-subtle/15 text-foreground-subtle`}};function lt({book:e,loading:t,saving:n,error:i,onSetRole:a,onAdd:o,onRemove:l}){let[u,d]=s(``),[f,m]=s(``),[h,g]=s(null),[_,v]=s(null),y=()=>{let e=Ie(u);if(!e.ok){g(e.reason);return}g(null),d(``),m(``),o(e.baseUrl,f.trim())},b=(e,t)=>{let n=ot(e,t.role);if(n.kind!==`none`){if(n.kind===`confirm-http`){v({input:n.input,reason:n.reason});return}v(null),a(n.input)}},x=[{key:`url`,header:`Address`,render:t=>r(`div`,{className:`min-w-0 py-0.5`,children:[c(`div`,{className:`font-mono text-[12px] break-all ${t.plausible?`text-foreground`:`text-foreground-subtle`}`,children:t.baseUrl}),r(`div`,{className:`mt-0.5 flex flex-wrap items-center gap-1.5 text-[11px] text-foreground-subtle`,children:[c(`span`,{children:t.label}),t.kind===`app-link`&&r(`span`,{className:`font-mono`,children:[`hub= `,e?.appLinkHub??`none (no tunnel or Tailscale)`]}),t.custom&&t.detected&&c(`span`,{children:`also added as a custom address`}),t.kind!==`custom`&&t.interfaceKind!==`lan`&&t.interfaceKind!==`public`&&t.interfaceKind!==`other`&&c(`span`,{className:`uppercase tracking-wide`,children:t.interfaceKind}),!t.plausible&&t.interfaceKind!==`loopback`&&r(`span`,{className:`inline-flex items-center gap-1 text-amber-700 dark:text-amber-300`,title:t.plausibleReason||void 0,children:[c(F,{className:`h-2.5 w-2.5`}),`Unlikely usable`]})]})]})},{key:`kind`,header:`Kind`,nowrap:!0,render:e=>{let t=ct[e.kind]??ct.loopback;return c(`span`,{className:`text-[10px] rounded-full px-2 py-0.5 font-medium ${t.className}`,children:t.label})}},...Y.map(e=>({key:e.role,headerRender:()=>c(`span`,{title:e.help,children:e.title}),align:`center`,nowrap:!0,width:`w-24`,render:t=>{let i=t.eligible.includes(e.role),a=t.flags.includes(e.role),o=X(t,e.role);return r(`label`,{className:`inline-flex items-center justify-center gap-1 ${i?`cursor-pointer`:`cursor-not-allowed opacity-40`}`,title:i?o&&!a?`${e.title}: in use automatically. ${e.help}`:e.help:`This address cannot be used for this.`,children:[c(ke,{checked:i&&o,disabled:!i||n,onChange:()=>b(t,e),"aria-label":`${e.title}: ${t.baseUrl}`}),o&&!a&&i&&c(`span`,{className:`text-[9px] text-foreground-subtle`,children:`auto`})]})}})),{key:`actions`,header:``,align:`right`,nowrap:!0,render:e=>e.custom?c(w,{size:`sm`,variant:`ghost`,disabled:n,onClick:()=>l(e.baseUrl),"aria-label":`Remove ${e.baseUrl}`,title:`Remove this custom address (its roles turn automatic)`,children:c(p,{className:`h-3 w-3`})}):null}],S=e===void 0?[]:st(e);return r(Me,{children:[r(`div`,{className:`px-4 py-3 border-b border-border flex flex-col gap-2`,children:[r(`div`,{children:[c(`div`,{className:`text-sm font-semibold`,children:`Addresses`}),c(`div`,{className:`text-xs text-foreground-subtle max-w-3xl`,children:`Every address this hub is reached at, detected or added by you. Tick what each one is used for.`}),r(`details`,{className:`mt-1.5 text-xs max-w-3xl`,"data-testid":`address-roles-help`,children:[c(`summary`,{className:`cursor-pointer select-none text-foreground-subtle hover:text-foreground`,children:`What are roles?`}),c(`dl`,{className:`mt-1.5 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1`,children:Y.map(e=>r(`div`,{className:`contents`,children:[c(`dt`,{className:`font-medium`,children:e.title}),c(`dd`,{className:`text-foreground-subtle`,children:e.help})]},e.role))}),c(`p`,{className:`mt-1.5 text-foreground-subtle`,children:at})]})]}),S.length>0&&c(`div`,{className:`grid grid-cols-[auto_1fr_auto] gap-x-3 gap-y-1 items-baseline text-xs`,"data-testid":`address-roles-summary`,children:S.map(e=>r(`div`,{className:`contents`,children:[c(`span`,{className:`text-foreground-subtle`,children:e.title}),r(`span`,{className:`font-mono break-all`,"data-testid":`address-role-${e.role}`,children:[e.inUse,c(`span`,{className:`ml-1.5 font-sans text-foreground-subtle`,children:e.automatic?`(automatic)`:`(set by you)`})]}),e.automatic?c(`span`,{}):c(w,{size:`sm`,variant:`secondary`,disabled:n,onClick:()=>a({role:e.role,baseUrl:null,enabled:!1}),children:`Use automatic`})]},e.role))}),_!==null&&r(`div`,{className:`rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs flex flex-wrap items-center gap-2`,children:[c(F,{className:`h-3.5 w-3.5 text-amber-600`}),c(`span`,{className:`flex-1 min-w-[220px]`,children:_.reason}),c(w,{size:`sm`,variant:`secondary`,onClick:()=>{a({..._.input,acceptInsecureHttp:!0}),v(null)},children:`Use http anyway`}),c(w,{size:`sm`,variant:`ghost`,onClick:()=>v(null),children:`Cancel`})]}),i!==null&&c(`div`,{className:`text-[11px] text-red-600 dark:text-red-400`,role:`alert`,children:i})]}),c(k,{columns:x,rows:e?.rows??[],rowKey:e=>`${e.kind}-${e.baseUrl}`,minWidthPx:760,bordered:!1,mobileMode:`scroll`,emptyMessage:t?`Loading…`:`No addresses available.`,rowClassName:e=>e.plausible?void 0:`opacity-80`}),r(`div`,{className:`px-4 py-3 border-t border-border flex flex-wrap items-start gap-2`,children:[r(`div`,{className:`flex-1 min-w-[220px]`,children:[c(le,{value:u,onChange:e=>{d(e.target.value),g(null)},placeholder:`https://cams.example.com or https://192.168.1.50:4443`,"aria-label":`Custom address`,disabled:n}),h!==null&&c(`div`,{className:`mt-1 text-[11px] text-red-600 dark:text-red-400`,children:h})]}),c(`div`,{className:`w-48`,children:c(le,{value:f,onChange:e=>m(e.target.value),placeholder:`Label (optional)`,"aria-label":`Custom address label`,maxLength:80,disabled:n})}),r(w,{size:`sm`,variant:`secondary`,disabled:n||u.trim()===``,onClick:y,children:[c(xe,{className:`h-3 w-3`}),`Add custom address`]})]})]})}n();function ut(){let e=o(),t=()=>{e.invalidateQueries({queryKey:[[`localNetwork`]]})},n=ee(void 0,{refetchInterval:3e4}),a=se(void 0),d=he({onSuccess:t}),f=de({onSuccess:e=>{T(e.addresses),t()}}),p=i(()=>n.data?.interfaces??[],[n.data]),m=we({scheme:typeof window<`u`&&window.location.protocol===`https:`?`https`:`http`},{refetchInterval:3e4}),h=b({onSuccess:t}),_=y({onSuccess:t}),v=oe({onSuccess:t}),x=h.error?.message??_.error?.message??v.error?.message??null,S=h.isPending||_.isPending||v.isPending,[C,T]=s([]),[E,D]=s(!1);l(()=>{!E&&a.data&&(T(a.data.addresses),D(!0))},[a.data,E]);let O=a.data?.addresses??[],k=i(()=>{if(C.length!==O.length)return!0;let e=[...C].toSorted(),t=[...O].toSorted();return e.some((e,n)=>e!==t[n])},[C,O]);return r(V,{embedded:!0,children:[r(`div`,{className:`flex items-center justify-between gap-3 flex-wrap`,children:[c(`p`,{className:`text-xs text-foreground-subtle max-w-2xl`,children:`Pin which host interfaces exist, then tick what each address is used for. Add a custom address when this hub is reached at one it cannot detect.`}),c(`div`,{className:`flex items-center gap-2 shrink-0`,children:r(w,{size:`sm`,variant:`secondary`,onClick:()=>{n.refetch(),a.refetch(),m.refetch()},disabled:n.isLoading,children:[c(be,{className:`h-3 w-3 ${n.isLoading?`animate-spin`:``}`}),`Refresh`]})})]}),c(j,{headingId:`network-allowed-addresses`,title:`Allowed addresses`,actions:r(`div`,{className:`flex flex-wrap items-center gap-1`,children:[r(w,{size:`sm`,variant:`secondary`,onClick:()=>f.mutate(void 0),disabled:f.isPending||d.isPending,title:`Re-run the auto-detection heuristic + overwrite the current allowlist with the best matches.`,children:[c(pe,{className:`h-3 w-3`}),f.isPending?`Detecting…`:`Best match`]}),r(w,{size:`sm`,variant:`secondary`,onClick:()=>{T([])},disabled:C.length===0||d.isPending,children:[c(g,{className:`h-3 w-3`}),`Clear`]}),k&&c(w,{size:`sm`,variant:`secondary`,onClick:()=>{T(O)},disabled:d.isPending,children:`Discard`}),r(w,{size:`sm`,variant:`primary`,onClick:()=>{d.mutate({addresses:[...C]})},disabled:!k||d.isPending,children:[c(Oe,{className:`h-3 w-3`}),d.isPending?`Saving…`:`Save`]})]}),children:r(`div`,{className:u,children:[c(`p`,{className:`mb-3 text-xs text-foreground-subtle`,children:C.length===0?`Auto — every non-loopback / non-link-local interface participates.`:`${String(C.length)} pinned`}),c(it,{mode:`multiple`,interfaces:p,value:C,onChange:T,disabled:d.isPending,emptyMessage:n.isLoading?`Loading interfaces…`:`No interfaces detected.`})]})}),c(lt,{book:m.data,loading:m.isLoading,saving:S,error:x,onSetRole:e=>h.mutate({...e}),onAdd:(e,t)=>_.mutate({baseUrl:e,label:t}),onRemove:e=>v.mutate({baseUrl:e})})]})}n();var dt={mesh:`bg-violet-500/15 text-violet-700 dark:text-violet-300`,public:`bg-blue-500/15 text-blue-700 dark:text-blue-300`},Z=5e3,Q=`mesh-network`,ft=3e4,pt={joined:!1,meshIp:``,magicDnsHostname:``,peerCount:0,endpoints:[],tenantName:``,magicDnsSuffix:``,userLogin:null,controlPlaneUrl:``,keyExpiry:null};function mt(){let e=A({capName:`mesh-network`},{refetchInterval:Z}),t=P(void 0,{refetchInterval:Z}),n=i(()=>G(t.data??[]),[t.data]),r=i(()=>(e.data??[]).map(e=>({addonId:e.addonId,displayName:n.get(e.addonId)??e.addonId,isActive:e.isActive})),[e.data,n]);return c(H,{embedded:!0,icon:L,title:`Mesh Networks`,subtitle:`Private VPN meshes that connect CamStack to your other devices (Tailscale, Headscale, …). Each provider can also expose this hub publicly via its ingress (Tailscale Funnel).`,groups:[{id:Q,label:`Mesh networks`,install:{capability:Q,label:`Add mesh`},entries:r.map(e=>({id:e.addonId,name:e.displayName,fallbackIcon:N,...W(e.isActive),render:()=>c(U,{addonId:e.addonId,displayName:e.displayName,capName:Q,isActive:e.isActive,badges:c(gt,{addonId:e.addonId}),primaryAction:c(_t,{addonId:e.addonId}),panels:[{id:`status`,title:`Status`,icon:L,content:c(vt,{addonId:e.addonId})}]})}))}],isLoading:e.isLoading,emptyTitle:`No mesh-network providers installed`,emptyDescription:`Install a mesh addon (e.g. @camstack/addon-tailscale) to join this hub into a private VPN mesh.`})}function ht(e){let{data:t,isLoading:n}=me({addonId:e},{refetchInterval:Z,retry:!1}),r=t?Re.safeParse(t):null;return{status:r?.success?r.data:pt,isLoading:n}}function gt({addonId:e}){let{status:t}=ht(e);return r(a,{children:[c(I,{variant:t.joined?`success`:`default`,className:We,children:t.joined?`Joined`:`Not joined`}),t.joined&&r(I,{className:`text-[10px]`,children:[t.peerCount,` peer`,t.peerCount===1?``:`s`]})]})}function _t({addonId:t}){let n=o(),[i,l]=s(!1),[u,d]=s(null),f=h(),{status:p}=ht(t),m=e(()=>{n.invalidateQueries({queryKey:[[`meshNetwork`]]})},[n]),g=De(),_=ge({onSuccess:m}),v=ie({onSuccess:m}),y=e(async()=>{l(!0);let e={cancelled:!1};d(e);try{let n=await g.mutateAsync({addonId:t});if(e.cancelled)return;n.loginUrl&&window.open(n.loginUrl,`_blank`,`noopener,noreferrer`);let r=Date.now();for(;!e.cancelled&&(await Tt(2e3),!e.cancelled);){let e=await f.trpcClient.meshNetwork.getStatus.query({addonId:t}).catch(()=>null),n=e?Re.safeParse(e):null;if(n?.success&&n.data.joined){m();break}if(Date.now()-r>6e5)break}}finally{l(!1),d(null)}},[t,m,g,f]),b=e(()=>{u&&(u.cancelled=!0),l(!1),d(null)},[u]);return c(a,{children:p.joined?r(a,{children:[r(w,{size:`sm`,variant:`secondary`,disabled:_.isPending,onClick:()=>_.mutate({addonId:t}),children:[_.isPending?c(S,{className:`h-3 w-3 animate-spin`}):c(Ve,{className:`h-3 w-3`}),`Disconnect`]}),r(w,{size:`sm`,variant:`ghost`,disabled:v.isPending,onClick:()=>v.mutate({addonId:t}),children:[v.isPending?c(S,{className:`h-3 w-3 animate-spin`}):c(ae,{className:`h-3 w-3`}),`Log out`]})]}):i?r(w,{size:`sm`,variant:`ghost`,onClick:b,children:[c(Te,{className:`h-3 w-3 mr-1`}),` Cancel`]}):r(a,{children:[r(w,{size:`sm`,variant:`primary`,onClick:()=>void y(),children:[c(Pe,{className:`h-3 w-3`}),`Connect`]}),r(w,{size:`sm`,variant:`ghost`,disabled:v.isPending,onClick:()=>v.mutate({addonId:t}),children:[v.isPending?c(S,{className:`h-3 w-3 animate-spin`}):c(ae,{className:`h-3 w-3`}),`Log out`]})]})})}function vt({addonId:e}){let{status:t}=ht(e),n=T({addonId:e},{refetchInterval:ft,retry:!1});return r(`div`,{className:`rounded-lg border border-border bg-surface`,children:[r(`div`,{className:`space-y-4 p-2 sm:p-4`,children:[t.error&&c(D,{message:t.error}),c(yt,{status:t}),t.endpoints.length>0&&r(`div`,{className:`space-y-1.5`,children:[c(`div`,{className:`text-[11px] font-semibold text-foreground-subtle uppercase tracking-wide`,children:`Endpoints`}),t.endpoints.map(e=>r(`div`,{className:`flex items-center gap-2 text-xs flex-wrap`,children:[r(`span`,{className:`text-[10px] rounded-full px-2 py-0.5 font-medium ${dt[e.scope]??`bg-foreground-subtle/15 text-foreground-subtle`}`,children:[e.scope===`public`?c(z,{className:`inline h-2.5 w-2.5 mr-0.5`}):null,e.label]}),r(`a`,{href:e.url,target:`_blank`,rel:`noopener noreferrer`,className:`text-emerald-700 dark:text-emerald-300 hover:underline break-all flex items-center gap-1`,children:[c(re,{className:`h-3 w-3 shrink-0`}),e.url]})]},e.id))]})]}),c(`div`,{className:`border-t border-border`,children:c(xt,{peers:n.data?.peers??[],loading:n.isLoading})})]})}function yt({status:e}){return c(`div`,{className:`grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-1.5`,children:[{label:`Tenant`,value:e.tenantName||`—`},{label:`User`,value:e.userLogin??`—`},{label:`Control plane`,value:e.controlPlaneUrl||`—`},{label:`Mesh IP`,value:e.meshIp||`—`,mono:!0},{label:`MagicDNS`,value:e.magicDnsHostname||`—`,mono:!0},{label:`DNS suffix`,value:e.magicDnsSuffix||`—`,mono:!0},{label:`Peers`,value:String(e.peerCount)},{label:`Key expiry`,value:e.keyExpiry?bt(e.keyExpiry):`—`}].map(e=>r(`div`,{className:`flex items-baseline justify-between gap-3 text-xs`,children:[c(`span`,{className:`text-foreground-subtle shrink-0`,children:e.label}),c(`span`,{className:`text-foreground truncate ${e.mono?`font-mono`:``}`,children:e.value})]},e.label))})}function bt(e){let t=e-Date.now();if(t<0)return`Expired ${wt(e)}`;let n=Math.floor(t/864e5);if(n>60)return new Date(e).toLocaleDateString();if(n>1)return`in ${n} days`;let r=Math.floor(t/36e5);return r>1?`in ${r}h`:`in ${Math.max(1,Math.floor(t/6e4))} min`}function xt({peers:t,loading:n}){let a=i(()=>t.map(e=>({...e,_id:e.id})),[t]),o=e(e=>{e&&navigator.clipboard?.writeText(e)},[]),s=i(()=>[{key:`hostname`,header:`Hostname`,render:e=>r(`div`,{className:`flex flex-col`,children:[r(`div`,{className:`flex items-center gap-1.5`,children:[c(`span`,{className:`font-medium text-foreground`,children:e.hostname||`(unknown)`}),e.isSelf&&c(I,{variant:`info`,children:`self`}),e.exitNodeOption&&c(I,{variant:`warning`,children:`exit-node`})]}),e.userLogin&&c(`div`,{className:`text-[10px] text-foreground-subtle truncate`,children:e.userLogin})]})},{key:`magicDns`,header:`MagicDNS`,render:e=>e.magicDns?r(`div`,{className:`flex items-center gap-1`,children:[c(`span`,{className:`font-mono text-xs truncate`,children:e.magicDns}),c(`button`,{onClick:()=>o(e.magicDns),className:`p-0.5 hover:bg-foreground-subtle/10 rounded`,"aria-label":`Copy MagicDNS`,children:c(v,{className:`h-3 w-3 text-foreground-subtle`})})]}):c(`span`,{className:`text-foreground-subtle`,children:`—`})},{key:`addresses`,header:`IP`,render:e=>{let t=e.addresses[0]??``;return t?r(`div`,{className:`flex items-center gap-1`,children:[c(`span`,{className:`font-mono text-xs`,children:t}),c(`button`,{onClick:()=>o(t),className:`p-0.5 hover:bg-foreground-subtle/10 rounded`,"aria-label":`Copy IP`,children:c(v,{className:`h-3 w-3 text-foreground-subtle`})})]}):c(`span`,{className:`text-foreground-subtle`,children:`—`})}},{key:`connection`,header:`Connection`,render:e=>r(`div`,{className:`flex items-center gap-1.5`,children:[c(I,{variant:St(e.connection),children:e.connection}),e.relay&&c(`span`,{className:`text-[10px] text-foreground-subtle font-mono`,children:e.relay})]})},{key:`routes`,header:`Routes`,render:e=>e.advertisedRoutes.length>0?c(`span`,{className:`text-[10px] font-mono text-foreground-subtle truncate`,children:e.advertisedRoutes.join(`, `)}):c(`span`,{className:`text-foreground-subtle`,children:`—`})},{key:`tx`,header:`TX / RX`,render:e=>r(`span`,{className:`text-[10px] text-foreground-subtle font-mono`,children:[`↑ `,Ct(e.txBytes),` · ↓ `,Ct(e.rxBytes)]})},{key:`lastSeen`,header:`Last seen`,render:e=>c(`span`,{className:`text-[10px] text-foreground-subtle`,children:e.lastSeenMs>0?wt(e.lastSeenMs):`—`})}],[o]);return n&&t.length===0?r(`div`,{className:`p-4 text-xs text-foreground-subtle`,children:[c(S,{className:`h-3 w-3 animate-spin inline mr-1`}),` Loading peers…`]}):t.length===0?c(`div`,{className:`p-4 text-xs text-foreground-subtle`,children:`No peers visible — your tailnet may be empty or the host is not joined.`}):c(`div`,{className:`p-4`,children:c(k,{columns:s,rows:a,rowKey:e=>e._id,minWidthPx:820})})}function St(e){return e===`direct`?`success`:e===`relay`?`warning`:`info`}function Ct(e){return e<1024?`${e}B`:e<1024*1024?`${(e/1024).toFixed(1)}KB`:e<1024*1024*1024?`${(e/1024/1024).toFixed(1)}MB`:`${(e/1024/1024/1024).toFixed(2)}GB`}function wt(e){let t=Date.now()-e;return t<6e4?`just now`:t<36e5?`${Math.round(t/6e4)}m ago`:t<864e5?`${Math.round(t/36e5)}h ago`:`${Math.round(t/864e5)}d ago`}function Tt(e){return new Promise(t=>setTimeout(t,e))}var Et=/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/,Dt=/-----BEGIN ((?:RSA |EC |ENCRYPTED )?PRIVATE KEY)-----[\s\S]+?-----END \1-----/,Ot={certificate:`.pem,.crt,.cer`,privateKey:`.pem,.key`,ca:`.pem,.crt,.cer`};function $(e,t){if(t.trim()===``)return null;if(e===`privateKey`){let e=Dt.exec(t);return e===null?Et.test(t)?`This is a certificate, not a private key.`:`Not a PEM private key — expected -----BEGIN … PRIVATE KEY-----.`:e[1]===`ENCRYPTED PRIVATE KEY`?`The private key is password-protected. Export it without a passphrase.`:null}return Et.test(t)?e===`certificate`&&Dt.test(t)?`This file also contains a private key. Pick the certificate file, and the key in its own field.`:null:Dt.test(t)?`This is a private key, not a certificate.`:`Not a PEM certificate — expected -----BEGIN CERTIFICATE-----.`}function kt(e){return e.text()}n();function At({kind:e,label:n,value:i,onChange:a,rows:o=6}){let l=t(null),[u,d]=s(null),[f,p]=s(null),m=f??$(e,i),h=`pem-${e}`,g=async e=>{let t=e.target.files?.[0];if(e.target.value=``,t!==void 0){d(t.name);try{let e=await kt(t);p(null),a(e)}catch(e){p(`Could not read ${t.name}: ${e instanceof Error?e.message:String(e)}`)}}};return r(`div`,{className:`space-y-1`,children:[r(`div`,{className:`flex items-center justify-between gap-2`,children:[c(`label`,{htmlFor:h,className:`text-[11px] text-foreground-subtle`,children:n}),r(`div`,{className:`flex min-w-0 items-center gap-2`,children:[u!==null&&c(`span`,{className:`truncate font-mono text-[10px] text-foreground-subtle`,"data-testid":`${h}-file-name`,children:u}),c(`input`,{ref:l,type:`file`,accept:Ot[e],onChange:e=>void g(e),className:`hidden`,"data-testid":`${h}-file`}),r(w,{size:`sm`,variant:`outline`,type:`button`,onClick:()=>l.current?.click(),children:[c(Ge,{className:`mr-1 h-3 w-3`}),`Choose file`]})]})]}),c(`textarea`,{id:h,value:i,onChange:e=>{d(null),p(null),a(e.target.value)},rows:o,"aria-invalid":m!==null,className:`w-full rounded border border-border bg-surface px-2 py-1 font-mono text-[11px]`}),m!==null&&c(`p`,{className:`text-[11px] text-danger`,role:`alert`,children:m})]})}n();function jt(e){if(e===null||e===``)return`—`;let t=e.replace(/:/g,``);return t.length<=16?e:`${t.slice(0,8)}…${t.slice(-8)}`}function Mt(){let e=o(),t=ce(void 0,{refetchInterval:15e3}),n=te({onSuccess:()=>void e.invalidateQueries({queryKey:[[`localNetwork`]]})}),i=Ne({onSuccess:()=>void e.invalidateQueries({queryKey:[[`localNetwork`]]})}),a=O({onSuccess:()=>{p(!1),h(``),_(``),y(``),e.invalidateQueries({queryKey:[[`localNetwork`]]})}}),l=t.data,[f,p]=s(!1),[m,h]=s(``),[g,_]=s(``),[v,y]=s(``),b=()=>{if(!l?.caCertPem)return;let e=new Blob([l.caCertPem],{type:`application/x-pem-file`}),t=URL.createObjectURL(e),n=document.createElement(`a`);n.href=t,n.download=`camstack-ca.crt`,n.click(),URL.revokeObjectURL(t)},x=l?.mode===`uploaded`?`Uploaded`:l?.mode===`generated`?`Generated local CA`:`TLS disabled`;return c(V,{embedded:!0,children:r(`div`,{className:d,children:[c(Ue,{nodeId:`hub`,addonIds:[`local-network`],level:`global`,title:`LAN HTTP`,description:`Native apps on this Wi-Fi use http:// and skip the private certificate. The tunnel and this admin UI stay on HTTPS.`}),c(j,{headingId:`local-access-certificate`,title:`Certificate`,icon:E,children:r(`div`,{className:`${u} space-y-3`,children:[t.isError&&c(`p`,{className:`text-xs text-danger`,children:`Could not read TLS status.`}),l?.reissueError&&c(D,{variant:`warning`,title:`Certificate reissue`,message:l.reissueError}),l?.restartRequired&&c(D,{variant:`warning`,title:`Restart required`,message:`The HTTPS listener still serves the previous certificate. Restart the hub to apply it.`}),r(`dl`,{className:`grid grid-cols-1 gap-2 text-xs sm:grid-cols-2`,children:[r(`div`,{children:[c(`dt`,{className:`text-foreground-subtle`,children:`Mode`}),c(`dd`,{className:`font-medium`,children:x})]}),r(`div`,{children:[c(`dt`,{className:`text-foreground-subtle`,children:`Valid to`}),c(`dd`,{className:`font-mono`,children:l?.validTo?new Date(l.validTo).toLocaleString():`—`})]}),r(`div`,{children:[c(`dt`,{className:`text-foreground-subtle`,children:`Leaf SHA-256`}),c(`dd`,{className:`font-mono`,title:l?.leafFingerprintSha256??void 0,children:jt(l?.leafFingerprintSha256??null)})]}),r(`div`,{children:[c(`dt`,{className:`text-foreground-subtle`,children:`CA SHA-256`}),c(`dd`,{className:`font-mono`,title:l?.caFingerprintSha256??void 0,children:l?.mode===`uploaded`&&l.caFingerprintSha256===null?c(`span`,{className:`font-sans text-foreground-subtle`,children:`Issuer not included in the upload (not the local CA)`}):jt(l?.caFingerprintSha256??null)})]})]}),l&&l.sans.length>0&&c(`div`,{className:`flex flex-wrap gap-1`,children:l.sans.map(e=>c(`span`,{className:`rounded border border-border bg-surface px-1.5 py-0.5 font-mono text-[10px] text-foreground-subtle`,children:e},e))}),r(`div`,{className:`flex flex-wrap gap-2`,children:[c(ne,{label:`Regenerate`,title:`Issue a new leaf?`,description:`Issues a new leaf under the current local CA. Clients that trust the CA keep working. The HTTPS listener reloads.`,confirmLabel:`Regenerate`,disabled:l?.mode===`uploaded`||n.isPending,action:()=>n.mutateAsync({})}),c(w,{size:`sm`,variant:`outline`,onClick:()=>p(e=>!e),children:`Upload`}),l?.caCertPem&&c(w,{size:`sm`,variant:`outline`,onClick:b,children:`Download CA`}),l?.mode===`uploaded`&&c(ne,{label:`Revert to generated`,title:`Drop the uploaded certificate?`,description:`Deletes the uploaded pair and returns to the generated local CA.`,confirmLabel:`Revert`,confirmVariant:`danger`,disabled:i.isPending,action:()=>i.mutateAsync({})})]}),f&&r(`div`,{className:`space-y-2 border-t border-border pt-3`,children:[c(At,{kind:`certificate`,label:`Certificate or full chain (PEM)`,value:m,onChange:h}),c(At,{kind:`privateKey`,label:`Private key (PEM)`,value:g,onChange:_}),c(At,{kind:`ca`,label:`CA (PEM, optional)`,value:v,onChange:y,rows:4}),c(w,{size:`sm`,disabled:!m.trim()||!g.trim()||$(`certificate`,m)!==null||$(`privateKey`,g)!==null||$(`ca`,v)!==null||a.isPending,onClick:()=>a.mutate({certPem:m,keyPem:g,...v.trim()?{caPem:v}:{}}),children:`Save certificate`}),a.isError&&c(`p`,{className:`text-xs text-danger`,children:a.error instanceof Error?a.error.message:`Upload failed`})]})]})})]})})}n();function Nt({value:e,onChange:t}){let{data:n}=Ae({isCamera:!0,projection:`slim`}),a=i(()=>n===void 0?[{value:``,label:`Loading cameras…`}]:n.length===0?[{value:``,label:`No cameras`}]:[{value:``,label:`Pick a camera`},...n.toSorted((e,t)=>e.name.localeCompare(t.name)).map(e=>({value:String(e.id),label:`${e.name} (#${String(e.id)})`}))],[n]);return r(`label`,{className:`flex min-w-0 flex-col gap-1`,children:[c(`span`,{className:`text-[11px] text-foreground-subtle`,children:`Camera`}),c(Ee,{value:e===null?``:String(e),disabled:n===void 0||n.length===0,onChange:e=>t(e.target.value===``?null:Number(e.target.value)),options:a})]})}function Pt(){let t=h(),[n,i]=s(null);return c(V,{embedded:!0,children:r(`div`,{className:d,"data-testid":`network-diagnostics`,children:[c(x,{loadServers:e(async()=>t.trpcClient.turnProvider.getTurnServers.query(),[t])}),c(fe,{trpc:t.trpcClient,serverUrl:f(),deviceId:n,cameraField:c(Nt,{value:n,onChange:i})})]})})}function Ft(){let{t:e}=ze(),t=Be(`/system/network`,{t:e,isAdmin:!0})??{icon:z,title:e(`system.network`,`Network`)};return c(He,{icon:t.icon,title:t.title,defaultSection:`addresses`,sectionsLabel:`${t.title} sections`,sections:[{id:`local-access`,label:`Local access`,content:c(Mt,{})},{id:`addresses`,label:`Network Addresses`,content:c(ut,{})},{id:R.remoteAccess.tab,label:`Remote Access`,content:c(qe,{}),ownedParams:B},{id:R.mesh.tab,label:`Mesh Networks`,content:c(mt,{}),ownedParams:B},{id:R.turn.tab,label:`TURN Servers`,content:c($e,{}),ownedParams:B},{id:`diagnostics`,label:`Diagnostics`,content:c(Pt,{})}]})}export{Ft as NetworkPage};