paskia 0.9.1__py3-none-any.whl → 0.10.2__py3-none-any.whl
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.
- paskia/_version.py +2 -2
- paskia/bootstrap.py +8 -7
- paskia/db/__init__.py +2 -0
- paskia/db/background.py +5 -8
- paskia/db/jsonl.py +2 -2
- paskia/db/logging.py +130 -45
- paskia/db/operations.py +25 -4
- paskia/db/structs.py +3 -2
- paskia/fastapi/__main__.py +33 -19
- paskia/fastapi/admin.py +2 -2
- paskia/fastapi/api.py +7 -3
- paskia/fastapi/authz.py +11 -9
- paskia/fastapi/logging.py +64 -21
- paskia/fastapi/mainapp.py +8 -5
- paskia/fastapi/remote.py +11 -37
- paskia/fastapi/user.py +22 -0
- paskia/fastapi/ws.py +12 -35
- paskia/fastapi/wschat.py +55 -2
- paskia/fastapi/wsutil.py +2 -7
- paskia/frontend-build/auth/admin/index.html +7 -6
- paskia/frontend-build/auth/assets/{AccessDenied-DPkUS8LZ.css → AccessDenied-CVQZxSIL.css} +1 -1
- paskia/frontend-build/auth/assets/AccessDenied-Licr0tqA.js +8 -0
- paskia/frontend-build/auth/assets/{RestrictedAuth-CvR33_Z0.css → RestrictedAuth-0MFeNWS2.css} +1 -1
- paskia/frontend-build/auth/assets/{RestrictedAuth-DsJXicIw.js → RestrictedAuth-DWKMTEV3.js} +1 -1
- paskia/frontend-build/auth/assets/_plugin-vue_export-helper-DJsHCwvl.js +33 -0
- paskia/frontend-build/auth/assets/_plugin-vue_export-helper-DUBf8-iM.css +1 -0
- paskia/frontend-build/auth/assets/{admin-DzzjSg72.css → admin-B1H4YqM_.css} +1 -1
- paskia/frontend-build/auth/assets/admin-CZKsX1OI.js +1 -0
- paskia/frontend-build/auth/assets/{auth-C7k64Wad.css → auth-B4EpDxom.css} +1 -1
- paskia/frontend-build/auth/assets/auth-Pe-PKe8b.js +1 -0
- paskia/frontend-build/auth/assets/forward-BC0p23CH.js +1 -0
- paskia/frontend-build/auth/assets/{pow-2N9bxgAo.js → pow-DUr-T9XX.js} +1 -1
- paskia/frontend-build/auth/assets/reset-B8PlNXuP.css +1 -0
- paskia/frontend-build/auth/assets/reset-CkY9h28U.js +1 -0
- paskia/frontend-build/auth/assets/restricted-C9cJlHkd.js +1 -0
- paskia/frontend-build/auth/assets/theme-C2WysaSw.js +1 -0
- paskia/frontend-build/auth/index.html +8 -7
- paskia/frontend-build/auth/restricted/index.html +7 -6
- paskia/frontend-build/int/forward/index.html +6 -6
- paskia/frontend-build/int/reset/index.html +4 -4
- paskia/frontend-build/paskia.webp +0 -0
- paskia/util/__init__.py +0 -0
- paskia/util/apistructs.py +110 -0
- paskia/util/frontend.py +75 -0
- paskia/util/hostutil.py +75 -0
- paskia/util/htmlutil.py +47 -0
- paskia/util/passphrase.py +20 -0
- paskia/util/permutil.py +43 -0
- paskia/util/pow.py +45 -0
- paskia/util/querysafe.py +11 -0
- paskia/util/sessionutil.py +38 -0
- paskia/util/startupbox.py +103 -0
- paskia/util/timeutil.py +47 -0
- paskia/util/useragent.py +10 -0
- paskia/util/userinfo.py +63 -0
- paskia/util/vitedev.py +71 -0
- paskia/util/wordlist.py +54 -0
- {paskia-0.9.1.dist-info → paskia-0.10.2.dist-info}/METADATA +14 -11
- paskia-0.10.2.dist-info/RECORD +78 -0
- paskia/frontend-build/auth/assets/AccessDenied-Fmeb6EtF.js +0 -8
- paskia/frontend-build/auth/assets/_plugin-vue_export-helper-BTzJAQlS.css +0 -1
- paskia/frontend-build/auth/assets/_plugin-vue_export-helper-nhjnO_bd.js +0 -2
- paskia/frontend-build/auth/assets/admin-CPE1pLMm.js +0 -1
- paskia/frontend-build/auth/assets/auth-YIZvPlW_.js +0 -1
- paskia/frontend-build/auth/assets/forward-DmqVHZ7e.js +0 -1
- paskia/frontend-build/auth/assets/reset-Chtv69AT.css +0 -1
- paskia/frontend-build/auth/assets/reset-s20PATTN.js +0 -1
- paskia/frontend-build/auth/assets/restricted-D3AJx3_6.js +0 -1
- paskia-0.9.1.dist-info/RECORD +0 -60
- {paskia-0.9.1.dist-info → paskia-0.10.2.dist-info}/WHEEL +0 -0
- {paskia-0.9.1.dist-info → paskia-0.10.2.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as re,b as m,c as p,d as o,t as A,g as T,F as C,r as X,f as Re,B as te,m as I,A as J,E as ie,k as O,j as Ce,L as De,x as G,q as ae,G as ke,z as P,y as _e,h as z,i as ne,v as oe,u as ge,o as Se,a as Oe,N as et,w as tt,C as st,D as nt,J as ot,K as at}from"./_plugin-vue_export-helper-DJsHCwvl.js";import{i as it}from"./theme-C2WysaSw.js";import{u as Ae,U as rt,_ as lt,a as ut,R as dt,N as me,M as ct,b as mt,L as ft,A as fe,B as gt,c as vt}from"./AccessDenied-Licr0tqA.js";import"./helpers-DzjFIx78.js";const yt={key:0},pt=["onClick"],ht=["onClick"],bt={class:"role-names"},wt={class:"center"},$t={key:0,class:"center"},Dt=["onClick"],kt={key:0,class:"permissions-section"},St={class:"matrix-scroll"},Ot=["title"],Rt=["title"],Ct={class:"display-text"},At=["checked","onChange"],Ut={class:"perm-name-cell"},Et={class:"perm-title"},Mt={class:"display-text"},Pt=["onClick"],xt={class:"perm-id-info"},qt={class:"id-text"},Nt={class:"perm-domain"},Tt={class:"perm-members center"},Lt={class:"perm-actions center"},Ft=["onClick"],It={__name:"AdminOverview",props:{info:Object,orgs:Array,permissions:Array,permissionSummary:Object,navigationDisabled:{type:Boolean,default:!1}},emits:["createOrg","openOrg","updateOrg","deleteOrg","toggleOrgPermission","openDialog","deletePermission","renamePermissionDisplay","navigateOut"],setup(n,{expose:V,emit:Y}){const k=n,L=Y,E=O(null),c=O(null),y=O(null),x=O(null),N=O(null),B=O(null),q=I(()=>[...k.orgs].sort((r,t)=>{const s=r.display_name.localeCompare(t.display_name);return s!==0?s:r.uuid.localeCompare(t.uuid)})),w=I(()=>[...k.permissions].sort((r,t)=>r.scope.localeCompare(t.scope))),F=I(()=>k.info?.ctx.permissions.includes("auth:admin")),$=I(()=>k.info?.ctx.permissions.includes("auth:org:admin"));function _(r){return r.roles.slice().sort((t,s)=>t.display_name.localeCompare(s.display_name)).map(t=>t.display_name).join(", ")}function Z(r,t){if(k.navigationDisabled)return;const s=J(r);if(!s)return;const D=r.target,l=D.closest("tr");if(!l)return;const d=l.closest("tbody");if(!d)return;const h=Array.from(d.querySelectorAll("tr")),R=h.indexOf(l);if(R===-1)return;if(s==="left"||s==="right"){r.preventDefault();const M=Array.from(l.querySelectorAll("a, button:not([disabled])")),K=M.indexOf(D);if(K===-1)return;s==="left"&&K>0?M[K-1].focus():s==="right"&&K<M.length-1&&M[K+1].focus();return}let j=R;if(s==="up"&&R>0)j=R-1;else if(s==="down"&&R<h.length-1)j=R+1;else if(s==="up"&&R===0){r.preventDefault(),t==="org"?te(c.value,{itemSelector:"button"}):t==="perm"&&te(N.value,{itemSelector:"button"});return}else if(s==="down"&&R===h.length-1){if(r.preventDefault(),t==="org"&&F.value&&x.value){const M=x.value.querySelector('input[type="checkbox"]');M?M.focus():te(N.value,{itemSelector:"button"})}return}if(j!==R){r.preventDefault();const K=h[j].querySelector("a, button:not([disabled])");K&&K.focus()}}function ee(r){if(k.navigationDisabled)return;const t=J(r);if(t){if(r.preventDefault(),t==="left"||t==="right")ie(c.value,r.target,t,{itemSelector:"button"});else if(t==="up")L("navigateOut","up");else if(t==="down"){const s=y.value?.querySelector("tbody tr a, tbody tr button:not([disabled])");s&&s.focus()}}}function g(r){if(k.navigationDisabled)return;const t=J(r);if(!t)return;const s=r.target;if(s.tagName!=="INPUT")return;r.preventDefault();const D=Array.from(x.value.querySelectorAll('input[type="checkbox"]')),l=D.indexOf(s);if(l===-1)return;const d=q.value.length,h=w.value.length;if(d===0||h===0)return;const R=Math.floor(l/d),j=l%d;let M=l;if(t==="left")j>0&&(M=l-1);else if(t==="right")j<d-1&&(M=l+1);else if(t==="up")if(R>0)M=l-d;else{const le=y.value?.querySelector("tbody tr:last-child")?.querySelector("a, button:not([disabled])");le&&le.focus();return}else if(t==="down")if(R<h-1)M=l+d;else{te(N.value,{itemSelector:"button"});return}M!==l&&D[M]&&D[M].focus()}function u(r){if(k.navigationDisabled)return;const t=J(r);if(t){if(r.preventDefault(),t==="left"||t==="right")ie(N.value,r.target,t,{itemSelector:"button"});else if(t==="up"){const s=x.value?.querySelectorAll('input[type="checkbox"]');if(s?.length){const D=q.value.length,d=(w.value.length-1)*D;s[d]?s[d].focus():s[0].focus()}else{const l=y.value?.querySelector("tbody tr:last-child")?.querySelector("a, button:not([disabled])");l&&l.focus()}}else if(t==="down"){const s=B.value?.querySelector("tbody tr button:not([disabled])");s&&s.focus()}}}function f(){if(F.value)te(c.value,{itemSelector:"button"});else{const r=y.value?.querySelector("tbody tr a, tbody tr button:not([disabled])");r&&r.focus()}}return V({focusFirstElement:f}),(r,t)=>(m(),p(C,null,[o("div",{class:"permissions-section",ref_key:"orgSection",ref:E},[o("h2",null,A(F.value?"Organizations":"Your Organizations"),1),o("div",{class:"actions",ref_key:"orgActionsRef",ref:c,onKeydown:ee},[F.value?(m(),p("button",{key:0,onClick:t[0]||(t[0]=s=>r.$emit("createOrg"))},"+ Create Org")):T("",!0)],544),o("table",{class:"org-table",ref_key:"orgTableRef",ref:y,onKeydown:t[1]||(t[1]=s=>Z(s,"org"))},[o("thead",null,[o("tr",null,[t[4]||(t[4]=o("th",null,"Name",-1)),t[5]||(t[5]=o("th",null,"Roles",-1)),t[6]||(t[6]=o("th",null,"Members",-1)),F.value?(m(),p("th",yt,"Actions")):T("",!0)])]),o("tbody",null,[(m(!0),p(C,null,X(q.value,s=>(m(),p("tr",{key:s.uuid},[o("td",null,[o("a",{href:"#org/{{o.uuid}}",onClick:Ce(D=>r.$emit("openOrg",s),["prevent"])},A(s.display_name),9,pt),F.value||$.value?(m(),p("button",{key:0,onClick:D=>r.$emit("updateOrg",s),class:"icon-btn edit-org-btn","aria-label":"Rename organization",title:"Rename organization"},"✏️",8,ht)):T("",!0)]),o("td",bt,A(_(s)),1),o("td",wt,A(s.roles.reduce((D,l)=>D+l.users.length,0)),1),F.value?(m(),p("td",$t,[o("button",{onClick:D=>r.$emit("deleteOrg",s),class:"icon-btn delete-icon","aria-label":"Delete organization",title:"Delete organization"},"❌",8,Dt)])):T("",!0)]))),128))])],544)],512),F.value?(m(),p("div",kt,[t[10]||(t[10]=o("h2",null,"Permissions",-1)),o("div",{class:"matrix-wrapper",ref_key:"permMatrixRef",ref:x,onKeydown:g},[o("div",St,[o("div",{class:"perm-matrix-grid",style:Re({gridTemplateColumns:"minmax(180px, 1fr) "+q.value.map(()=>"2.2rem").join(" ")})},[t[7]||(t[7]=o("div",{class:"grid-head perm-head"},"Permission",-1)),(m(!0),p(C,null,X(q.value,s=>(m(),p("div",{key:"head-"+s.uuid,class:"grid-head org-head",title:s.display_name},[o("span",null,A(s.display_name),1)],8,Ot))),128)),(m(!0),p(C,null,X(w.value,s=>(m(),p(C,{key:s.uuid},[o("div",{class:"perm-name",title:s.scope},[o("span",Ct,A(s.display_name),1)],8,Rt),(m(!0),p(C,null,X(q.value,D=>(m(),p("div",{key:D.uuid+"-"+s.uuid,class:"matrix-cell"},[o("input",{type:"checkbox",checked:D.permissions.includes(s.uuid),onChange:l=>r.$emit("toggleOrgPermission",D,s.uuid,l.target.checked)},null,40,At)]))),128))],64))),128))],4)]),t[8]||(t[8]=o("p",{class:"matrix-hint muted"},"Toggle which permissions each organization can grant to its members.",-1))],544),o("div",{class:"actions",ref_key:"permActionsRef",ref:N,onKeydown:u},[F.value?(m(),p("button",{key:0,onClick:t[2]||(t[2]=s=>r.$emit("openDialog","perm-create",{display_name:"",scope:"",domain:""}))},"+ Create Permission")):T("",!0)],544),o("table",{class:"org-table",ref_key:"permTableRef",ref:B,onKeydown:t[3]||(t[3]=s=>Z(s,"perm"))},[t[9]||(t[9]=o("thead",null,[o("tr",null,[o("th",{scope:"col"},"Permission"),o("th",{scope:"col"},"Domain"),o("th",{scope:"col",class:"center"},"Members"),o("th",{scope:"col",class:"center"},"Actions")])],-1)),o("tbody",null,[(m(!0),p(C,null,X(w.value,s=>(m(),p("tr",{key:s.uuid},[o("td",Ut,[o("div",Et,[o("span",Mt,A(s.display_name),1),o("button",{onClick:D=>r.$emit("renamePermissionDisplay",s),class:"icon-btn edit-display-btn","aria-label":"Edit permission",title:"Edit permission"},"✏️",8,Pt)]),o("div",xt,[o("span",qt,A(s.scope),1)])]),o("td",Nt,A(s.domain||"—"),1),o("td",Tt,A(n.permissionSummary[s.uuid]?.userCount||0),1),o("td",Lt,[o("button",{onClick:D=>r.$emit("deletePermission",s),class:"icon-btn delete-icon","aria-label":"Delete permission",title:"Delete permission"},"❌",8,Ft)])]))),128))])],544)])):T("",!0)],64))}},Kt=re(It,[["__scopeId","data-v-56228c4a"]]),Bt=["title"],zt={class:"org-name"},Vt={class:"matrix-scroll"},jt=["title"],Ht=["title"],Gt=["checked","onChange"],Jt=["onDrop"],Yt=["onKeydown"],Wt=["title"],Zt=["onClick"],Qt={class:"role-actions"},Xt=["onClick"],_t=["onDragstart","onClick","onKeydown","title"],es={class:"name"},ts={class:"meta"},ss=["onKeydown"],ns=["onClick"],os={__name:"AdminOrgDetail",props:{selectedOrg:Object,permissions:Array,navigationDisabled:{type:Boolean,default:!1}},emits:["updateOrg","createRole","updateRole","deleteRole","createUserInRole","openUser","toggleRolePermission","onRoleDragOver","onRoleDrop","onUserDragStart","navigateOut"],setup(n,{expose:V,emit:Y}){const k=n,L=Y,E=O(null),c=O(null),y=O(null),x=I(()=>[...k.selectedOrg.roles].sort((g,u)=>{const f=g.display_name.toLowerCase(),r=u.display_name.toLowerCase();return f!==r?f.localeCompare(r):g.uuid.localeCompare(u.uuid)})),N=I(()=>{const g=new Set(k.selectedOrg.permissions||[]);return k.permissions.filter(u=>g.has(u.uuid))});function B(g,u,f){L("toggleRolePermission",g,u,f)}function q(g){if(k.navigationDisabled)return;const u=J(g);if(u){if(g.preventDefault(),u==="left"||u==="right")ie(E.value,g.target,u,{itemSelector:"button"});else if(u==="up")L("navigateOut","up");else if(u==="down"){const f=c.value?.querySelector('input[type="checkbox"]');f?f.focus():Z()}}}function w(g){if(k.navigationDisabled)return;const u=J(g);if(!u)return;const f=g.target;if(f.tagName!=="INPUT")return;g.preventDefault();const r=Array.from(c.value.querySelectorAll('input[type="checkbox"]')),t=r.indexOf(f);if(t===-1)return;const s=x.value.length,D=k.selectedOrg.permissions.length,l=Math.floor(t/s),d=t%s;let h=t;if(u==="left"&&d>0)h=t-1;else if(u==="right"&&d<s-1)h=t+1;else if(u==="up"&&l>0)h=t-s;else if(u==="down"&&l<D-1)h=t+s;else if(u==="up"&&l===0){const R=E.value?.querySelector("button");R&&R.focus();return}else if(u==="down"&&l===D-1){Z();return}h!==t&&r[h]&&r[h].focus()}function F(g){if(k.navigationDisabled)return;const u=J(g);if(!u)return;const f=g.target;if(!f.classList.contains("user-chip"))return;const r=f.closest(".user-list");if(!r)return;const t=Array.from(r.querySelectorAll(".user-chip")),s=t.indexOf(f);if(s!==-1){if(u==="up"&&s>0){g.preventDefault(),t[s-1].focus();return}else if(u==="down"&&s<t.length-1){g.preventDefault(),t[s+1].focus();return}if(u==="up"&&s===0){g.preventDefault();const l=r.closest(".role-column")?.querySelector(".role-header button");l&&l.focus();return}if(!(u==="down"&&s===t.length-1)&&(u==="left"||u==="right")){g.preventDefault();const D=Array.from(y.value?.querySelectorAll(".role-column")||[]),l=r.closest(".role-column"),d=D.indexOf(l);let h=u==="left"?d-1:d+1;if(h>=0&&h<D.length){const R=D[h],j=R.querySelectorAll(".user-chip"),M=Math.min(s,j.length-1);if(j[M])j[M].focus();else{const K=R.querySelector(".plus-btn");K&&K.focus()}}else if(u==="left"&&d===0){const R=c.value?.querySelector('input[type="checkbox"]:last-of-type');R&&R.focus()}}}}function $(g,u){if(k.navigationDisabled)return;const f=J(g);if(!f)return;const r=Array.from(y.value?.querySelectorAll(".role-column")||[]);if(f==="left"||f==="right"){g.preventDefault();const t=g.currentTarget.querySelectorAll("button:not([disabled])"),s=Array.from(t).indexOf(g.target);if(f==="left"&&s>0)t[s-1].focus();else if(f==="right"&&s<t.length-1)t[s+1].focus();else if(f==="left"&&s===0&&u>0){const l=r[u-1]?.querySelectorAll(".role-header button");l?.length&&l[l.length-1].focus()}else if(f==="right"&&s===t.length-1&&u<r.length-1){const l=r[u+1]?.querySelector(".role-header button");l&&l.focus()}}else if(f==="up"){g.preventDefault();const t=c.value?.querySelectorAll('input[type="checkbox"]');if(t?.length){const s=x.value.length,l=(k.selectedOrg.permissions.length-1)*s+u;t[l]?t[l].focus():t[t.length-1].focus()}else{const s=E.value?.querySelector("button");s&&s.focus()}}else if(f==="down"){g.preventDefault();const s=r[u]?.querySelector(".user-chip");s&&s.focus()}}function _(g,u){if(k.navigationDisabled)return;const f=J(g);if(!f)return;const r=Array.from(y.value?.querySelectorAll(".role-column")||[]);if(f==="up"){g.preventDefault();const s=r[u]?.querySelector(".role-header button");s&&s.focus()}else if(f==="left"&&u>0){g.preventDefault();const t=r[u-1],s=t?.querySelector(".empty-role button"),D=t?.querySelector(".user-chip:last-child");s?s.focus():D&&D.focus()}else if(f==="right"&&u<r.length-1){g.preventDefault();const t=r[u+1],s=t?.querySelector(".empty-role button"),D=t?.querySelector(".user-chip");s?s.focus():D&&D.focus()}}function Z(){const u=y.value?.querySelector(".role-column")?.querySelector(".role-header button");u&&u.focus()}function ee(){const g=E.value?.querySelector("button");g&&g.focus()}return V({focusFirstElement:ee}),(g,u)=>(m(),p(C,null,[o("h2",{class:"org-title",ref_key:"orgTitleRef",ref:E,onKeydown:q,title:n.selectedOrg.uuid},[o("span",zt,A(n.selectedOrg.display_name),1),o("button",{onClick:u[0]||(u[0]=f=>g.$emit("updateOrg",n.selectedOrg)),class:"icon-btn","aria-label":"Rename organization",title:"Rename organization"},"✏️")],40,Bt),o("div",{class:"matrix-wrapper",ref_key:"permMatrixRef",ref:c,onKeydown:w},[o("div",Vt,[o("div",{class:"perm-matrix-grid",style:Re({gridTemplateColumns:"minmax(180px, 1fr) "+x.value.map(()=>"2.2rem").join(" ")+" 2.2rem"})},[u[5]||(u[5]=o("div",{class:"grid-head perm-head"},"Permission",-1)),(m(!0),p(C,null,X(x.value,f=>(m(),p("div",{key:"head-"+f.uuid,class:"grid-head role-head",title:f.display_name},[o("span",null,A(f.display_name),1)],8,jt))),128)),o("div",{class:"grid-head role-head add-role-head",title:"Add role",onClick:u[1]||(u[1]=f=>g.$emit("createRole",n.selectedOrg)),role:"button",tabindex:"0",onKeydown:u[2]||(u[2]=De(f=>g.$emit("createRole",n.selectedOrg),["enter"]))},"➕",32),(m(!0),p(C,null,X(N.value,f=>(m(),p(C,{key:f.uuid},[o("div",{class:"perm-name",title:f.scope},A(f.display_name),9,Ht),(m(!0),p(C,null,X(x.value,r=>(m(),p("div",{key:r.uuid+"-"+f.uuid,class:"matrix-cell"},[o("input",{type:"checkbox",checked:r.permissions.includes(f.uuid),onChange:t=>B(r,f.uuid,t.target.checked)},null,40,Gt)]))),128)),u[4]||(u[4]=o("div",{class:"matrix-cell add-role-cell"},null,-1))],64))),128))],4)]),u[6]||(u[6]=o("p",{class:"matrix-hint muted"},"Toggle which permissions each role grants.",-1))],544),o("div",{class:"roles-grid",ref_key:"rolesGridRef",ref:y},[(m(!0),p(C,null,X(x.value,(f,r)=>(m(),p("div",{key:f.uuid,class:"role-column",onDragover:u[3]||(u[3]=t=>g.$emit("onRoleDragOver",t)),onDrop:t=>g.$emit("onRoleDrop",t,n.selectedOrg,f)},[o("div",{class:"role-header",onKeydown:t=>$(t,r)},[o("strong",{class:"role-name",title:f.uuid},[o("span",null,A(f.display_name),1),o("button",{onClick:t=>g.$emit("updateRole",f),class:"icon-btn","aria-label":"Edit role",title:"Edit role"},"✏️",8,Zt)],8,Wt),o("div",Qt,[o("button",{onClick:t=>g.$emit("createUserInRole",n.selectedOrg,f),class:"plus-btn","aria-label":"Add user",title:"Add user"},"➕",8,Xt)])],40,Yt),f.users.length>0?(m(),p("ul",{key:0,class:"user-list",onKeydown:F},[(m(!0),p(C,null,X(f.users.slice().sort((t,s)=>{const D=t.display_name.toLowerCase(),l=s.display_name.toLowerCase();return D!==l?D.localeCompare(l):t.uuid.localeCompare(s.uuid)}),t=>(m(),p("li",{key:t.uuid,class:"user-chip",tabindex:"0",draggable:"true",onDragstart:s=>g.$emit("onUserDragStart",s,t,n.selectedOrg.uuid),onClick:s=>g.$emit("openUser",t),onKeydown:De(s=>g.$emit("openUser",t),["enter"]),title:t.uuid},[o("span",es,A(t.display_name),1),o("span",ts,A(t.last_seen?new Date(t.last_seen).toLocaleDateString():"—"),1)],40,_t))),128))],32)):(m(),p("div",{key:1,class:"empty-role",onKeydown:t=>_(t,r)},[u[7]||(u[7]=o("p",{class:"empty-text muted"},"No members",-1)),o("button",{onClick:t=>g.$emit("deleteRole",f),class:"icon-btn delete-icon","aria-label":"Delete empty role",title:"Delete role"},"❌",8,ns)],40,ss))],40,Jt))),128))],512)],64))}},as=re(os,[["__scopeId","data-v-f76fbbc3"]]),is={class:"user-detail"},rs={key:0,class:"error small"},ls=["disabled"],us={class:"section-block","data-section":"registered-passkeys"},ds={class:"section-body"},cs={__name:"AdminUserDetail",props:{selectedUser:Object,userDetail:Object,selectedOrg:Object,loading:Boolean,showRegModal:Boolean,navigationDisabled:{type:Boolean,default:!1}},emits:["generateUserRegistrationLink","goOverview","openOrg","onUserNameSaved","closeRegModal","editUserName","refreshUserDetail","navigateOut"],setup(n,{expose:V,emit:Y}){const k=n,L=Y,E=Ae(),c=O({}),y=O(null),x=O(null),N=O(null),B=O(null),q=O(null),w=O(null),F=O(null),$=I(()=>k.showRegModal);function _(){E.showMessage(`📋 Link copied! Send it to ${k.selectedUser.display_name}.`),L("closeRegModal")}function Z(){L("editUserName",k.selectedUser)}async function ee(l){try{const d=await P(`/auth/api/admin/orgs/${k.selectedUser.org}/users/${k.selectedUser.uuid}/credentials/${l.credential}`,{method:"DELETE"});d.status==="ok"?L("onUserNameSaved"):console.error("Failed to delete credential",d)}catch(d){console.error("Delete credential error",d)}}async function g(l){const d=l?.id;if(d){c.value={...c.value,[d]:!0};try{const h=await P(`/auth/api/admin/orgs/${k.selectedUser.org}/users/${k.selectedUser.uuid}/sessions/${d}`,{method:"DELETE"});if(h.status==="ok"){if(h.current_session_terminated){sessionStorage.clear(),location.reload();return}L("refreshUserDetail"),E.showMessage("Session terminated","success",2500)}else E.showMessage(h.detail||"Failed to terminate session","error")}catch(h){console.error("Terminate session error",h),E.showMessage(h.message||"Failed to terminate session","error")}finally{const h={...c.value};delete h[d],c.value=h}}}function u(l){if($.value||k.navigationDisabled)return;const d=J(l);d&&(l.preventDefault(),d==="left"||d==="right"?ie(N.value,l.target,d,{itemSelector:".mini-btn"}):d==="up"?L("navigateOut","up"):d==="down"&&te(B.value,{itemSelector:"button"}))}function f(l){if($.value||k.navigationDisabled)return;const d=J(l);d&&(l.preventDefault(),d==="left"||d==="right"?ie(B.value,l.target,d,{itemSelector:"button"}):d==="up"?te(N.value,{itemSelector:".mini-btn"}):d==="down"&&q.value?.$el?.focus())}function r(l){$.value||k.navigationDisabled||(l==="up"?te(B.value,{itemSelector:"button"}):l==="down"&&ke(w.value?.$el,0,{itemSelector:".session-group"}))}function t(l){if(!($.value||k.navigationDisabled)){if(l==="up")q.value?.$el?.focus();else if(l==="down"){const d=F.value?.querySelector("button");d&&d.focus()}}}function s(l){if($.value||k.navigationDisabled)return;const d=J(l);d&&(l.preventDefault(),d==="up"&&ke(w.value?.$el,-1,{itemSelector:".session-group"}))}function D(){te(N.value,{itemSelector:".mini-btn"})}return V({focusFirstElement:D}),(l,d)=>(m(),p("div",is,[o("div",{ref_key:"userInfoRef",ref:N,onKeydown:u},[n.userDetail&&!n.userDetail.error?(m(),G(rt,{key:0,name:n.userDetail.display_name||n.selectedUser.display_name,visits:n.userDetail.visits,"created-at":n.userDetail.created_at,"last-seen":n.userDetail.last_seen,loading:n.loading,"org-display-name":n.userDetail.org.display_name,"role-name":n.userDetail.role,"update-endpoint":`/auth/api/admin/orgs/${n.selectedUser.org}/users/${n.selectedUser.uuid}/display-name`,onSaved:d[0]||(d[0]=h=>l.$emit("onUserNameSaved")),onEditName:Z},null,8,["name","visits","created-at","last-seen","loading","org-display-name","role-name","update-endpoint"])):T("",!0)],544),n.userDetail?.error?(m(),p("div",rs,A(n.userDetail.error),1)):T("",!0),n.userDetail&&!n.userDetail.error?(m(),p(C,{key:1},[o("div",{class:"registration-actions",ref_key:"regActionsRef",ref:B,onKeydown:f},[o("button",{class:"btn-secondary reg-token-btn",onClick:d[1]||(d[1]=h=>l.$emit("generateUserRegistrationLink",n.selectedUser)),disabled:n.loading},"Generate Registration Token",8,ls),d[6]||(d[6]=o("p",{class:"matrix-hint muted"}," Generate a one-time registration link so this user can register or add another passkey. Copy the link from the dialog and send it to the user, or have the user scan the QR code on their device. ",-1))],544),o("section",us,[d[7]||(d[7]=o("div",{class:"section-header"},[o("h2",null,"Registered Passkeys")],-1)),o("div",ds,[ae(lt,{ref_key:"credentialListRef",ref:q,credentials:n.userDetail.credentials,"aaguid-info":n.userDetail.aaguid_info,"allow-delete":!0,"hovered-credential-uuid":y.value,"hovered-session-credential-uuid":x.value?.credential,"navigation-disabled":$.value,onDelete:ee,onCredentialHover:d[2]||(d[2]=h=>y.value=h),onNavigateOut:r},null,8,["credentials","aaguid-info","hovered-credential-uuid","hovered-session-credential-uuid","navigation-disabled"])])]),ae(ut,{ref_key:"sessionListRef",ref:w,sessions:n.userDetail.sessions||[],"terminating-sessions":c.value,"hovered-credential-uuid":y.value,"navigation-disabled":$.value,"empty-message":"This user has no active sessions.","section-description":"View and manage the active sessions for this user.",onTerminate:g,onSessionHover:d[3]||(d[3]=h=>x.value=h),onNavigateOut:t},null,8,["sessions","terminating-sessions","hovered-credential-uuid","navigation-disabled"])],64)):T("",!0),o("div",{class:"actions ancillary-actions",ref_key:"backButtonRef",ref:F,onKeydown:s},[n.selectedOrg?(m(),p("button",{key:0,onClick:d[4]||(d[4]=h=>l.$emit("openOrg",n.selectedOrg)),class:"icon-btn",title:"Back to Org"},"↩️")):T("",!0)],544),n.showRegModal?(m(),G(dt,{key:2,endpoint:`/auth/api/admin/orgs/${n.selectedUser.org}/users/${n.selectedUser.uuid}/create-link`,"user-name":n.userDetail?.display_name||n.selectedUser.display_name,onClose:d[5]||(d[5]=h=>l.$emit("closeRegModal")),onCopied:_},null,8,["endpoint","user-name"])):T("",!0)]))}},ms=re(cs,[["__scopeId","data-v-738bccea"]]),fs={class:"modal-title"},gs={key:0},vs={key:2},ys={class:"small muted"},ps=["placeholder","pattern"],hs={class:"small muted"},bs={key:7},ws={key:8,class:"error small"},$s={key:9,class:"modal-actions"},Ds=["disabled"],ks=["disabled"],Ss={__name:"AdminDialogs",props:{dialog:Object,PERMISSION_ID_PATTERN:String,settings:Object},emits:["submitDialog","closeDialog"],setup(n,{emit:V}){const Y=n,k=new Set(["org-update","role-update","user-update-name"]),L=I(()=>Y.settings?.rp_id||"the configured domain");return(E,c)=>n.dialog.type?(m(),G(ct,{key:0,onClose:c[14]||(c[14]=y=>E.$emit("closeDialog"))},{default:_e(()=>[o("h3",fs,[n.dialog.type==="org-create"?(m(),p(C,{key:0},[z("Create Organization")],64)):n.dialog.type==="org-update"?(m(),p(C,{key:1},[z("Rename Organization")],64)):n.dialog.type==="role-create"?(m(),p(C,{key:2},[z("Create Role")],64)):n.dialog.type==="role-update"?(m(),p(C,{key:3},[z("Edit Role")],64)):n.dialog.type==="user-create"?(m(),p(C,{key:4},[z("Add User To Role")],64)):n.dialog.type==="user-update-name"?(m(),p(C,{key:5},[z("Edit User Name")],64)):n.dialog.type==="perm-create"||n.dialog.type==="perm-display"?(m(),p(C,{key:6},[z(A(n.dialog.type==="perm-create"?"Create Permission":"Edit Permission"),1)],64)):n.dialog.type==="confirm"?(m(),p(C,{key:7},[z("Confirm")],64)):T("",!0)]),o("form",{onSubmit:c[13]||(c[13]=Ce(y=>E.$emit("submitDialog"),["prevent"])),class:"modal-form"},[n.dialog.type==="org-create"?(m(),p("label",gs,[c[15]||(c[15]=z("Name ",-1)),ne(o("input",{ref:"nameInput","onUpdate:modelValue":c[0]||(c[0]=y=>n.dialog.data.name=y),required:""},null,512),[[oe,n.dialog.data.name]])])):n.dialog.type==="org-update"?(m(),G(me,{key:1,label:"Organization Name",modelValue:n.dialog.data.name,"onUpdate:modelValue":c[1]||(c[1]=y=>n.dialog.data.name=y),busy:n.dialog.busy,error:n.dialog.error,onCancel:c[2]||(c[2]=y=>E.$emit("closeDialog"))},null,8,["modelValue","busy","error"])):n.dialog.type==="role-create"?(m(),p("label",vs,[c[16]||(c[16]=z("Role Name ",-1)),ne(o("input",{"onUpdate:modelValue":c[3]||(c[3]=y=>n.dialog.data.name=y),placeholder:"Role name",required:""},null,512),[[oe,n.dialog.data.name]])])):n.dialog.type==="role-update"?(m(),G(me,{key:3,label:"Role Name",modelValue:n.dialog.data.name,"onUpdate:modelValue":c[4]||(c[4]=y=>n.dialog.data.name=y),busy:n.dialog.busy,error:n.dialog.error,onCancel:c[5]||(c[5]=y=>E.$emit("closeDialog"))},null,8,["modelValue","busy","error"])):n.dialog.type==="user-create"?(m(),p(C,{key:4},[o("p",ys,"Role: "+A(n.dialog.data.role.display_name),1),o("label",null,[c[17]||(c[17]=z("Display Name ",-1)),ne(o("input",{"onUpdate:modelValue":c[6]||(c[6]=y=>n.dialog.data.name=y),placeholder:"User display name",required:""},null,512),[[oe,n.dialog.data.name]])])],64)):n.dialog.type==="user-update-name"?(m(),G(me,{key:5,label:"Display Name",modelValue:n.dialog.data.name,"onUpdate:modelValue":c[7]||(c[7]=y=>n.dialog.data.name=y),busy:n.dialog.busy,error:n.dialog.error,onCancel:c[8]||(c[8]=y=>E.$emit("closeDialog"))},null,8,["modelValue","busy","error"])):n.dialog.type==="perm-create"||n.dialog.type==="perm-display"?(m(),p(C,{key:6},[o("label",null,[c[18]||(c[18]=z("Display Name ",-1)),ne(o("input",{ref:"displayNameInput","onUpdate:modelValue":c[9]||(c[9]=y=>n.dialog.data.display_name=y),required:""},null,512),[[oe,n.dialog.data.display_name]])]),o("label",null,[c[19]||(c[19]=z("Permission Scope ",-1)),ne(o("input",{"onUpdate:modelValue":c[10]||(c[10]=y=>n.dialog.data.scope=y),placeholder:n.dialog.type==="perm-create"?"yourapp:permission":n.dialog.data.permission.scope,required:"",pattern:n.PERMISSION_ID_PATTERN,title:"Allowed: A-Za-z0-9:._~-","data-form-type":"other"},null,8,ps),[[oe,n.dialog.data.scope]])]),c[21]||(c[21]=o("p",{class:"small muted"},"E.g. yourapp:reports. Changing the scope name may break deployed applications.",-1)),o("label",null,[c[20]||(c[20]=z("Domain Scope ",-1)),ne(o("input",{"onUpdate:modelValue":c[11]||(c[11]=y=>n.dialog.data.domain=y),placeholder:"e.g. app.example.com","data-form-type":"other"},null,512),[[oe,n.dialog.data.domain]])]),o("p",hs,"If set, this permission is effective only on the specified domain, which can be "+A(L.value)+" or its subdomain.",1)],64)):n.dialog.type==="confirm"?(m(),p("p",bs,A(n.dialog.data.message),1)):T("",!0),n.dialog.error&&!ge(k).has(n.dialog.type)?(m(),p("div",ws,A(n.dialog.error),1)):T("",!0),ge(k).has(n.dialog.type)?T("",!0):(m(),p("div",$s,[o("button",{type:"button",class:"btn-secondary",onClick:c[12]||(c[12]=y=>E.$emit("closeDialog")),disabled:n.dialog.busy}," Cancel ",8,Ds),o("button",{type:"submit",class:"btn-primary",disabled:n.dialog.busy},A(n.dialog.type==="confirm"?"OK":"Save"),9,ks)]))],32)]),_:1})):T("",!0)}},Os=re(Ss,[["__scopeId","data-v-8d16c6cc"]]),Rs={class:"app-shell admin-shell"},Cs={class:"app-main"},As={key:4,class:"view-root view-root--wide view-admin"},Us={class:"view-header"},Es={class:"section-block admin-section"},Ms={class:"section-body admin-section-body"},Ps={class:"admin-panels"},xs="^[A-Za-z0-9:._~-]+$",qs={__name:"AdminApp",setup(n){const V=O(null),Y=O(!0),k=O("Loading..."),L=O(!1),E=O(!1),c=O(null),y=O([]),x=O([]),N=O(null),B=O(null),q=O(null),w=Ae(),F=O(null),$=O({type:null,data:null,busy:!1,error:""}),_=O(null),Z=O(null),ee=O(null),g=O(null),u=O(null),f=I(()=>$.value.type!==null||ue.value),r=I(()=>V.value?.ctx.permissions.includes("auth:admin")),t=I(()=>V.value?.ctx.permissions.includes("auth:org:admin"));function s(e){if(!F.value)return;const a=e.target.closest(".org-add-menu"),i=e.target.closest(".add-org-btn");!a&&!i&&(F.value=null)}Se(async()=>{document.addEventListener("click",s),window.addEventListener("hashchange",d),await w.loadSettings(),w.settings?.rp_name&&(document.title=w.settings.rp_name+" Admin"),await Ee()}),Oe(()=>{document.removeEventListener("click",s),window.removeEventListener("hashchange",d)});const D=I(()=>{const e={};for(const i of y.value){const v={uuid:i.uuid,display_name:i.display_name},b=new Set(i.permissions||[]);for(const S of i.permissions||[])e[S]||(e[S]={orgs:[],orgSet:new Set,userCount:0}),e[S].orgSet.has(i.uuid)||(e[S].orgs.push(v),e[S].orgSet.add(i.uuid));for(const S of i.roles)for(const U of S.permissions)b.has(U)&&(e[U]||(e[U]={orgs:[],orgSet:new Set,userCount:0}),e[U].orgSet.has(i.uuid)||(e[U].orgs.push(v),e[U].orgSet.add(i.uuid)),e[U].userCount+=S.users.length)}const a={};for(const[i,v]of Object.entries(e))a[i]={orgs:v.orgs.sort((b,S)=>b.display_name.localeCompare(S.display_name)),userCount:v.userCount};return a});function l(e){Q("perm-display",{permission:e,scope:e.scope,display_name:e.display_name,domain:e.domain||""})}function d(){const e=window.location.hash||"";N.value=null,B.value=null,e.startsWith("#org/")?N.value=e.slice(5):e.startsWith("#user/")&&(B.value=e.slice(6))}async function h(){const e=await P("/auth/api/admin/orgs");y.value=e.map(a=>{const i=a.roles.map(b=>({...b,org:a.uuid,users:[]})),v=Object.fromEntries(i.map(b=>[b.display_name,b]));for(const b of a.users||[])v[b.role]&&v[b.role].users.push(b);return{...a,roles:i}})}async function R(){x.value=await P("/auth/api/admin/permissions")}async function j(){const e=await P("/auth/api/validate",{method:"POST"});V.value=e,L.value=!0}function M(){V.value=null,y.value=[],x.value=[],q.value=null,L.value=!1}function K(e){M(),e.name==="AuthCancelledError"?E.value=!0:c.value=e.message}const le=()=>V.value?.ctx.user.uuid,ve=new et(le,K);Se(()=>ve.start()),Oe(()=>ve.stop());async function Ee(){Y.value=!0,k.value="Loading...",c.value=null;try{await Promise.all([h(),R()]),await j(),!r.value&&t.value&&y.value.length===1&&(!window.location.hash||window.location.hash==="#overview")?(N.value=y.value[0].uuid,window.location.hash=`#org/${N.value}`,w.showMessage(`Navigating to ${y.value[0].display_name} Administration`,"info",3e3)):d()}catch(e){K(e)}finally{Y.value=!1}}function Me(){Q("org-create",{})}function ye(e){Q("org-update",{org:e,name:e.display_name})}function Pe(e){Q("user-update-name",{user:e,name:e.display_name})}async function pe(e){await P(`/auth/api/admin/orgs/${e}`,{method:"DELETE"}),await Promise.all([h(),R()])}function xe(e){if(e.roles.reduce((b,S)=>b+S.users.length,0)===0){pe(e.uuid).then(()=>{w.showMessage(`Organization "${e.display_name}" deleted.`,"success",2500)}).catch(b=>{w.showMessage(b.message||"Failed to delete organization","error")});return}const v=e.roles.filter(b=>b.users.length>0).map(b=>`${b.users.length} ${b.display_name}`).join(", ");Q("confirm",{message:`Delete organization "${e.display_name}", including accounts of ${v})?`,action:async()=>{await pe(e.uuid)}})}function qe(e,a){Q("user-create",{org:e,role:a})}async function Ne(e,a,i){if(a.role!==i)try{await P(`/auth/api/admin/orgs/${e.uuid}/users/${a.uuid}/role`,{method:"PATCH",body:{role:i}}),await h()}catch(v){w.showMessage(v.message||"Failed to update user role")}}function Te(e,a,i){e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",JSON.stringify({user_uuid:a.uuid,org:i}))}function Le(e){e.preventDefault(),e.dataTransfer.dropEffect="move"}function Fe(e,a,i){e.preventDefault();try{const v=JSON.parse(e.dataTransfer.getData("text/plain"));if(v.org!==a.uuid)return;const b=a.roles.flatMap(S=>S.users).find(S=>S.uuid===v.user_uuid);b&&Ne(a,b,i.display_name)}catch{}}function Ie(e){Q("role-create",{org:e})}function Ke(e){Q("role-update",{role:e,name:e.display_name})}function Be(e){P(`/auth/api/admin/orgs/${e.org}/roles/${e.uuid}`,{method:"DELETE"}).then(()=>{w.showMessage(`Role "${e.display_name}" deleted.`,"success",2500),h()}).catch(a=>{w.showMessage(a.message||"Failed to delete role","error")})}async function ze(e,a,i){const v=[...e.permissions],b=i?[...e.permissions,a]:e.permissions.filter(S=>S!==a);e.permissions=b;try{const S=i?"POST":"DELETE";await P(`/auth/api/admin/orgs/${e.org}/roles/${e.uuid}/permissions/${a}`,{method:S}),await h()}catch(S){w.showMessage(S.message||"Failed to update role permission"),e.permissions=v}}async function he(e){const a=new URLSearchParams({permission_uuid:e});await P(`/auth/api/admin/permission?${a.toString()}`,{method:"DELETE"}),await R()}function Ve(e){const a=D.value[e.uuid]?.userCount||0;let i=0;for(const S of y.value)for(const U of S.roles)U.permissions.includes(e.uuid)&&i++;if(i===0){he(e.uuid).then(()=>{w.showMessage(`Permission "${e.display_name}" deleted.`,"success",2500)}).catch(S=>{w.showMessage(S.message||"Failed to delete permission","error")});return}const v=[];i>0&&v.push(`${i} role${i!==1?"s":""}`),a>0&&v.push(`${a} user${a!==1?"s":""}`);const b=v.join(", ");Q("confirm",{message:`Delete permission "${e.display_name}" (${b})?`,action:async()=>{await he(e.uuid)}})}const se=I(()=>y.value.find(e=>e.uuid===N.value)||null);function be(e){window.location.hash=`#org/${e.uuid}`}function je(){window.location.hash="#overview"}function He(e){window.location.hash=`#user/${e.uuid}`}const H=I(()=>{if(!B.value)return null;for(const e of y.value)for(const a of e.roles){const i=a.users.find(v=>v.uuid===B.value);if(i)return{...i,org:e.uuid,role_display_name:a.display_name}}return null}),Ge=I(()=>H.value?"Admin: User":se.value?"Admin: Org":(w.settings?.rp_name||"Master")+" Admin"),Je=I(()=>{const e=[{label:"Auth",href:st()},{label:"Admin",href:nt()}];let a=null;H.value&&(a=y.value.find(v=>v.uuid===H.value.org)||null);const i=se.value||a;return i&&e.push({label:i.display_name,href:`#org/${i.uuid}`}),H.value&&e.push({label:H.value.display_name,href:`#user/${H.value.uuid}`}),e});tt(H,async e=>{if(!e){q.value=null;return}try{q.value=await P(`/auth/api/admin/orgs/${e.org}/users/${e.uuid}`)}catch(a){q.value={error:a.message}}});const ue=O(!1);function Ye(e){ue.value=!0}async function We(e,a,i){const v=e.permissions.includes(a);if(i&&v||!i&&!v)return;const b=i?[...e.permissions,a]:e.permissions.filter(U=>U!==a),S=[...e.permissions];e.permissions=b;try{const U=new URLSearchParams({permission_uuid:a});await P(`/auth/api/admin/orgs/${e.uuid}/permission?${U.toString()}`,{method:i?"POST":"DELETE"}),await h()}catch(U){w.showMessage(U.message||"Failed to update organization permission","error"),e.permissions=S}}function Q(e,a){const i=document.activeElement;if(_.value=i,e==="confirm"&&i){const v=i.closest("tr");if(v){const b=v.closest("tbody");if(b){const S=Array.from(b.querySelectorAll("tr")),U=S.indexOf(v);$.value.focusContext={tbody:b,index:U,total:S.length,selector:"button:not([disabled]), a"}}}}$.value={...$.value,type:e,data:a,busy:!1,error:""}}function W(){const e=_.value,a=$.value.focusContext;$.value={type:null,data:null,busy:!1,error:""},Ze(e,a),_.value=null}function Ze(e,a){if(!e)return;if(document.body.contains(e)&&!e.disabled){e.focus();return}if(a?.tbody&&a.selector){const b=Array.from(a.tbody.querySelectorAll("tr"));if(b.length>0){const S=Math.min(a.index,b.length-1),de=b[S]?.querySelector(a.selector);if(de){de.focus();return}}}const i=document.querySelector(".admin-panels");if(!i)return;const v=i.querySelector('button:not([disabled]), a, input:not([disabled]), [tabindex="0"]');v&&v.focus()}function Qe(e){if(f.value)return;const a=J(e);a&&a==="down"&&(e.preventDefault(),ee.value?ee.value.focusFirstElement?.():g.value?g.value.focusFirstElement?.():u.value&&u.value.focusFirstElement?.())}function ce(e){f.value||e==="up"&&Z.value?.focusCurrent?.()}async function we(){if(await h(),H.value)try{q.value=await P(`/auth/api/admin/orgs/${H.value.org}/users/${H.value.uuid}`)}catch(e){w.showMessage(e.message||"Failed to reload user","error")}}async function $e(){await we(),w.showMessage("User renamed","success",1500)}async function Xe(){if(!(!$.value.type||$.value.busy)){$.value.busy=!0,$.value.error="";try{const e=$.value.type;if(e==="org-create"){const a=$.value.data.name?.trim();if(!a)throw new Error("Name required");W(),P("/auth/api/admin/orgs",{method:"POST",body:{display_name:a,permissions:[]}}).then(()=>{w.showMessage(`Organization "${a}" created.`,"success",2500),Promise.all([h(),R()])}).catch(i=>{w.showMessage(i.message||"Failed to create organization","error")});return}else if(e==="org-update"){const{org:a}=$.value.data,i=$.value.data.name?.trim();if(!i)throw new Error("Name required");W(),P(`/auth/api/admin/orgs/${a.uuid}`,{method:"PATCH",body:{display_name:i}}).then(()=>{w.showMessage(`Organization renamed to "${i}".`,"success",2500),h()}).catch(v=>{w.showMessage(v.message||"Failed to update organization","error")});return}else if(e==="role-create"){const{org:a}=$.value.data,i=$.value.data.name?.trim();if(!i)throw new Error("Name required");W(),P(`/auth/api/admin/orgs/${a.uuid}/roles`,{method:"POST",body:{display_name:i,permissions:[]}}).then(()=>{w.showMessage(`Role "${i}" created.`,"success",2500),h()}).catch(v=>{w.showMessage(v.message||"Failed to create role","error")});return}else if(e==="role-update"){const{role:a}=$.value.data,i=$.value.data.name?.trim();if(!i)throw new Error("Name required");W(),P(`/auth/api/admin/orgs/${a.org}/roles/${a.uuid}`,{method:"PATCH",body:{display_name:i}}).then(()=>{w.showMessage(`Role renamed to "${i}".`,"success",2500),h()}).catch(v=>{w.showMessage(v.message||"Failed to update role","error")});return}else if(e==="user-create"){const{org:a,role:i}=$.value.data,v=$.value.data.name?.trim();if(!v)throw new Error("Name required");W(),P(`/auth/api/admin/orgs/${a.uuid}/users`,{method:"POST",body:{display_name:v,role:i.display_name}}).then(()=>{w.showMessage(`User "${v}" added to ${i.display_name} role.`,"success",2500),h()}).catch(b=>{w.showMessage(b.message||"Failed to add user","error")});return}else if(e==="user-update-name"){const{user:a}=$.value.data,i=$.value.data.name?.trim();if(!i)throw new Error("Name required");W(),P(`/auth/api/admin/orgs/${a.org}/users/${a.uuid}/display-name`,{method:"PATCH",body:{display_name:i}}).then(()=>{w.showMessage(`User renamed to "${i}".`,"success",2500),$e()}).catch(v=>{w.showMessage(v.message||"Failed to update user name","error")});return}else if(e==="perm-display"){const{permission:a}=$.value.data,i=$.value.data.scope?.trim(),v=$.value.data.display_name?.trim(),b=$.value.data.domain?.trim()||"";if(!v)throw new Error("Display name required");if(!i)throw new Error("Scope required");W();const S=a.domain||"";if(i===a.scope&&v===a.display_name&&b===S)return;const U=new URLSearchParams({permission_uuid:a.uuid});i!==a.scope&&U.set("scope",i),v!==a.display_name&&U.set("display_name",v),b!==S&&U.set("domain",b||""),P(`/auth/api/admin/permission?${U.toString()}`,{method:"PATCH"}).then(()=>{w.showMessage(`Permission "${v}" updated.`,"success",2500),R()}).catch(de=>{w.showMessage(de.message||"Failed to update permission","error")});return}else if(e==="perm-create"){const a=$.value.data.scope?.trim();if(!a)throw new Error("Scope required");const i=$.value.data.display_name?.trim();if(!i)throw new Error("Display name required");const v=$.value.data.domain?.trim()||"";W(),P("/auth/api/admin/permissions",{method:"POST",body:{scope:a,display_name:i,domain:v||void 0}}).then(()=>{w.showMessage(`Permission "${i}" created.`,"success",2500),R()}).catch(b=>{w.showMessage(b.message||"Failed to create permission","error")});return}else if(e==="confirm"){const a=$.value.data.action;if(W(),a)try{await a()}catch(i){w.showMessage(i.message||"Action failed","error")}return}W()}catch(e){$.value.error=e.message||"Error"}finally{$.value.busy=!1}}}return(e,a)=>(m(),p("div",Rs,[ae(mt),o("main",Cs,[Y.value?(m(),G(ft,{key:0,message:k.value},null,8,["message"])):E.value?(m(),G(fe,{key:1})):c.value?(m(),G(fe,{key:2,icon:"⚠️",title:"Error",message:c.value},null,8,["message"])):L.value&&!r.value&&!t.value?(m(),G(fe,{key:3,icon:"⛔",message:"You do not have admin permissions for this application."})):L.value&&(r.value||t.value)?(m(),p("section",As,[o("header",Us,[o("h1",null,A(Ge.value),1),ae(gt,{ref_key:"breadcrumbsRef",ref:Z,entries:Je.value,onKeydown:Qe},null,8,["entries"])]),o("section",Es,[o("div",Ms,[o("div",Ps,[!H.value&&!se.value&&(r.value||t.value)?(m(),G(Kt,{key:0,ref_key:"adminOverviewRef",ref:ee,info:V.value,orgs:y.value,permissions:x.value,"navigation-disabled":f.value,"permission-summary":D.value,onCreateOrg:Me,onOpenOrg:be,onUpdateOrg:ye,onDeleteOrg:xe,onToggleOrgPermission:We,onOpenDialog:Q,onDeletePermission:Ve,onRenamePermissionDisplay:l,onNavigateOut:ce},null,8,["info","orgs","permissions","navigation-disabled","permission-summary"])):H.value?(m(),G(ms,{key:1,ref_key:"adminUserDetailRef",ref:u,"selected-user":H.value,"user-detail":q.value,"selected-org":se.value,loading:Y.value,"show-reg-modal":ue.value,"navigation-disabled":f.value,onGenerateUserRegistrationLink:Ye,onGoOverview:je,onOpenOrg:be,onOnUserNameSaved:$e,onRefreshUserDetail:we,onEditUserName:Pe,onCloseRegModal:a[0]||(a[0]=i=>ue.value=!1),onNavigateOut:ce},null,8,["selected-user","user-detail","selected-org","loading","show-reg-modal","navigation-disabled"])):se.value?(m(),G(as,{key:2,ref_key:"adminOrgDetailRef",ref:g,"selected-org":se.value,permissions:x.value,"navigation-disabled":f.value,onUpdateOrg:ye,onCreateRole:Ie,onUpdateRole:Ke,onDeleteRole:Be,onCreateUserInRole:qe,onOpenUser:He,onToggleRolePermission:ze,onOnRoleDragOver:Le,onNavigateOut:ce,onOnRoleDrop:Fe,onOnUserDragStart:Te},null,8,["selected-org","permissions","navigation-disabled"])):T("",!0)])])])])):T("",!0)]),ae(Os,{dialog:$.value,"permission-id-pattern":xs,settings:ge(w).settings,onSubmitDialog:Xe,onCloseDialog:W},null,8,["dialog","settings"])]))}},Ns=re(qs,[["__scopeId","data-v-6d820553"]]);it();const Ue=ot(Ns);Ue.use(vt());Ue.mount("#admin-app");at();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.pairing-entry[data-v-77a073d9]{display:flex;flex-direction:column;gap:1rem}.pairing-form[data-v-77a073d9]{display:flex;flex-direction:column;gap:.5rem}.input-row[data-v-77a073d9]{display:flex;align-items:center;gap:.5rem}.input-wrapper[data-v-77a073d9]{position:relative;display:flex;width:280px;max-width:100%}.slot-machine[data-v-77a073d9]{position:absolute;left:0;top:0;width:100%;height:100%;gap:0;box-sizing:border-box;z-index:1;pointer-events:none}.input-wrapper.focused.has-error .slot-machine[data-v-77a073d9]{background:var(--color-error-bg, rgba(239, 68, 68, .05))}.slot-reel[data-v-77a073d9]{flex:1 1 33.333%;overflow:visible}.slot-reel[data-v-77a073d9]:not(:last-child){margin-right:.5rem}.slot-word[data-v-77a073d9]{font-weight:600;letter-spacing:.05em;text-align:center;width:100%;color:var(--color-text);display:flex;align-items:center;justify-content:center;position:relative}.slot-word .typed-prefix[data-v-77a073d9]{color:var(--color-text)}.slot-word .hint-suffix[data-v-77a073d9]{color:var(--color-text-muted);opacity:.6}.cursor-overlay[data-v-77a073d9]{position:absolute;width:2px;height:1.2em;background:var(--color-text);animation:none;pointer-events:none;left:calc(50% + (var(--cursor-pos) - var(--word-len, 0) / 2) * .65em);transform:translate(-1px);opacity:0}.input-wrapper.focused .cursor-overlay[data-v-77a073d9]{opacity:1;animation:cursorBlink-77a073d9 .25s alternate infinite}.input-wrapper.focused.has-selection .cursor-overlay[data-v-77a073d9]{animation:none}.selection-overlay[data-v-77a073d9]{position:absolute;height:1.2em;background:var(--color-primary, #3b82f6);opacity:.3;pointer-events:none;left:calc(50% + (var(--sel-start) - var(--word-len, 0) / 2) * .65em);width:calc((var(--sel-end) - var(--sel-start)) * .65em)}@keyframes cursorBlink-77a073d9{0%,50%{opacity:1}80%,to{opacity:0}}.slot-reel.invalid-word .slot-word[data-v-77a073d9],.slot-reel.invalid-word .slot-word .typed-prefix[data-v-77a073d9]{color:var(--color-error, #ef4444)}.slot-reel.invalid-word .cursor-overlay[data-v-77a073d9]{background:var(--color-error, #ef4444)}.slot-reel.empty .slot-word[data-v-77a073d9]{color:var(--color-text-muted)}.pairing-input[data-v-77a073d9]{flex:1;width:100%;height:100%;border-radius:var(--radius-sm, 6px);position:relative;z-index:0}.pairing-input.hidden-input[data-v-77a073d9]{opacity:0}.pairing-input[data-v-77a073d9]:disabled{cursor:not-allowed}.pairing-input[data-v-77a073d9]::placeholder{color:transparent}.processing-status[data-v-77a073d9]{display:flex;align-items:center;gap:.25rem;font-size:.875rem;color:var(--color-text-muted)}.processing-icon[data-v-77a073d9]{font-size:.875rem}.processing-spinner-small[data-v-77a073d9]{width:12px;height:12px;border:2px solid var(--color-border);border-top-color:var(--color-primary);border-radius:50%;animation:spin-77a073d9 .8s linear infinite}@keyframes spin-77a073d9{to{transform:rotate(360deg)}}.device-info[data-v-77a073d9]{display:flex;flex-direction:column;gap:.5rem}.device-permit-text[data-v-77a073d9]{margin:0;font-size:.95rem;color:var(--color-text)}.device-meta[data-v-77a073d9]{margin:0;font-size:.8rem;color:var(--color-text-muted);font-family:SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Courier New,monospace}.error-message[data-v-77a073d9]{margin:0;font-size:.875rem;color:var(--color-error, #ef4444);margin-bottom:1rem}.view-lede[data-v-
|
|
1
|
+
.pairing-entry[data-v-77a073d9]{display:flex;flex-direction:column;gap:1rem}.pairing-form[data-v-77a073d9]{display:flex;flex-direction:column;gap:.5rem}.input-row[data-v-77a073d9]{display:flex;align-items:center;gap:.5rem}.input-wrapper[data-v-77a073d9]{position:relative;display:flex;width:280px;max-width:100%}.slot-machine[data-v-77a073d9]{position:absolute;left:0;top:0;width:100%;height:100%;gap:0;box-sizing:border-box;z-index:1;pointer-events:none}.input-wrapper.focused.has-error .slot-machine[data-v-77a073d9]{background:var(--color-error-bg, rgba(239, 68, 68, .05))}.slot-reel[data-v-77a073d9]{flex:1 1 33.333%;overflow:visible}.slot-reel[data-v-77a073d9]:not(:last-child){margin-right:.5rem}.slot-word[data-v-77a073d9]{font-weight:600;letter-spacing:.05em;text-align:center;width:100%;color:var(--color-text);display:flex;align-items:center;justify-content:center;position:relative}.slot-word .typed-prefix[data-v-77a073d9]{color:var(--color-text)}.slot-word .hint-suffix[data-v-77a073d9]{color:var(--color-text-muted);opacity:.6}.cursor-overlay[data-v-77a073d9]{position:absolute;width:2px;height:1.2em;background:var(--color-text);animation:none;pointer-events:none;left:calc(50% + (var(--cursor-pos) - var(--word-len, 0) / 2) * .65em);transform:translate(-1px);opacity:0}.input-wrapper.focused .cursor-overlay[data-v-77a073d9]{opacity:1;animation:cursorBlink-77a073d9 .25s alternate infinite}.input-wrapper.focused.has-selection .cursor-overlay[data-v-77a073d9]{animation:none}.selection-overlay[data-v-77a073d9]{position:absolute;height:1.2em;background:var(--color-primary, #3b82f6);opacity:.3;pointer-events:none;left:calc(50% + (var(--sel-start) - var(--word-len, 0) / 2) * .65em);width:calc((var(--sel-end) - var(--sel-start)) * .65em)}@keyframes cursorBlink-77a073d9{0%,50%{opacity:1}80%,to{opacity:0}}.slot-reel.invalid-word .slot-word[data-v-77a073d9],.slot-reel.invalid-word .slot-word .typed-prefix[data-v-77a073d9]{color:var(--color-error, #ef4444)}.slot-reel.invalid-word .cursor-overlay[data-v-77a073d9]{background:var(--color-error, #ef4444)}.slot-reel.empty .slot-word[data-v-77a073d9]{color:var(--color-text-muted)}.pairing-input[data-v-77a073d9]{flex:1;width:100%;height:100%;border-radius:var(--radius-sm, 6px);position:relative;z-index:0}.pairing-input.hidden-input[data-v-77a073d9]{opacity:0}.pairing-input[data-v-77a073d9]:disabled{cursor:not-allowed}.pairing-input[data-v-77a073d9]::placeholder{color:transparent}.processing-status[data-v-77a073d9]{display:flex;align-items:center;gap:.25rem;font-size:.875rem;color:var(--color-text-muted)}.processing-icon[data-v-77a073d9]{font-size:.875rem}.processing-spinner-small[data-v-77a073d9]{width:12px;height:12px;border:2px solid var(--color-border);border-top-color:var(--color-primary);border-radius:50%;animation:spin-77a073d9 .8s linear infinite}@keyframes spin-77a073d9{to{transform:rotate(360deg)}}.device-info[data-v-77a073d9]{display:flex;flex-direction:column;gap:.5rem}.device-permit-text[data-v-77a073d9]{margin:0;font-size:.95rem;color:var(--color-text)}.device-meta[data-v-77a073d9]{margin:0;font-size:.8rem;color:var(--color-text-muted);font-family:SF Mono,Monaco,Cascadia Code,Roboto Mono,Consolas,Courier New,monospace}.error-message[data-v-77a073d9]{margin:0;font-size:.875rem;color:var(--color-error, #ef4444);margin-bottom:1rem}.view-lede[data-v-2cc35e77]{margin:0;color:var(--color-text-muted);font-size:1rem}.section-header[data-v-2cc35e77]{display:flex;flex-direction:column;gap:.4rem}.empty-state[data-v-2cc35e77]{margin:0;color:var(--color-text-muted);text-align:center;padding:1rem 0}.logout-note[data-v-2cc35e77]{margin:.75rem 0 0;color:var(--color-text-muted);font-size:.875rem}.remote-auth-inline[data-v-2cc35e77]{display:flex;flex-direction:column;gap:.5rem}.remote-auth-label[data-v-2cc35e77]{display:block;margin:0;font-size:.875rem;color:var(--color-text-muted);font-weight:500}.remote-auth-description[data-v-2cc35e77]{font-size:.75rem;color:var(--color-text-muted)}.theme-toggle[data-v-2cc35e77]{position:absolute;top:var(--layout-padding);right:var(--layout-padding)}.theme-btn[data-v-2cc35e77]{background:none;border:none;padding:.25rem;font-size:1.25rem;cursor:pointer;opacity:.5;transition:opacity .15s}.theme-btn[data-v-2cc35e77]:hover{opacity:.8}.theme-menu[data-v-2cc35e77]{position:absolute;top:100%;right:0;width:5rem;height:4rem;margin-top:.25rem}.theme-option[data-v-2cc35e77]{position:absolute;background:none;border:none;font-size:1.25rem;cursor:pointer;opacity:.5;padding:.25rem;border-radius:var(--radius-sm);transition:opacity .15s,transform .15s}.theme-option[data-v-2cc35e77]:hover{opacity:1;transform:scale(1.2)}.theme-option.active[data-v-2cc35e77]{opacity:1}.theme-option.top[data-v-2cc35e77]{top:0;left:50%;transform:translate(-50%)}.theme-option.top[data-v-2cc35e77]:hover{transform:translate(-50%) scale(1.2)}.theme-option.left[data-v-2cc35e77]{bottom:0;left:0}.theme-option.right[data-v-2cc35e77]{bottom:0;right:0}.host-view[data-v-237999a2]{padding:3rem 1.5rem 4rem}.host-actions[data-v-237999a2]{display:flex;flex-direction:column;gap:.75rem}.host-actions .button-row[data-v-237999a2]{gap:.75rem;flex-wrap:wrap}.host-actions .button-row button[data-v-237999a2]{flex:1 1 0}.note[data-v-237999a2],.empty-state[data-v-237999a2]{margin:0;color:var(--color-text-muted)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as Ke,w as He,o as Se,n as Ie,a as $e,b as f,c as m,d as a,e as ie,F as he,r as it,f as Ne,g as B,t as W,h as F,i as ut,v as ct,j as pe,k as c,l as dt,m as A,s as ft,p as vt,q as re,u as k,x as Q,y as Ye,z as Re,A as ge,B as Te,C as ht,D as pt,E as Ce,G as Ue,H as gt,N as mt,O as xe,I as yt,J as wt,K as bt}from"./_plugin-vue_export-helper-DJsHCwvl.js";import{u as kt,i as _t}from"./theme-C2WysaSw.js";import{u as Ae,B as It,U as et,_ as xt,a as Ct,N as St,M as $t,R as Lt,L as At,b as Wt,A as Pt,c as Mt}from"./AccessDenied-Licr0tqA.js";import{g as Ze,i as te,a as Ge,b as Je,c as Qe,s as Xe}from"./pow-DUr-T9XX.js";import{g as Le}from"./helpers-DzjFIx78.js";const Ve={};async function Et(T="login"){if(Ve[T])return Ve[T];const o=await fetch("/auth/api/forward");if(o.status===401||o.status===403){const $=await o.json();if($.auth?.iframe){let _=$.auth.iframe;return T!==$.auth.mode&&(_=_.replace(/mode=[^&]*/,`mode=${T}`)),Ve[T]=_,_}}throw new Error("Unable to fetch auth iframe URL")}const Dt={class:"pairing-entry"},Bt={key:0,class:"input-row"},Nt={class:"slot-word"},Tt={class:"typed-prefix"},Ut={class:"hint-suffix"},Vt={key:0,class:"cursor-overlay",style:{"--cursor-pos":0,"--word-len":0}},Ht=["placeholder"],Rt={key:0,class:"processing-status"},Kt={class:"processing-icon"},zt={key:1,class:"device-info"},Ft={class:"device-permit-text"},jt={class:"device-meta"},Ot={key:0,class:"error-message",style:{"margin-top":"0.5rem"}},qt={class:"button-row",style:{"margin-top":"0.75rem",display:"flex",gap:"0.5rem"}},Yt=["disabled"],Zt=["disabled"],Gt={__name:"RemoteAuthPermit",props:{title:{type:String,default:"Help Another Device Sign In"},description:{type:String,default:"Enter the code shown on the device that needs to sign in."},placeholder:{type:String,default:"Enter three words"},action:{type:String,default:"login"}},emits:["completed","error","cancelled","back","register","deviceInfoVisible"],setup(T,{expose:o,emit:$}){const _=$,I=c(!1),C=c(null),U=c(null);let d=null,V=null;try{V=Ae()}catch{}const E=c(null),j=c(null),u=c(""),g=c(!1),y=c(""),h=c(null),p=c("");He(h,e=>{_("deviceInfoVisible",!!e)});const w=c(!1),P=c(!1),L=c(0),H=c(0),X=c(0),ue=c(!1),se=c(!1);let R=0,ne=!1,G=null,oe=null,q=null,N=null,Y=null,J=null;function ae(e,n="info",s=3e3){V&&V.showMessage(e,n,s)}async function me(){try{const e=await dt();U.value=e}catch(e){console.warn("Unable to load settings",e)}}function ye(e,n){if(!e||n<0)return{word:"",start:0,end:0};let s=n,i=n;for(;s>0&&/[a-zA-Z]/.test(e[s-1]);)s--;for(;i<e.length&&/[a-zA-Z]/.test(e[i]);)i++;return{word:e.slice(s,i),start:s,end:i}}function le(e){return e.trim().split(/[.\s]+/).filter(n=>n.length>0)}function D(e){const n=le(e),s=[];for(const i of n){let r=i.toLowerCase();for(;r.length>0&&s.length<3;){let S=null;for(let b=Math.min(r.length,6);b>=3;b--){const x=r.slice(0,b);if(te(x)){S=x;break}}if(S)s.push(S),r=r.slice(S.length);else{s.push(r);break}}if(s.length>=3)break}return s}function We(e){const n=/[.\s]$/.test(e),s=D(e);return n?s.length:Math.max(0,s.length-1)}function ce(e){if(!e)return{valid:!0,segments:[]};const n=[],s=/[.\s]$/.test(e);let i,r=/([a-zA-Z]+)|([.\s]+)/g;for(;(i=r.exec(e))!==null;)i[1]?n.push({text:i[1],isWord:!0,start:i.index}):i[2]&&n.push({text:i[2],isWord:!1,start:i.index});const S=n.filter(x=>x.isWord);let b=!0;return S.forEach((x,K)=>{const M=K===S.length-1,Z=x.text.toLowerCase();M&&!s?x.invalid=!Ge(Z):x.invalid=!te(Z),x.invalid&&(b=!1)}),{valid:b,segments:n}}function we(e){return ce(e).valid}function ee(e){return D(e).length>0&&D(e).every(n=>te(n))}function Pe(e){if(/[.\s]$/.test(e))return"";const s=e.match(/[a-zA-Z]+$/);return s?s[0].toLowerCase():""}function be(e,n){if(!e||n===0)return{wordIndex:0,charIndex:0};const s=e.slice(0,n),i=/[.\s]$/.test(s),r=D(s);if(r.length===0)return{wordIndex:0,charIndex:0};if(i)return{wordIndex:Math.min(r.length,2),charIndex:0};const S=r[r.length-1],b=r.length-1;D(e);const x=S.length;return b<2&&!se.value&&te(S)?{wordIndex:b+1,charIndex:0}:{wordIndex:Math.min(b,2),charIndex:x}}function de(e,n){if(!e||n===0)return{wordIndex:0,charIndex:0};const s=e.slice(0,n),i=/[.\s]$/.test(s),r=D(s);if(r.length===0)return{wordIndex:0,charIndex:0};if(i)return{wordIndex:Math.min(r.length,2),charIndex:0};const S=r[r.length-1],b=r.length-1;return{wordIndex:Math.min(b,2),charIndex:S.length}}const Me=A(()=>{const e=D(u.value),n=[],s=Pe(u.value),i=p.value,r=/[.\s]$/.test(u.value),S=H.value!==X.value,b=de(u.value,Math.min(H.value,X.value)),x=de(u.value,Math.max(H.value,X.value)),K=S?de(u.value,L.value):be(u.value,L.value);for(let M=0;M<3;M++){const Z=K.wordIndex===M;let O=-1,z=-1;if(S&&(M>b.wordIndex&&M<x.wordIndex?(O=0,z=e[M]?.length??0):M===b.wordIndex&&M===x.wordIndex?(O=b.charIndex,z=x.charIndex):M===b.wordIndex?(O=b.charIndex,z=e[M]?.length??0):M===x.wordIndex&&(O=0,z=x.charIndex)),M<e.length){const _e=e[M].toLowerCase(),Oe=M===e.length-1,qe=Oe&&!r?!Ge(_e):!te(_e);if(Oe&&!r&&i&&s){const rt=i.length;n.push({text:"",typedPrefix:s,hintSuffix:i.slice(s.length),invalid:qe,hasCursor:Z,cursorCharIndex:Z?K.charIndex:-1,wordLen:rt,selectionStartChar:O,selectionEndChar:z})}else n.push({text:_e,invalid:qe,hasCursor:Z,cursorCharIndex:Z?K.charIndex:-1,wordLen:_e.length,selectionStartChar:O,selectionEndChar:z})}else n.push({text:"",invalid:!1,hasCursor:Z,cursorCharIndex:0,wordLen:0,selectionStartChar:O,selectionEndChar:z})}return n}),Ee=A(()=>H.value!==X.value),fe=A(()=>{const e=D(u.value);return e.length===3&&e.every(n=>te(n))});function ke(e){return D(e).join(".")}function De(){if(!G||q)return;const e=Qe(G);q=Xe(e,oe).then(n=>{N=n,q=null})}async function l(){if(N){const n=N;return N=null,n}if(q){await q;const n=N;return N=null,n}if(!G)throw new Error("No PoW challenge available");const e=Qe(G);return await Xe(e,oe)}function t(e){e?.challenge&&(G=e.challenge,oe=e.work,N=null,q=null,De())}async function v(){if(!(d||ne)){ne=!0;try{const e=U.value?.auth_host,n="/auth/ws/remote-auth/permit",s=e&&location.host!==e?`//${e}${n}`:n;d=await vt(s);const i=await d.receive_json();if(i.status&&i.detail)throw new Error(i.detail);if(!i.pow?.challenge)throw new Error("Server did not send PoW challenge");t(i.pow)}catch(e){throw console.error("WebSocket connection error:",e),d=null,e}finally{ne=!1}}}function st(e){if(e.key==="Tab"||e.key===" "||e.key==="Escape"){at(e);return}setTimeout(ze,0)}function ze(){const e=E.value,n=e?.selectionStart??u.value.length,s=e?.selectionEnd??n,r=(e?.selectionDirection??"none")==="backward"?n:s;se.value=r<R,R=r,L.value=r,X.value=s,H.value=n}function nt(){L.value=E.value?.selectionStart??u.value.length;const{word:e,end:n}=ye(u.value,L.value);if(We(u.value)>=3||!e||e.length<1||L.value!==n){p.value="";return}const i=Ze(e.toLowerCase());i&&i!==e.toLowerCase()?p.value=i:p.value=""}function Fe(){if(!p.value)return!1;const{word:e,start:n,end:s}=ye(u.value,L.value);if(!e)return!1;const i=u.value.slice(0,n),b=D(i).length===2?"":" ",x=u.value.slice(s);u.value=i+p.value+b+x.trimStart();const K=n+p.value.length+b.length;return Ie(()=>{E.value?.setSelectionRange(K,K),L.value=K}),p.value="",!0}function ve(){L.value=E.value?.selectionStart??u.value.length;const e=L.value,s=u.value.slice(0,e).match(/([a-zA-Z]+) $/);if(s){const r=s[1].toLowerCase(),S=Ze(r);if(S&&S!==r&&!te(r)){const b=e-s[0].length,x=u.value.slice(0,b),K=u.value.slice(e),O=D(x).length===2?"":" ";u.value=x+S+O+K;const z=b+S.length+O.length;Ie(()=>{E.value?.setSelectionRange(z,z),L.value=z})}}nt(),Y&&(clearTimeout(Y),Y=null),h.value=null,C.value=null,P.value=!1,w.value=!we(u.value);const i=D(u.value);if(i.length>=1&&!d&&!ne&&v(),i.length===3){if(!ee(u.value))return;Y=setTimeout(()=>{ot()},150)}}async function ot(){if(!(g.value||I.value||!fe.value||ke(u.value)===J&&h.value)){g.value=!0,y.value="pow",C.value=null,P.value=!1;try{if(await v(),!d)throw new Error("Failed to connect");const n=await l(),s=Je(n),i=ke(u.value);if(!fe.value)return;y.value="server",d.send_json({code:i,pow:s});const r=await d.receive_json();if(t(r.pow),typeof r.status=="number"&&r.status>=400){ae(r.detail||"Request failed","error"),P.value=!0,h.value=null,J=null;return}r.status==="found"&&r.host?(h.value={host:r.host,user_agent_pretty:r.user_agent_pretty,client_ip:r.client_ip,action:r.action||"login"},J=i,Ie(()=>{j.value?.focus()})):(ae("Unexpected response from server","error"),P.value=!0,h.value=null,J=null)}catch(n){console.error("Lookup error:",n),ae(n.message||"Lookup failed","error"),P.value=!0,h.value=null,J=null,d&&(d.close(),d=null)}finally{g.value=!1,y.value=""}}}function at(e){if(e.key==="Escape"){u.value="",ve(),e.preventDefault();return}if(e.key==="Tab"){if(p.value&&Fe()){e.preventDefault(),ve();return}u.value.trim()&&e.preventDefault();return}e.key===" "&&p.value&&Fe()&&(e.preventDefault(),ve())}async function lt(){if(!(!h.value||I.value)){I.value=!0,C.value=null;try{if(d||await v(),!d)throw new Error("Failed to connect");const e=await l(),n=Je(e);d.send_json({authenticate:!0,pow:n});const s=await d.receive_json();if(typeof s.status=="number"&&s.status>=400)throw new Error(s.detail||"Authentication failed");if(!s.optionsJSON)throw new Error(s.detail||"Failed to get authentication options");const i=await ft(s);d.send_json(i);const r=await d.receive_json();if(typeof r.status=="number"&&r.status>=400)throw new Error(r.detail||"Authentication failed");if(r.status==="success")ae("Device authenticated successfully!","success",3e3),_("completed"),Be();else throw new Error(r.detail||"Authentication failed")}catch(e){console.error("Pairing error:",e);const n=e.name==="NotAllowedError"?"Passkey authentication was cancelled":e.message||"Authentication failed";C.value=n,_("error",n)}finally{I.value=!1,d&&(d.close(),d=null)}}}async function je(){if(d){try{d.send_json({deny:!0}),await new Promise(e=>setTimeout(e,100))}catch(e){console.error("Error sending deny message:",e)}d.close(),d=null}Be()}function Be(){u.value="",C.value=null,P.value=!1,h.value=null,g.value=!1,y.value="",p.value="",w.value=!1,J=null,d&&(d.close(),d=null),G=null,oe=null,q=null,N=null}return Se(async()=>{await me(),Ie(()=>{L.value=E.value?.selectionStart??0})}),$e(()=>{Y&&(clearTimeout(Y),Y=null),d&&(d.close(),d=null)}),o({reset:Be,deny:je,code:u,handleInput:ve,loading:I,error:C}),(e,n)=>(f(),m("div",Dt,[a("form",{onSubmit:pe(lt,["prevent"]),class:"pairing-form"},[h.value?h.value?(f(),m("div",zt,[a("p",Ft,[F("Permit "+W(h.value.action==="register"?"registration":"login")+" to ",1),a("strong",null,W(h.value.host),1)]),a("p",jt,W(h.value.user_agent_pretty),1),C.value?(f(),m("p",Ot,W(C.value),1)):B("",!0),a("div",qt,[a("button",{type:"button",class:"btn-secondary",disabled:I.value,onClick:je,style:{flex:"1"}}," Deny ",8,Yt),a("button",{ref_key:"submitBtnRef",ref:j,type:"submit",disabled:I.value,class:"btn-primary",style:{flex:"1"}},W(I.value?"Authenticating…":"Authorize"),9,Zt)])])):B("",!0):(f(),m("div",Bt,[a("div",{class:ie(["input-wrapper",{"has-error":P.value,"is-complete":h.value&&!P.value,focused:ue.value,"has-selection":Ee.value}])},[a("div",{class:ie(["slot-machine",{"has-error":P.value,"is-complete":h.value&&!P.value}]),"aria-hidden":"true"},[(f(!0),m(he,null,it(Me.value,(s,i)=>(f(),m("div",{key:i,class:ie(["slot-reel",{"invalid-word":s.invalid,empty:!s.text&&!s.typedPrefix}])},[a("div",Nt,[s.selectionStartChar>=0&&s.selectionEndChar>s.selectionStartChar?(f(),m("span",{key:0,class:"selection-overlay",style:Ne({"--sel-start":s.selectionStartChar,"--sel-end":s.selectionEndChar,"--word-len":s.wordLen})},null,4)):B("",!0),s.typedPrefix?(f(),m(he,{key:1},[a("span",Tt,W(s.typedPrefix),1),a("span",Ut,W(s.hintSuffix),1),s.hasCursor?(f(),m("span",{key:0,class:"cursor-overlay",style:Ne({"--cursor-pos":s.cursorCharIndex,"--word-len":s.wordLen})},null,4)):B("",!0)],64)):s.text?(f(),m(he,{key:2},[F(W(s.text)+" ",1),s.hasCursor?(f(),m("span",{key:0,class:"cursor-overlay",style:Ne({"--cursor-pos":s.cursorCharIndex,"--word-len":s.wordLen})},null,4)):B("",!0)],64)):(f(),m(he,{key:3},[s.hasCursor?(f(),m("span",Vt)):B("",!0)],64))])],2))),128))],2),ut(a("input",{ref_key:"inputRef",ref:E,"onUpdate:modelValue":n[0]||(n[0]=s=>u.value=s),type:"text",placeholder:T.placeholder,autocomplete:"off",autocapitalize:"none",autocorrect:"off",spellcheck:"false",class:"pairing-input hidden-input",onInput:ve,onKeydown:st,onMouseup:ze,onFocus:n[1]||(n[1]=s=>ue.value=!0),onBlur:n[2]||(n[2]=s=>ue.value=!1)},null,40,Ht),[[ct,u.value]])],2),y.value?(f(),m("div",Rt,[a("span",Kt,W(y.value==="pow"?"🔐":"📡"),1),n[3]||(n[3]=a("span",{class:"processing-spinner-small"},null,-1))])):B("",!0)]))],32)]))}},Jt=Ke(Gt,[["__scopeId","data-v-77a073d9"]]),Qt={class:"view-root","data-view":"profile"},Xt={class:"theme-toggle"},es=["title"],ts={class:"view-header"},ss={class:"remote-auth-inline"},ns={key:0,class:"remote-auth-label"},os={class:"section-block"},as={class:"section-body"},ls={class:"section-block"},rs=["disabled"],is=["disabled"],us=["disabled"],cs={key:0,class:"logout-note"},ds={key:1,class:"logout-note"},fs={__name:"ProfileView",setup(T){const o=Ae(),$=c(null),_=c(!1),I=c(!1),C=c(""),U=c(!1),d=c(null),V=c(null),E=c(!1),j=c(null),u=c(null),g=c(null),y=c(null),h=c(null),p=c(null),w=c(null),P=c(null),L=c(""),H=c(!1),X=A(()=>({"":"🌓",light:"☀️",dark:"🌙"})[L.value]||"🌓"),ue=A(()=>({"":"Auto (system)",light:"Light mode",dark:"Dark mode"})[L.value]||"Theme");He(()=>o.userInfo?.ctx?.user?.theme,l=>{L.value=l||""},{immediate:!0});function se(l){L.value=l,H.value=!1,kt({user:{theme:l}},!0),Re("/auth/api/user/theme",{method:"PATCH",body:{theme:l}}).catch(t=>o.showMessage(t.message,"error"))}const R=A(()=>_.value||I.value);He(_,l=>{l&&(C.value=o.userInfo?.ctx.user.display_name??"")}),Se(()=>{$.value=setInterval(()=>{o.userInfo&&(o.userInfo={...o.userInfo})},6e4)}),$e(()=>{$.value&&clearInterval($.value)});const ne=async()=>{try{await gt.register(null,null,()=>{o.showMessage("Adding new passkey...","info")}),await o.loadUserInfo(),o.showMessage("New passkey added successfully!","success",3e3)}catch(l){console.error("Failed to add new passkey:",l),o.showMessage(l.message,"error")}},G=()=>{o.showMessage("The other device is now signed in!","success",4e3),setTimeout(()=>j.value?.reset(),3e3)},oe=l=>{l.includes("cancelled")||o.showMessage(l,"error",4e3)},q=()=>{o.showMessage("📋 Link copied! Send it to your other device."),I.value=!1},N=l=>{Te(l,{primarySelector:".btn-primary",itemSelector:"button"})},Y=l=>{if(R.value)return;const t=ge(l);t&&t==="down"&&(l.preventDefault(),Te(P.value,{primarySelector:".mini-btn",itemSelector:".mini-btn, .pairing-input"}))},J=l=>{if(R.value)return;const t=ge(l);if(!t)return;l.preventDefault(),t==="left"||t==="right"?Ce(P.value,l.target,t,{itemSelector:".mini-btn, .pairing-input"}):t==="up"?p.value?.focusCurrent?.():t==="down"&&u.value?.$el?.focus()},ae=l=>{R.value||(l==="down"||l==="right"?N(g.value):(l==="up"||l==="left")&&Te(P.value,{primarySelector:".mini-btn",itemSelector:".mini-btn, .pairing-input"}))},me=l=>{if(R.value)return;const t=ge(l);t&&(l.preventDefault(),t==="left"||t==="right"?Ce(g.value,l.target,t,{itemSelector:"button"}):t==="up"?Ue(u.value?.$el,0,{itemSelector:".credential-item"}):t==="down"&&Ue(y.value?.$el,0,{itemSelector:".session-group"}))},ye=l=>{R.value||(l==="up"?N(g.value):l==="down"&&N(h.value))},le=l=>{if(R.value)return;const t=ge(l);t&&(l.preventDefault(),t==="left"||t==="right"?Ce(h.value,l.target,t,{itemSelector:"button"}):t==="up"&&Ue(y.value?.$el,-1,{itemSelector:".session-group"}))},D=async l=>{const t=l?.credential;if(t)try{await o.deleteCredential(t),o.showMessage("Passkey deleted! You should also remove it from your password manager or device.","success",3e3)}catch(v){o.showMessage(`Failed to delete passkey: ${v.message}`,"error")}},We=A(()=>o.settings?.rp_name||"this service"),ce=A(()=>o.userInfo?.sessions||[]),we=A(()=>ce.value.find(t=>t.is_current)?.host||"this host"),ee=c({}),Pe=async l=>{const t=l?.id;if(t){ee.value={...ee.value,[t]:!0};try{await o.terminateSession(t)}catch(v){o.showMessage(v.message||"Failed to terminate session","error",5e3)}finally{const v={...ee.value};delete v[t],ee.value=v}}},be=async()=>{await o.logoutEverywhere()},de=async()=>{await o.logout()},Me=()=>{C.value=o.userInfo?.ctx.user.display_name??"",_.value=!0},Ee=A(()=>{const l=o.userInfo?.ctx.permissions;return l.includes("auth:admin")||l.includes("auth:org:admin")}),fe=A(()=>ce.value.length>1),ke=A(()=>{const l=[{label:"Auth",href:ht()}];return Ee.value&&l.push({label:"Admin",href:pt()}),l}),De=async()=>{const l=C.value.trim();if(!l){o.showMessage("Name cannot be empty","error");return}try{U.value=!0,await Re("/auth/api/user/display-name",{method:"PATCH",body:{display_name:l}}),_.value=!1,await o.loadUserInfo(),o.showMessage("Name updated successfully!","success",3e3)}catch(t){o.showMessage(t.message||"Failed to update name","error")}finally{U.value=!1}};return(l,t)=>(f(),m("section",Qt,[a("div",Xt,[a("button",{class:"theme-btn",onClick:t[0]||(t[0]=v=>H.value=!H.value),title:ue.value},W(X.value),9,es),H.value?(f(),m("div",{key:0,class:"theme-menu",onClick:t[4]||(t[4]=v=>H.value=!1)},[a("button",{class:ie(["theme-option top",{active:L.value===""}]),onClick:t[1]||(t[1]=pe(v=>se(""),["stop"])),title:"Auto"},"🌓",2),a("button",{class:ie(["theme-option left",{active:L.value==="light"}]),onClick:t[2]||(t[2]=pe(v=>se("light"),["stop"])),title:"Light"},"☀️",2),a("button",{class:ie(["theme-option right",{active:L.value==="dark"}]),onClick:t[3]||(t[3]=pe(v=>se("dark"),["stop"])),title:"Dark"},"🌙",2)])):B("",!0)]),a("header",ts,[t[15]||(t[15]=a("h1",null,"User Profile",-1)),re(It,{ref_key:"breadcrumbs",ref:p,entries:ke.value,onKeydown:Y},null,8,["entries"]),t[16]||(t[16]=a("p",{class:"view-lede"},"Account dashboard for managing credentials and authenticating with other devices.",-1))]),a("section",{class:"section-block",ref_key:"userInfoSection",ref:P},[k(o).userInfo?.ctx?(f(),Q(et,{key:0,ref_key:"userBasicInfo",ref:w,name:k(o).userInfo.ctx.user.display_name,visits:k(o).userInfo.visits,"created-at":k(o).userInfo.created_at,"last-seen":k(o).userInfo.last_seen,loading:k(o).isLoading,"update-endpoint":"/auth/api/user/display-name",onSaved:t[6]||(t[6]=v=>k(o).loadUserInfo()),onEditName:Me,onKeydown:J},{default:Ye(()=>[a("div",ss,[E.value?B("",!0):(f(),m("label",ns,"Code words:")),re(Jt,{ref_key:"pairingEntry",ref:j,title:"",description:"",onCompleted:G,onError:oe,onDeviceInfoVisible:t[5]||(t[5]=v=>E.value=v)},null,512)]),t[17]||(t[17]=a("p",{class:"remote-auth-description"},"Provided by another device requesting remote auth.",-1))]),_:1},8,["name","visits","created-at","last-seen","loading"])):B("",!0)],512),a("section",os,[t[18]||(t[18]=a("div",{class:"section-header"},[a("h2",null,"Your Passkeys"),a("p",{class:"section-description"},[F("Ideally have at least two passkeys in case you lose one. More than one user can be registered on the same device, giving you a choice at login. "),a("a",{href:"https://bitwarden.com/pricing/",target:"_blank",rel:"noopener noreferrer"},"Bitwarden"),F(" can sync one passkey to all your devices. Other secure options include "),a("b",null,"local passkeys"),F(", as well as hardware keys such as "),a("a",{href:"https://www.yubico.com",target:"_blank",rel:"noopener noreferrer"},"YubiKey"),F(". Cloud sync via Google, Microsoft or iCloud is discouraged.")])],-1)),a("div",as,[re(xt,{ref_key:"credentialList",ref:u,credentials:k(o).userInfo?.credentials||[],"aaguid-info":k(o).userInfo?.aaguid_info||{},loading:k(o).isLoading,"hovered-credential-uuid":d.value,"hovered-session-credential-uuid":V.value?.credential,"navigation-disabled":R.value,"allow-delete":"",onDelete:D,onCredentialHover:t[7]||(t[7]=v=>d.value=v),onNavigateOut:ae},null,8,["credentials","aaguid-info","loading","hovered-credential-uuid","hovered-session-credential-uuid","navigation-disabled"]),a("div",{class:"button-row",ref_key:"credentialButtons",ref:g},[a("button",{onClick:ne,class:"btn-primary",onKeydown:me},"Register New",32),a("button",{onClick:t[8]||(t[8]=v=>I.value=!0),class:"btn-secondary",onKeydown:me},"Another Device",32)],512)])]),re(Ct,{ref_key:"sessionList",ref:y,sessions:ce.value,"terminating-sessions":ee.value,"hovered-credential-uuid":d.value,"navigation-disabled":R.value,onTerminate:Pe,onSessionHover:t[9]||(t[9]=v=>V.value=v),onNavigateOut:ye,"section-description":"You are currently signed in to the following sessions. If you don't recognize something, consider deleting not only the session but the associated passkey you suspect is compromised, as only this terminates all linked sessions and prevents logging in again."},null,8,["sessions","terminating-sessions","hovered-credential-uuid","navigation-disabled"]),_.value?(f(),Q($t,{key:0,onClose:t[12]||(t[12]=v=>_.value=!1)},{default:Ye(()=>[t[19]||(t[19]=a("h3",null,"Edit Display Name",-1)),a("form",{onSubmit:pe(De,["prevent"]),class:"modal-form"},[re(St,{label:"Display Name",modelValue:C.value,"onUpdate:modelValue":t[10]||(t[10]=v=>C.value=v),busy:U.value,onCancel:t[11]||(t[11]=v=>_.value=!1)},null,8,["modelValue","busy"])],32)]),_:1})):B("",!0),a("section",ls,[a("div",{class:"button-row",ref_key:"logoutButtons",ref:h},[a("button",{type:"button",class:"btn-secondary",onClick:t[13]||(t[13]=(...v)=>k(Le)&&k(Le)(...v)),onKeydown:le}," Back ",32),fe.value?(f(),m(he,{key:1},[a("button",{onClick:de,class:"btn-danger",disabled:k(o).isLoading,onKeydown:le},"Logout",40,is),a("button",{onClick:be,class:"btn-danger",disabled:k(o).isLoading,onKeydown:le},"All",40,us)],64)):(f(),m("button",{key:0,onClick:be,class:"btn-danger",disabled:k(o).isLoading,onKeydown:le},"Logout",40,rs))],512),fe.value?(f(),m("p",ds,[t[21]||(t[21]=a("strong",null,"Logout",-1)),F(" this session on "+W(we.value)+", or ",1),t[22]||(t[22]=a("strong",null,"All",-1)),F(" sessions across all sites and devices for "+W(We.value)+". You'll need to log in again with your passkey afterwards.",1)])):(f(),m("p",cs,[t[20]||(t[20]=a("strong",null,"Logout",-1)),F(" from "+W(we.value)+".",1)]))]),I.value?(f(),Q(Lt,{key:1,endpoint:"/auth/api/user/create-link",onClose:t[14]||(t[14]=v=>I.value=!1),onCopied:q})):B("",!0)]))}},vs=Ke(fs,[["__scopeId","data-v-2cc35e77"]]),hs={class:"view-root host-view","data-view":"host-profile"},ps={class:"view-header"},gs={class:"view-lede"},ms={class:"section-body"},ys={key:1,class:"empty-state"},ws={class:"section-block"},bs={class:"section-body host-actions"},ks=["disabled"],_s=["disabled"],Is={class:"note"},xs={__name:"HostProfileView",props:{initializing:{type:Boolean,default:!1}},setup(T){const o=Ae(),$=window.location.host,_=c(null),I=c(null),C=A(()=>o.userInfo?.ctx||null),U=A(()=>C.value?.org.display_name??""),d=A(()=>C.value?.role.display_name??""),V=A(()=>{const p=o.settings?.rp_name;return p?`${p} account`:"Account overview"}),E=A(()=>`You're signed in to ${$}.`),j=A(()=>o.settings?.auth_host||""),u=A(()=>{const p=j.value;if(!p)return"";let w=o.settings?.ui_base_path??"/auth/";return w.startsWith("/")||(w=`/${w}`),w.endsWith("/")||(w=`${w}/`),`${window.location.protocol||"https:"}//${p}${w}`}),g=()=>{u.value&&(window.location.href=u.value)},y=async()=>{await o.logout()},h=p=>{const w=ge(p);w&&(p.preventDefault(),(w==="left"||w==="right")&&Ce(I.value,p.target,w,{itemSelector:"button"}))};return(p,w)=>(f(),m("section",hs,[a("header",ps,[a("h1",null,W(V.value),1),a("p",gs,W(E.value),1)]),a("section",{class:"section-block",ref_key:"userInfoSection",ref:_},[a("div",ms,[C.value?(f(),Q(et,{key:0,name:C.value.user.display_name,visits:k(o).userInfo?.visits||0,"created-at":k(o).userInfo?.created_at,"last-seen":k(o).userInfo?.last_seen,"org-display-name":U.value,"role-name":d.value,"can-edit":!1},null,8,["name","visits","created-at","last-seen","org-display-name","role-name"])):(f(),m("p",ys,W(T.initializing?"Loading your account…":"No active session found."),1))])],512),a("section",ws,[a("div",bs,[a("div",{class:"button-row",ref_key:"buttonRow",ref:I,onKeydown:h},[a("button",{type:"button",class:"btn-secondary",onClick:w[0]||(w[0]=(...P)=>k(Le)&&k(Le)(...P))}," Back "),a("button",{type:"button",class:"btn-danger",disabled:k(o).isLoading,onClick:y},W(k(o).isLoading?"Signing out…":"Logout"),9,ks),u.value?(f(),m("button",{key:0,type:"button",class:"btn-primary",disabled:k(o).isLoading,onClick:g}," Full Profile ",8,_s)):B("",!0)],544),a("p",Is,[w[1]||(w[1]=a("strong",null,"Logout",-1)),F(" from "+W(k($))+", or access your ",1),w[2]||(w[2]=a("strong",null,"Full Profile",-1)),F(" at "+W(j.value)+" (you may need to sign in again).",1)])])])]))}},Cs=Ke(xs,[["__scopeId","data-v-237999a2"]]),Ss={class:"app-shell"},$s={class:"app-main"},Ls={__name:"App",setup(T){const o=Ae(),$=c("loading"),_=c("Loading...");function I(g){if(!g)return null;const y=g.trim().toLowerCase();return y?y.replace(/:80$/,"").replace(/:443$/,""):null}const C=A(()=>{const g=o.settings?.auth_host;if(!g)return!1;const y=I(window.location.host),h=I(g);return y!==h});function U(){o.userInfo=null,$.value="terminal"}const d=()=>o.userInfo?.ctx.user.uuid,V=new mt(d,U);Se(()=>V.start()),$e(()=>V.stop());async function E(){try{return o.userInfo=await Re("/auth/api/user-info",{method:"POST"}),$.value="profile",!0}catch{return o.userInfo=null,!1}}async function j(){const g=await Et("login");yt(g),_.value="Authentication required..."}function u(g){const y=g.data;if(y?.type)switch(y.type){case"auth-success":xe(),$.value="loading",_.value="Loading user profile...",E();break;case"auth-error":y.cancelled?console.log("Authentication cancelled by user"):o.showMessage(y.message||"Authentication failed","error",5e3);break;case"auth-cancelled":console.log("Authentication cancelled");break;case"auth-back":xe(),U();break;case"auth-close-request":xe();break}}return Se(async()=>{window.addEventListener("message",u),await o.loadSettings();const g=o.settings?.rp_name;if(g){const h=o.settings?.auth_host,p=h&&I(window.location.host)!==I(h);document.title=p?`${g} · Account summary`:g}await E()||j()}),$e(()=>{window.removeEventListener("message",u),xe()}),(g,y)=>(f(),m("div",Ss,[re(Wt),a("main",$s,[$.value==="profile"&&C.value?(f(),Q(Cs,{key:0})):$.value==="profile"?(f(),Q(vs,{key:1})):$.value==="loading"?(f(),Q(At,{key:2,message:_.value},null,8,["message"])):$.value==="terminal"?(f(),Q(Pt,{key:3})):B("",!0)])]))}};_t();const tt=wt(Ls);tt.use(Mt());tt.mount("#app");bt();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as d,b as u,x as s,u as m,m as o,a8 as l,J as h,K as p}from"./_plugin-vue_export-helper-DJsHCwvl.js";import{R as f}from"./RestrictedAuth-DWKMTEV3.js";import{g as a}from"./helpers-DzjFIx78.js";import"./pow-DUr-T9XX.js";const w={__name:"RestrictedForward",setup(_){const n=o(()=>l()),r=o(()=>{const e=document.documentElement.getAttribute("data-mode");return e==="reauth"?"reauth":e==="forbidden"?"forbidden":"login"});function i(){location.reload()}function c(){const t=n.value||"/auth/";window.location.pathname!==t&&history.replaceState(null,"",t),window.location.href=t}return d(()=>{window.addEventListener("keydown",t=>{t.key==="Escape"&&a()})}),(t,e)=>(u(),s(f,{mode:r.value,onAuthenticated:i,onBack:m(a),onHome:c},null,8,["mode","onBack"]))}};h(w).mount("#app");p();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function v(e){const a=e.replace(/-/g,"+").replace(/_/g,"/"),t=a+"=".repeat((4-a.length%4)%4);return Uint8Array.from(atob(t),i=>i.charCodeAt(0))}function k(e){return btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}const h=["able","about","absent","abuse","access","acid","across","act","adapt","add","adjust","admit","adult","advice","affair","afraid","again","age","agree","ahead","aim","air","aisle","alarm","album","alert","alien","all","almost","alone","alpha","also","alter","always","amazed","among","amused","anchor","angle","animal","ankle","annual","answer","any","apart","appear","april","arch","are","argue","army","around","array","art","ascent","ash","ask","aspect","assume","asthma","atom","attack","audit","august","aunt","author","avoid","away","awful","axis","baby","back","bad","bag","ball","bamboo","bank","bar","base","battle","beach","become","beef","before","begin","behind","below","bench","best","better","beyond","bid","bike","bind","bio","birth","bitter","black","bleak","blind","blood","blue","board","body","boil","bomb","bone","book","border","boss","bottom","bounce","bowl","box","boy","brain","bread","bring","brown","brush","bubble","buck","budget","build","bulk","bundle","burden","bus","but","buyer","buzz","cable","cache","cage","cake","call","came","can","car","case","catch","cause","cave","celery","cement","census","cereal","change","check","child","choice","chunk","cigar","circle","city","civil","class","clean","client","close","club","coast","code","coffee","coil","cold","come","cool","copy","core","cost","cotton","couch","cover","coyote","craft","cream","crime","cross","cruel","cry","cube","cue","cult","cup","curve","custom","cute","cycle","dad","damage","danger","daring","dash","dawn","day","deal","debate","decide","deer","define","degree","deity","delay","demand","denial","depth","derive","design","detail","device","dial","dice","die","differ","dim","dinner","direct","dish","divert","dizzy","doctor","dog","dollar","domain","donate","door","dose","double","dove","draft","dream","drive","drop","drum","dry","duck","dumb","dune","during","dust","dutch","dwarf","eager","early","east","echo","eco","edge","edit","effort","egg","eight","either","elbow","elder","elite","else","embark","emerge","emily","employ","enable","end","enemy","engine","enjoy","enlist","enough","enrich","ensure","entire","envy","equal","era","erode","error","erupt","escape","essay","estate","ethics","evil","evoke","exact","excess","exist","exotic","expect","extent","eye","fabric","face","fade","faith","fall","family","fan","far","father","fault","feel","female","fence","fetch","fever","few","fiber","field","figure","file","find","first","fish","fit","fix","flat","flesh","flight","float","fluid","fly","foam","focus","fog","foil","follow","food","force","fossil","found","fox","frame","fresh","friend","frog","fruit","fuel","fun","fury","future","gadget","gain","galaxy","game","gap","garden","gas","gate","gauge","gaze","genius","ghost","giant","gift","giggle","ginger","girl","give","glass","glide","globe","glue","goal","god","gold","good","gospel","govern","gown","grant","great","grid","group","grunt","guard","guess","guide","gulf","gun","gym","habit","hair","half","hammer","hand","happy","hard","hat","have","hawk","hay","hazard","head","hedge","height","help","hen","hero","hidden","high","hill","hint","hip","hire","hobby","hockey","hold","home","honey","hood","hope","horse","host","hotel","hour","hover","how","hub","huge","human","hungry","hurt","hybrid","ice","icon","idea","idle","ignore","ill","image","immune","impact","income","index","infant","inhale","inject","inmate","inner","input","inside","into","invest","iron","island","issue","italy","item","ivory","jacket","jaguar","james","jar","jazz","jeans","jelly","jewel","job","joe","joke","joy","judge","juice","july","jump","june","just","kansas","kate","keep","kernel","key","kick","kid","kind","kiss","kit","kiwi","knee","knife","know","labor","lady","lag","lake","lamp","laptop","large","later","laugh","lava","law","layer","lazy","leader","left","legal","lemon","length","lesson","letter","level","liar","libya","lid","life","light","like","limit","line","lion","liquid","list","little","live","lizard","load","local","logic","long","loop","lost","loud","love","low","loyal","lucky","lumber","lunch","lust","luxury","lyrics","mad","magic","main","major","make","male","mammal","man","map","market","mass","matter","maze","mccoy","meadow","media","meet","melt","member","men","mercy","mesh","method","middle","milk","mimic","mind","mirror","miss","mix","mobile","model","mom","monkey","moon","more","mother","mouse","move","much","muffin","mule","must","mutual","myself","myth","naive","name","napkin","narrow","nasty","nation","near","neck","need","nephew","nerve","nest","net","never","news","next","nice","night","noble","noise","noodle","normal","nose","note","novel","now","number","nurse","nut","oak","obey","object","oblige","obtain","occur","ocean","odor","off","often","oil","okay","old","olive","omit","once","one","onion","online","open","opium","oppose","option","orange","orbit","order","organ","orient","orphan","other","outer","oval","oven","own","oxygen","oyster","ozone","pact","paddle","page","pair","palace","panel","paper","parade","past","path","pause","pave","paw","pay","peace","pen","people","pepper","permit","pet","philip","phone","phrase","piano","pick","piece","pig","pilot","pink","pipe","pistol","pitch","pizza","place","please","pluck","poem","point","polar","pond","pool","post","pot","pound","powder","praise","prefer","price","profit","public","pull","punch","pupil","purity","push","put","puzzle","qatar","quasi","queen","quite","quoted","rabbit","race","radio","rail","rally","ramp","range","rapid","rare","rather","raven","raw","razor","real","rebel","recall","red","reform","region","reject","relief","remain","rent","reopen","report","result","return","review","reward","rhythm","rib","rich","ride","rifle","right","ring","riot","ripple","risk","ritual","river","road","robot","rocket","room","rose","rotate","round","row","royal","rubber","rude","rug","rule","run","rural","sad","safe","sage","sail","salad","same","santa","sauce","save","say","scale","scene","school","scope","screen","scuba","sea","second","seed","self","semi","sense","series","settle","seven","shadow","she","ship","shock","shrimp","shy","sick","side","siege","sign","silver","simple","since","siren","sister","six","size","skate","sketch","ski","skull","slab","sleep","slight","slogan","slush","small","smile","smooth","snake","sniff","snow","soap","soccer","soda","soft","solid","son","soon","sort","south","space","speak","sphere","spirit","split","spoil","spring","spy","square","state","step","still","story","strong","stuff","style","submit","such","sudden","suffer","sugar","suit","summer","sun","supply","sure","swamp","sweet","switch","sword","symbol","syntax","syria","system","table","tackle","tag","tail","talk","tank","tape","target","task","tattoo","taxi","team","tell","ten","term","test","text","that","theme","this","three","thumb","tibet","ticket","tide","tight","tilt","time","tiny","tip","tired","tissue","title","toast","today","toe","toilet","token","tomato","tone","tool","top","torch","toss","total","toward","toy","trade","tree","trial","trophy","true","try","tube","tumble","tunnel","turn","twenty","twice","two","type","ugly","unable","uncle","under","unfair","unique","unlock","until","unveil","update","uphold","upon","upper","upset","urban","urge","usage","use","usual","vacuum","vague","valid","van","vapor","vast","vault","vein","velvet","vendor","very","vessel","viable","video","view","villa","violin","virus","visit","vital","vivid","vocal","voice","volume","vote","voyage","wage","wait","wall","want","war","wash","water","wave","way","wealth","web","weird","were","west","wet","what","when","whip","wide","wife","will","window","wire","wish","wolf","woman","wonder","wood","work","wrap","wreck","write","wrong","xander","xbox","xerox","xray","yang","yard","year","yellow","yes","yin","york","you","zane","zara","zebra","zen","zero","zippo","zone","zoo","zorro","zulu"],o=new Map;for(const e of h)for(let a=1;a<=Math.min(e.length,6);a++){const t=e.slice(0,a);o.has(t)||o.set(t,[]),o.get(t).push(e)}function w(e){if(!e)return[];const a=e.toLowerCase();return o.get(a)||[]}function x(e){const a=w(e);return a.length===1?a[0]:null}function z(e){if(!e)return!1;const a=e.toLowerCase();return h.includes(a)}function j(e){if(!e)return!0;const a=e.toLowerCase();return o.has(a)}async function A(e,a,t={}){const{signal:i}=t,m=performance.now(),n=e instanceof ArrayBuffer?new Uint8Array(e):e;if(!(n instanceof Uint8Array)||n.length!==8)throw new Error("Challenge must be exactly 8 bytes");const b=await crypto.subtle.importKey("raw",n,"PBKDF2",!1,["deriveBits"]),u=new Uint8Array(8*a);let l=0;const s=2047,r=new Uint32Array(2);for(let c=0;c<a;c++){if(i?.aborted)throw new DOMException("PoW operation aborted","AbortError");let p;do l++,++r[0]===4294967296&&++r[1],p=new Uint32Array(await crypto.subtle.deriveBits({name:"PBKDF2",salt:r,iterations:128,hash:"SHA-512"},b,32));while(p[0]&s);u.set(new Uint8Array(r.buffer),c*8)}const d=(performance.now()-m)/1e3,g=a*(s+1),f=(l/g).toFixed(1),y=l/((s+1)*d);return console.log(`PoW work=${a} solved in ${d.toFixed(2)}s (${f}x expected ${y.toFixed(1)} work/s)`),u}export{
|
|
1
|
+
function v(e){const a=e.replace(/-/g,"+").replace(/_/g,"/"),t=a+"=".repeat((4-a.length%4)%4);return Uint8Array.from(atob(t),i=>i.charCodeAt(0))}function k(e){return btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}const h=["able","about","absent","abuse","access","acid","across","act","adapt","add","adjust","admit","adult","advice","affair","afraid","again","age","agree","ahead","aim","air","aisle","alarm","album","alert","alien","all","almost","alone","alpha","also","alter","always","amazed","among","amused","anchor","angle","animal","ankle","annual","answer","any","apart","appear","april","arch","are","argue","army","around","array","art","ascent","ash","ask","aspect","assume","asthma","atom","attack","audit","august","aunt","author","avoid","away","awful","axis","baby","back","bad","bag","ball","bamboo","bank","bar","base","battle","beach","become","beef","before","begin","behind","below","bench","best","better","beyond","bid","bike","bind","bio","birth","bitter","black","bleak","blind","blood","blue","board","body","boil","bomb","bone","book","border","boss","bottom","bounce","bowl","box","boy","brain","bread","bring","brown","brush","bubble","buck","budget","build","bulk","bundle","burden","bus","but","buyer","buzz","cable","cache","cage","cake","call","came","can","car","case","catch","cause","cave","celery","cement","census","cereal","change","check","child","choice","chunk","cigar","circle","city","civil","class","clean","client","close","club","coast","code","coffee","coil","cold","come","cool","copy","core","cost","cotton","couch","cover","coyote","craft","cream","crime","cross","cruel","cry","cube","cue","cult","cup","curve","custom","cute","cycle","dad","damage","danger","daring","dash","dawn","day","deal","debate","decide","deer","define","degree","deity","delay","demand","denial","depth","derive","design","detail","device","dial","dice","die","differ","dim","dinner","direct","dish","divert","dizzy","doctor","dog","dollar","domain","donate","door","dose","double","dove","draft","dream","drive","drop","drum","dry","duck","dumb","dune","during","dust","dutch","dwarf","eager","early","east","echo","eco","edge","edit","effort","egg","eight","either","elbow","elder","elite","else","embark","emerge","emily","employ","enable","end","enemy","engine","enjoy","enlist","enough","enrich","ensure","entire","envy","equal","era","erode","error","erupt","escape","essay","estate","ethics","evil","evoke","exact","excess","exist","exotic","expect","extent","eye","fabric","face","fade","faith","fall","family","fan","far","father","fault","feel","female","fence","fetch","fever","few","fiber","field","figure","file","find","first","fish","fit","fix","flat","flesh","flight","float","fluid","fly","foam","focus","fog","foil","follow","food","force","fossil","found","fox","frame","fresh","friend","frog","fruit","fuel","fun","fury","future","gadget","gain","galaxy","game","gap","garden","gas","gate","gauge","gaze","genius","ghost","giant","gift","giggle","ginger","girl","give","glass","glide","globe","glue","goal","god","gold","good","gospel","govern","gown","grant","great","grid","group","grunt","guard","guess","guide","gulf","gun","gym","habit","hair","half","hammer","hand","happy","hard","hat","have","hawk","hay","hazard","head","hedge","height","help","hen","hero","hidden","high","hill","hint","hip","hire","hobby","hockey","hold","home","honey","hood","hope","horse","host","hotel","hour","hover","how","hub","huge","human","hungry","hurt","hybrid","ice","icon","idea","idle","ignore","ill","image","immune","impact","income","index","infant","inhale","inject","inmate","inner","input","inside","into","invest","iron","island","issue","italy","item","ivory","jacket","jaguar","james","jar","jazz","jeans","jelly","jewel","job","joe","joke","joy","judge","juice","july","jump","june","just","kansas","kate","keep","kernel","key","kick","kid","kind","kiss","kit","kiwi","knee","knife","know","labor","lady","lag","lake","lamp","laptop","large","later","laugh","lava","law","layer","lazy","leader","left","legal","lemon","length","lesson","letter","level","liar","libya","lid","life","light","like","limit","line","lion","liquid","list","little","live","lizard","load","local","logic","long","loop","lost","loud","love","low","loyal","lucky","lumber","lunch","lust","luxury","lyrics","mad","magic","main","major","make","male","mammal","man","map","market","mass","matter","maze","mccoy","meadow","media","meet","melt","member","men","mercy","mesh","method","middle","milk","mimic","mind","mirror","miss","mix","mobile","model","mom","monkey","moon","more","mother","mouse","move","much","muffin","mule","must","mutual","myself","myth","naive","name","napkin","narrow","nasty","nation","near","neck","need","nephew","nerve","nest","net","never","news","next","nice","night","noble","noise","noodle","normal","nose","note","novel","now","number","nurse","nut","oak","obey","object","oblige","obtain","occur","ocean","odor","off","often","oil","okay","old","olive","omit","once","one","onion","online","open","opium","oppose","option","orange","orbit","order","organ","orient","orphan","other","outer","oval","oven","own","oxygen","oyster","ozone","pact","paddle","page","pair","palace","panel","paper","parade","past","path","pause","pave","paw","pay","peace","pen","people","pepper","permit","pet","philip","phone","phrase","piano","pick","piece","pig","pilot","pink","pipe","pistol","pitch","pizza","place","please","pluck","poem","point","polar","pond","pool","post","pot","pound","powder","praise","prefer","price","profit","public","pull","punch","pupil","purity","push","put","puzzle","qatar","quasi","queen","quite","quoted","rabbit","race","radio","rail","rally","ramp","range","rapid","rare","rather","raven","raw","razor","real","rebel","recall","red","reform","region","reject","relief","remain","rent","reopen","report","result","return","review","reward","rhythm","rib","rich","ride","rifle","right","ring","riot","ripple","risk","ritual","river","road","robot","rocket","room","rose","rotate","round","row","royal","rubber","rude","rug","rule","run","rural","sad","safe","sage","sail","salad","same","santa","sauce","save","say","scale","scene","school","scope","screen","scuba","sea","second","seed","self","semi","sense","series","settle","seven","shadow","she","ship","shock","shrimp","shy","sick","side","siege","sign","silver","simple","since","siren","sister","six","size","skate","sketch","ski","skull","slab","sleep","slight","slogan","slush","small","smile","smooth","snake","sniff","snow","soap","soccer","soda","soft","solid","son","soon","sort","south","space","speak","sphere","spirit","split","spoil","spring","spy","square","state","step","still","story","strong","stuff","style","submit","such","sudden","suffer","sugar","suit","summer","sun","supply","sure","swamp","sweet","switch","sword","symbol","syntax","syria","system","table","tackle","tag","tail","talk","tank","tape","target","task","tattoo","taxi","team","tell","ten","term","test","text","that","theme","this","three","thumb","tibet","ticket","tide","tight","tilt","time","tiny","tip","tired","tissue","title","toast","today","toe","toilet","token","tomato","tone","tool","top","torch","toss","total","toward","toy","trade","tree","trial","trophy","true","try","tube","tumble","tunnel","turn","twenty","twice","two","type","ugly","unable","uncle","under","unfair","unique","unlock","until","unveil","update","uphold","upon","upper","upset","urban","urge","usage","use","usual","vacuum","vague","valid","van","vapor","vast","vault","vein","velvet","vendor","very","vessel","viable","video","view","villa","violin","virus","visit","vital","vivid","vocal","voice","volume","vote","voyage","wage","wait","wall","want","war","wash","water","wave","way","wealth","web","weird","were","west","wet","what","when","whip","wide","wife","will","window","wire","wish","wolf","woman","wonder","wood","work","wrap","wreck","write","wrong","xander","xbox","xerox","xray","yang","yard","year","yellow","yes","yin","york","you","zane","zara","zebra","zen","zero","zippo","zone","zoo","zorro","zulu"],o=new Map;for(const e of h)for(let a=1;a<=Math.min(e.length,6);a++){const t=e.slice(0,a);o.has(t)||o.set(t,[]),o.get(t).push(e)}function w(e){if(!e)return[];const a=e.toLowerCase();return o.get(a)||[]}function x(e){const a=w(e);return a.length===1?a[0]:null}function z(e){if(!e)return!1;const a=e.toLowerCase();return h.includes(a)}function j(e){if(!e)return!0;const a=e.toLowerCase();return o.has(a)}async function A(e,a,t={}){const{signal:i}=t,m=performance.now(),n=e instanceof ArrayBuffer?new Uint8Array(e):e;if(!(n instanceof Uint8Array)||n.length!==8)throw new Error("Challenge must be exactly 8 bytes");const b=await crypto.subtle.importKey("raw",n,"PBKDF2",!1,["deriveBits"]),u=new Uint8Array(8*a);let l=0;const s=2047,r=new Uint32Array(2);for(let c=0;c<a;c++){if(i?.aborted)throw new DOMException("PoW operation aborted","AbortError");let p;do l++,++r[0]===4294967296&&++r[1],p=new Uint32Array(await crypto.subtle.deriveBits({name:"PBKDF2",salt:r,iterations:128,hash:"SHA-512"},b,32));while(p[0]&s);u.set(new Uint8Array(r.buffer),c*8)}const d=(performance.now()-m)/1e3,g=a*(s+1),f=(l/g).toFixed(1),y=l/((s+1)*d);return console.log(`PoW work=${a} solved in ${d.toFixed(2)}s (${f}x expected ${y.toFixed(1)} work/s)`),u}export{j as a,k as b,v as c,x as g,z as i,A as s,h as w};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.center[data-v-4830d223]{text-align:center}.button-row.center[data-v-4830d223]{display:flex;justify-content:center}.section-body[data-v-4830d223]{gap:1.25rem}.name-edit span[data-v-4830d223]{color:var(--color-text-muted);font-size:.9rem}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as z,o as A,b as c,c as u,d as t,t as f,e as M,g as N,i as $,v as F,L as K,k as i,l as V,z as b,a6 as D,a7 as E,X as I,m as p,a8 as H,H as L,J as U,K as j}from"./_plugin-vue_export-helper-DJsHCwvl.js";const G={class:"app-shell"},J={key:0,class:"global-status",style:{display:"block"}},O={class:"view-root"},X={class:"surface surface--tight",style:{"max-width":"560px",margin:"0 auto",width:"100%"}},Y={class:"view-header",style:{"text-align":"center"}},q={class:"view-lede"},Q={key:0,class:"section-block"},W={key:1,class:"section-block"},Z={key:2,class:"section-block"},ee={class:"section-body"},se={class:"name-edit"},te=["disabled"],ae=["disabled"],ne={__name:"ResetApp",setup(ie){const o=I({show:!1,message:"",type:"info"}),d=i(!0),n=i(!1),l=i(""),x=i(null),g=i(null),m=i(""),y=i("");let v=null;const T=p(()=>g.value?.token_type||"your enrollment"),P=p(()=>d.value?"Preparing your secure enrollment…":h.value?`Finish up ${T.value}. You may edit the name below if needed, and it will be saved to your passkey.`:"This authentication link is no longer valid."),h=p(()=>!!(l.value&&g.value));function r(e,s="info",a=3e3){o.show=!0,o.message=e,o.type=s,v&&clearTimeout(v),a>0&&(v=setTimeout(()=>{o.show=!1},a))}async function R(){try{const e=await V();x.value=e,e?.rp_name&&(document.title=`${e.rp_name} · Passkey Setup`)}catch(e){console.warn("Unable to load settings",e)}}async function S(){if(l.value)try{g.value=await b("/auth/api/token-info",{method:"GET",headers:{Authorization:`Bearer ${l.value}`}}),m.value=g.value.display_name}catch(e){console.error("Failed to load token info",e);const s=e instanceof D?e.data?.detail||"The authentication link is invalid or expired.":E(e);y.value=s}}async function _(){if(!h.value||n.value)return;n.value=!0,r("Starting passkey registration…","info");let e;try{const s=m.value.trim()||null;e=await L.register(l.value,s)}catch(s){n.value=!1;const a=s?.message||"Passkey registration cancelled",w=a==="Passkey registration cancelled";r(w?a:`Registration failed: ${a}`,w?"info":"error",4e3);return}try{await B(e)}catch(s){n.value=!1;const a=s?.message||"Failed to establish session";r(a,"error",4e3);return}r("Passkey registered successfully!","success",800),setTimeout(()=>{n.value=!1,k()},800)}async function B(e){if(!e?.session_token)throw new Error("Registration response missing session_token");return await b("/auth/api/set-session",{method:"POST",headers:{Authorization:`Bearer ${e.session_token}`}})}function k(){const e=H.value||"/auth/";window.location.pathname!==e&&history.replaceState(null,"",e),window.location.reload()}function C(){const e=window.location.pathname.split("/").filter(Boolean);if(!e.length)return"";const s=e[e.length-1],a=e.slice(0,-1);return a.length>1||a.length===1&&a[0]!=="auth"||!s.includes(".")?"":s}return A(async()=>{if(l.value=C(),await R(),!l.value){const e="Reset link is missing or malformed.";y.value=e,r(e,"error",0),d.value=!1;return}await S(),d.value=!1}),(e,s)=>(c(),u("div",G,[o.show?(c(),u("div",J,[t("div",{class:M(["status",o.type])},f(o.message),3)])):N("",!0),t("main",O,[t("div",X,[t("header",Y,[s[1]||(s[1]=t("h1",null,"🔑 Registration",-1)),t("p",q,f(P.value),1)]),d.value?(c(),u("section",Q,[...s[2]||(s[2]=[t("div",{class:"section-body center"},[t("p",null,"Loading reset details…")],-1)])])):h.value?(c(),u("section",Z,[t("div",ee,[t("label",se,[s[3]||(s[3]=t("span",null,"👤 Name",-1)),$(t("input",{type:"text","onUpdate:modelValue":s[0]||(s[0]=a=>m.value=a),disabled:n.value,maxlength:"64",onKeyup:K(_,["enter"])},null,40,te),[[F,m.value]])]),t("button",{class:"btn-primary",disabled:n.value,onClick:_},f(n.value?"Registering…":"Register Passkey"),9,ae)])])):(c(),u("section",W,[t("div",{class:"section-body center"},[t("div",{class:"button-row center",style:{"justify-content":"center"}},[t("button",{class:"btn-secondary",onClick:k},"Return to sign-in")])])]))])])]))}},oe=z(ne,[["__scopeId","data-v-4830d223"]]);U(oe).mount("#app");j();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{o as f,b as w,x as k,u as g,k as y,J as _,K as T}from"./_plugin-vue_export-helper-DJsHCwvl.js";import{a as i,g as v}from"./theme-C2WysaSw.js";import{R as A}from"./RestrictedAuth-DWKMTEV3.js";import"./pow-DUr-T9XX.js";function h(){return new URLSearchParams(location.hash.slice(1)).get("theme")||v()||""}i(h(),".surface");addEventListener("hashchange",()=>i(h(),".surface"));const R={__name:"RestrictedApi",setup(u){const n=y(null);function d(){const a=window.location.pathname.match(/\/auth\/([^/]+)$/);if(a){const r=a[1],c=r.split(".");if(c.length===5&&c.every(l=>l.length>0))return r}return null}const o=new URLSearchParams(window.location.hash.slice(1)),p=["reauth","forbidden"].includes(o.get("mode"))?o.get("mode"):"login";function t(e){window.parent&&window.parent!==window&&window.parent.postMessage(e,"*")}function m(e){t({type:"auth-success",authenticated:!0,sessionToken:e.session_token})}function s(){t({type:"auth-back"})}return f(()=>{n.value=d(),t({type:"auth-ready"}),window.addEventListener("keydown",e=>{e.key==="Escape"&&s()})}),(e,a)=>(w(),k(A,{mode:g(p),"remote-auth-token":n.value,onAuthenticated:m,onBack:s},null,8,["mode","remote-auth-token"]))}};_(R).mount("#app");T();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const l={light:{"color-canvas":"#ffffff","color-surface":"#eff6ff","color-surface-subtle":"#dbeafe","color-border":"#2563eb","color-border-strong":"#1e40af","color-heading":"#1e3a8a","color-text":"#1e293b","color-text-muted":"#475569","color-link":"#1d4ed8","color-link-hover":"#1e40af","color-accent":"#2563eb","color-accent-strong":"#1e40af","color-accent-contrast":"#ffffff","color-success-text":"#166534","color-success-bg":"#dcfce7","color-error-text":"#b91c1c","color-error-bg":"#fee2e2","color-info-text":"#1e40af","color-info-bg":"#dbeafe","color-danger":"#dc2626","shadow-soft":"0 10px 30px rgba(30, 64, 175, 0.15)"},dark:{"color-canvas":"#0f172a","color-surface":"#141b2f","color-surface-subtle":"#1b243b","color-border":"#25304a","color-border-strong":"#3d4d6b","color-heading":"#fff","color-text":"#e2e8f0","color-text-muted":"#94a3b8","color-link":"#60a5fa","color-link-hover":"#93c5fd","color-accent":"#60a5fa","color-accent-strong":"#3b82f6","color-accent-contrast":"#0b1120","color-success-text":"#34d399","color-success-bg":"#1a4d2e","color-error-text":"#fca5a5","color-error-bg":"#4a1f1f","color-info-text":"#bae6fd","color-info-bg":"#1e3a5f","color-danger":"#f87171","shadow-soft":"0 0 0 #000000"}},s="theme-override",a="theme-transition",n="paskia-theme";function f(o,c=":root",t=!1){if(t){let e=document.getElementById(a);e||(e=document.createElement("style"),e.id=a,e.textContent="*, *::before, *::after { transition: background-color 0.3s, color 0.3s, border-color 0.3s, box-shadow 0.3s !important; }",document.head.appendChild(e)),setTimeout(()=>document.getElementById(a)?.remove(),350)}if(document.getElementById(s)?.remove(),o&&l[o]){const e=`${c} { ${Object.entries(l[o]).map(([d,i])=>`--${d}: ${i}`).join("; ")}; }`,r=document.createElement("style");r.id=s,r.textContent=e,document.head.appendChild(r)}}function m(){return localStorage.getItem(n)||""}function b(o){o?localStorage.setItem(n,o):localStorage.removeItem(n)}function u(){f(m())}function g(o,c=!1){const t=o?.user?.theme||"";b(t),f(t,":root",c)}export{f as a,m as g,u as i,g as u};
|
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Auth Profile</title>
|
|
7
|
-
<script type="module" crossorigin src="/auth/assets/auth-
|
|
8
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/_plugin-vue_export-helper-
|
|
7
|
+
<script type="module" crossorigin src="/auth/assets/auth-Pe-PKe8b.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/_plugin-vue_export-helper-DJsHCwvl.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/theme-C2WysaSw.js">
|
|
9
10
|
<link rel="modulepreload" crossorigin href="/auth/assets/helpers-DzjFIx78.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/AccessDenied-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/pow-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/auth/assets/_plugin-vue_export-helper-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/auth/assets/AccessDenied-
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/auth/assets/auth-
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/AccessDenied-Licr0tqA.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/pow-DUr-T9XX.js">
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/_plugin-vue_export-helper-DUBf8-iM.css">
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/AccessDenied-CVQZxSIL.css">
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/auth-B4EpDxom.css">
|
|
15
16
|
</head>
|
|
16
17
|
<body>
|
|
17
18
|
<div id="app"></div>
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<script type="module" crossorigin src="/auth/assets/restricted-
|
|
7
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/_plugin-vue_export-helper-
|
|
8
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/
|
|
10
|
-
<link rel="
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/auth/assets/
|
|
6
|
+
<script type="module" crossorigin src="/auth/assets/restricted-C9cJlHkd.js"></script>
|
|
7
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/_plugin-vue_export-helper-DJsHCwvl.js">
|
|
8
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/theme-C2WysaSw.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/pow-DUr-T9XX.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/RestrictedAuth-DWKMTEV3.js">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/_plugin-vue_export-helper-DUBf8-iM.css">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/RestrictedAuth-0MFeNWS2.css">
|
|
12
13
|
</head>
|
|
13
14
|
<body>
|
|
14
15
|
<div id="app"></div>
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Access Restricted</title>
|
|
7
|
-
<script type="module" crossorigin src="/auth/assets/forward-
|
|
8
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/_plugin-vue_export-helper-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/pow-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/RestrictedAuth-
|
|
7
|
+
<script type="module" crossorigin src="/auth/assets/forward-BC0p23CH.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/_plugin-vue_export-helper-DJsHCwvl.js">
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/pow-DUr-T9XX.js">
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/RestrictedAuth-DWKMTEV3.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/auth/assets/helpers-DzjFIx78.js">
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/auth/assets/_plugin-vue_export-helper-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/auth/assets/RestrictedAuth-
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/_plugin-vue_export-helper-DUBf8-iM.css">
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/RestrictedAuth-0MFeNWS2.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="app"></div>
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Complete Passkey Setup</title>
|
|
7
|
-
<script type="module" crossorigin src="/auth/assets/reset-
|
|
8
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/_plugin-vue_export-helper-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/auth/assets/_plugin-vue_export-helper-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/auth/assets/reset-
|
|
7
|
+
<script type="module" crossorigin src="/auth/assets/reset-CkY9h28U.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/_plugin-vue_export-helper-DJsHCwvl.js">
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/_plugin-vue_export-helper-DUBf8-iM.css">
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/reset-B8PlNXuP.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="app"></div>
|
|
Binary file
|
paskia/util/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""API response utilities using msgspec for JSON serialization.
|
|
2
|
+
|
|
3
|
+
msgspec handles UUID and datetime conversion automatically.
|
|
4
|
+
API structs inherit from db structs with kw_only=True to add uuid/key fields.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import UTC, datetime
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
|
|
10
|
+
import msgspec
|
|
11
|
+
|
|
12
|
+
from paskia.db.structs import Org, Permission, Role, User
|
|
13
|
+
from paskia.util import useragent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _utc_datetime(dt: datetime | None) -> datetime | None:
|
|
17
|
+
"""Convert datetime to UTC, handling both aware and naive datetimes."""
|
|
18
|
+
if dt is None:
|
|
19
|
+
return None
|
|
20
|
+
if dt.tzinfo:
|
|
21
|
+
return dt.astimezone(UTC)
|
|
22
|
+
return dt.replace(tzinfo=UTC)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def format_datetime(dt: datetime | None) -> str | None:
|
|
26
|
+
"""Format a datetime to ISO 8601 string with Z suffix for UTC."""
|
|
27
|
+
if dt is None:
|
|
28
|
+
return None
|
|
29
|
+
utc_dt = _utc_datetime(dt)
|
|
30
|
+
return utc_dt.isoformat().replace("+00:00", "Z") if utc_dt else None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# -------------------------------------------------------------------------
|
|
34
|
+
# API structs - inherit from db structs, add uuid for serialization
|
|
35
|
+
# -------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ApiUser(User, kw_only=True):
|
|
39
|
+
"""User with uuid serialized."""
|
|
40
|
+
|
|
41
|
+
uuid: UUID
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def from_db(cls, u: User) -> "ApiUser":
|
|
45
|
+
return cls(uuid=u.uuid, **msgspec.structs.asdict(u))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ApiOrg(Org, kw_only=True):
|
|
49
|
+
"""Org with uuid serialized."""
|
|
50
|
+
|
|
51
|
+
uuid: UUID
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def from_db(cls, o: Org) -> "ApiOrg":
|
|
55
|
+
return cls(uuid=o.uuid, **msgspec.structs.asdict(o))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ApiRole(Role, kw_only=True):
|
|
59
|
+
"""Role with uuid serialized."""
|
|
60
|
+
|
|
61
|
+
uuid: UUID
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def from_db(cls, r: Role) -> "ApiRole":
|
|
65
|
+
return cls(uuid=r.uuid, **msgspec.structs.asdict(r))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ApiPermission(Permission, kw_only=True):
|
|
69
|
+
"""Permission with uuid serialized."""
|
|
70
|
+
|
|
71
|
+
uuid: UUID
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def from_db(cls, p: Permission) -> "ApiPermission":
|
|
75
|
+
return cls(uuid=p.uuid, **msgspec.structs.asdict(p))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ApiSession(msgspec.Struct):
|
|
79
|
+
"""Session for API responses with computed fields."""
|
|
80
|
+
|
|
81
|
+
id: str
|
|
82
|
+
credential_uuid: UUID = msgspec.field(name="credential")
|
|
83
|
+
host: str
|
|
84
|
+
ip: str
|
|
85
|
+
user_agent: str
|
|
86
|
+
last_renewed: datetime
|
|
87
|
+
is_current: bool = False
|
|
88
|
+
is_current_host: bool = False
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def from_db(
|
|
92
|
+
cls,
|
|
93
|
+
s, # Session
|
|
94
|
+
*,
|
|
95
|
+
current_key: str,
|
|
96
|
+
normalized_host: str | None,
|
|
97
|
+
expires_delta, # timedelta
|
|
98
|
+
) -> "ApiSession":
|
|
99
|
+
return cls(
|
|
100
|
+
id=s.key,
|
|
101
|
+
credential_uuid=s.credential_uuid,
|
|
102
|
+
host=s.host,
|
|
103
|
+
ip=s.ip,
|
|
104
|
+
user_agent=useragent.compact_user_agent(s.user_agent),
|
|
105
|
+
last_renewed=s.expiry - expires_delta,
|
|
106
|
+
is_current=s.key == current_key,
|
|
107
|
+
is_current_host=bool(
|
|
108
|
+
normalized_host and s.host and s.host == normalized_host
|
|
109
|
+
),
|
|
110
|
+
)
|