paskia 0.7.1__py3-none-any.whl → 0.8.0__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.
Files changed (40) hide show
  1. paskia/_version.py +2 -2
  2. paskia/authsession.py +12 -49
  3. paskia/bootstrap.py +30 -25
  4. paskia/db/__init__.py +163 -401
  5. paskia/db/background.py +128 -0
  6. paskia/db/jsonl.py +132 -0
  7. paskia/db/operations.py +1241 -0
  8. paskia/db/structs.py +148 -0
  9. paskia/fastapi/admin.py +456 -215
  10. paskia/fastapi/api.py +16 -15
  11. paskia/fastapi/authz.py +7 -2
  12. paskia/fastapi/mainapp.py +2 -1
  13. paskia/fastapi/remote.py +20 -20
  14. paskia/fastapi/reset.py +9 -10
  15. paskia/fastapi/user.py +10 -18
  16. paskia/fastapi/ws.py +22 -19
  17. paskia/frontend-build/auth/admin/index.html +3 -3
  18. paskia/frontend-build/auth/assets/AccessDenied-aTdCvz9k.js +8 -0
  19. paskia/frontend-build/auth/assets/admin-BeNu48FR.css +1 -0
  20. paskia/frontend-build/auth/assets/admin-tVs8oyLv.js +1 -0
  21. paskia/frontend-build/auth/assets/{auth-BU_O38k2.css → auth-BKX7shEe.css} +1 -1
  22. paskia/frontend-build/auth/assets/auth-Dk3q4pNS.js +1 -0
  23. paskia/frontend-build/auth/index.html +3 -3
  24. paskia/globals.py +7 -10
  25. paskia/migrate/__init__.py +274 -0
  26. paskia/migrate/sql.py +381 -0
  27. paskia/util/permutil.py +16 -5
  28. paskia/util/sessionutil.py +3 -2
  29. paskia/util/userinfo.py +12 -26
  30. paskia-0.8.0.dist-info/METADATA +94 -0
  31. {paskia-0.7.1.dist-info → paskia-0.8.0.dist-info}/RECORD +33 -29
  32. {paskia-0.7.1.dist-info → paskia-0.8.0.dist-info}/entry_points.txt +1 -0
  33. paskia/db/sql.py +0 -1424
  34. paskia/frontend-build/auth/assets/AccessDenied-C-lL9vbN.js +0 -8
  35. paskia/frontend-build/auth/assets/admin-Cs6Mg773.css +0 -1
  36. paskia/frontend-build/auth/assets/admin-Df5_Damp.js +0 -1
  37. paskia/frontend-build/auth/assets/auth-Df3pjeSS.js +0 -1
  38. paskia/util/tokens.py +0 -44
  39. paskia-0.7.1.dist-info/METADATA +0 -22
  40. {paskia-0.7.1.dist-info → paskia-0.8.0.dist-info}/WHEEL +0 -0
@@ -1,8 +0,0 @@
1
- import{M as At,r as F,N as Rt,O as Yt,P as le,Q as Tt,R as Qt,S as Gt,T as Wt,n as ze,U as Zt,c as x,w as He,V as Xt,W as en,H as Q,m as tn,X as nn,Y as on,b as R,f as L,z as U,d as T,e as v,t as D,g as se,_ as Y,F as oe,i as ce,B as z,D as Mt,l as de,Z as Nt,$ as Pt,a0 as Dt,a1 as Lt,o as qt,a2 as rn,a as Ft,C as sn,k as xe,h as an,a3 as un,L as ln,x as cn}from"./_plugin-vue_export-helper-rKFEraYH.js";import{f as G,h as ae,g as We}from"./helpers-DzjFIx78.js";let Ut;const fe=e=>Ut=e,Kt=Symbol();function Oe(e){return e&&typeof e=="object"&&Object.prototype.toString.call(e)==="[object Object]"&&typeof e.toJSON!="function"}var re;(function(e){e.direct="direct",e.patchObject="patch object",e.patchFunction="patch function"})(re||(re={}));function br(){const e=At(!0),s=e.run(()=>F({}));let r=[],t=[];const n=Rt({install(o){fe(n),n._a=o,o.provide(Kt,n),o.config.globalProperties.$pinia=n,t.forEach(i=>r.push(i)),t=[]},use(o){return this._a?r.push(o):t.push(o),this},_p:r,_a:null,_e:e,_s:new Map,state:s});return n}const Vt=()=>{};function Ze(e,s,r,t=Vt){e.add(s);const n=()=>{e.delete(s)&&t()};return!r&&Gt()&&Wt(n),n}function ee(e,...s){e.forEach(r=>{r(...s)})}const dn=e=>e(),Xe=Symbol(),pe=Symbol();function je(e,s){e instanceof Map&&s instanceof Map?s.forEach((r,t)=>e.set(t,r)):e instanceof Set&&s instanceof Set&&s.forEach(e.add,e);for(const r in s){if(!s.hasOwnProperty(r))continue;const t=s[r],n=e[r];Oe(n)&&Oe(t)&&e.hasOwnProperty(r)&&!le(t)&&!Tt(t)?e[r]=je(n,t):e[r]=t}return e}const fn=Symbol();function hn(e){return!Oe(e)||!Object.prototype.hasOwnProperty.call(e,fn)}const{assign:J}=Object;function gn(e){return!!(le(e)&&e.effect)}function mn(e,s,r,t){const{state:n,actions:o,getters:i}=s,a=r.state.value[e];let u;function l(){a||(r.state.value[e]=n?n():{});const f=Zt(r.state.value[e]);return J(f,o,Object.keys(i||{}).reduce((w,b)=>(w[b]=Rt(x(()=>{fe(r);const h=r._s.get(e);return i[b].call(h,h)})),w),{}))}return u=Ht(e,l,s,r,t,!0),u}function Ht(e,s,r={},t,n,o){let i;const a=J({actions:{}},r),u={deep:!0};let l,f,w=new Set,b=new Set,h;const d=t.state.value[e];!o&&!d&&(t.state.value[e]={}),F({});let g;function N(y){let m;l=f=!1,typeof y=="function"?(y(t.state.value[e]),m={type:re.patchFunction,storeId:e,events:h}):(je(t.state.value[e],y),m={type:re.patchObject,payload:y,storeId:e,events:h});const _=g=Symbol();ze().then(()=>{g===_&&(l=!0)}),f=!0,ee(w,m,t.state.value[e])}const A=o?function(){const{state:m}=r,_=m?m():{};this.$patch(E=>{J(E,_)})}:Vt;function P(){i.stop(),w.clear(),b.clear(),t._s.delete(e)}const c=(y,m="")=>{if(Xe in y)return y[pe]=m,y;const _=function(){fe(t);const E=Array.from(arguments),k=new Set,B=new Set;function M(q){k.add(q)}function V(q){B.add(q)}ee(b,{args:E,name:_[pe],store:I,after:M,onError:V});let K;try{K=y.apply(this&&this.$id===e?this:I,E)}catch(q){throw ee(B,q),q}return K instanceof Promise?K.then(q=>(ee(k,q),q)).catch(q=>(ee(B,q),Promise.reject(q))):(ee(k,K),K)};return _[Xe]=!0,_[pe]=m,_},S={_p:t,$id:e,$onAction:Ze.bind(null,b),$patch:N,$reset:A,$subscribe(y,m={}){const _=Ze(w,y,m.detached,()=>E()),E=i.run(()=>He(()=>t.state.value[e],k=>{(m.flush==="sync"?f:l)&&y({storeId:e,type:re.direct,events:h},k)},J({},u,m)));return _},$dispose:P},I=Yt(S);t._s.set(e,I);const p=(t._a&&t._a.runWithContext||dn)(()=>t._e.run(()=>(i=At()).run(()=>s({action:c}))));for(const y in p){const m=p[y];if(le(m)&&!gn(m)||Tt(m))o||(d&&hn(m)&&(le(m)?m.value=d[y]:je(m,d[y])),t.state.value[e][y]=m);else if(typeof m=="function"){const _=c(m,y);p[y]=_,a.actions[y]=m}}return J(I,p),J(Qt(I),p),Object.defineProperty(I,"$state",{get:()=>t.state.value[e],set:y=>{N(m=>{J(m,y)})}}),t._p.forEach(y=>{J(I,i.run(()=>y({store:I,app:t._a,pinia:t,options:a})))}),d&&o&&r.hydrate&&r.hydrate(I.$state,d),l=!0,f=!0,I}function yn(e,s,r){let t;const n=typeof s=="function";t=n?r:s;function o(i,a){const u=en();return i=i||(u?Xt(Kt,null):null),i&&fe(i),i=Ut,i._s.has(e)||(n?Ht(e,s,t,i):mn(e,t,i)),i._s.get(e)}return o.$id=e,o}const $e=yn("auth",{state:()=>({userInfo:null,isLoading:!1,settings:null,currentView:"login",status:{message:"",type:"info",show:!1}}),getters:{},actions:{setLoading(e){this.isLoading=!!e},showMessage(e,s="info",r=3e3){this.status={message:e,type:s,show:!0},r>0&&setTimeout(()=>{this.status.show=!1},r)},async setSessionCookie(e){if(!e?.session_token)throw console.error("setSessionCookie called with missing session_token:",e),new Error("Authentication response missing session_token");return await Q("/auth/api/set-session",{method:"POST",headers:{Authorization:`Bearer ${e.session_token}`}})},async register(){this.isLoading=!0;try{const e=await on();return await this.setSessionCookie(e),await this.loadUserInfo(),this.selectView(),e}finally{this.isLoading=!1}},async authenticate(){this.isLoading=!0;try{const e=await nn();return await this.setSessionCookie(e),await this.loadUserInfo(),this.selectView(),e}finally{this.isLoading=!1}},selectView(){this.userInfo?this.currentView="profile":this.currentView="login"},async loadSettings(){this.settings=await tn()},async loadUserInfo(){try{this.userInfo=await Q("/auth/api/user-info",{method:"POST"}),console.log("User info loaded:",this.userInfo)}catch(e){throw e.status===401||e.status===403?console.log("Authentication required:",e.message):this.showMessage(e.message||"Failed to load user info","error",5e3),e}},async deleteCredential(e){await Q(`/auth/api/user/credential/${e}`,{method:"DELETE"}),await this.loadUserInfo()},async terminateSession(e){try{if((await Q(`/auth/api/user/session/${e}`,{method:"DELETE"}))?.current_session_terminated){sessionStorage.clear(),location.reload();return}await this.loadUserInfo(),this.showMessage("Session terminated","success",2500)}catch(s){throw console.error("Terminate session error:",s),s}},async logout(){try{await Q("/auth/api/logout",{method:"POST"}),sessionStorage.clear(),location.reload()}catch(e){console.error("Logout error:",e),e.status!==401&&e.status!==403&&this.showMessage(e.message,"error")}},async logoutEverywhere(){try{await Q("/auth/api/user/logout-all",{method:"POST"}),sessionStorage.clear(),location.reload()}catch(e){console.error("Logout-all error:",e),e.status!==401&&e.status!==403&&this.showMessage(e.message,"error")}}}}),pn={key:0,class:"global-status",style:{display:"block"}},vr={__name:"StatusMessage",setup(e){const s=$e();return(r,t)=>U(s).status.show?(T(),R("div",pn,[v("div",{class:se(["status",U(s).status.type])},D(U(s).status.message),3)])):L("",!0)}},wn=["href"],bn={key:0,class:"sep"},vn={__name:"Breadcrumbs",props:{entries:{type:Array,default:()=>[]},showHome:{type:Boolean,default:!0},homeHref:{type:String,default:"/"}},setup(e,{expose:s}){const r=e,t=F(null),n=x(()=>r.showHome&&r.entries.length>0&&r.entries[0].href===r.homeHref?[{label:"🏠 "+r.entries[0].label,href:r.homeHref},...r.entries.slice(1)]:[...r.showHome?[{label:"🏠",href:r.homeHref}]:[],...r.entries]),o=x(()=>{const l=window.location.hash||window.location.pathname;for(let f=n.value.length-1;f>=0;f--){const w=n.value[f].href;if(w===l||w&&l.startsWith(w))return f}return n.value.length-1});function i(l){if(l.target===t.value){const f=t.value.querySelectorAll("a"),w=Math.min(o.value,f.length-1);f[w]&&f[w].focus()}}function a(l){const f=z(l);f&&(f==="left"||f==="right")&&(l.preventDefault(),Mt(t.value,l.target,f,{itemSelector:"a"}))}function u(){const l=t.value?.querySelectorAll("a");if(l?.length){const f=Math.min(o.value,l.length-1);l[f]?.focus()}}return s({focusCurrent:u}),(l,f)=>n.value.length>1?(T(),R("nav",{key:0,ref_key:"navRef",ref:t,class:"breadcrumbs","aria-label":"Breadcrumb",tabindex:"0",onFocusin:i,onKeydown:a},[v("ol",null,[(T(!0),R(oe,null,ce(n.value,(w,b)=>(T(),R("li",{key:b},[v("a",{href:w.href,tabindex:"-1"},D(w.label),9,wn),b<n.value.length-1?(T(),R("span",bn," — ")):L("",!0)]))),128))])],544)):L("",!0)}},Cr=Y(vn,[["__scopeId","data-v-6344dbb8"]]),Cn={key:0},Sn={key:1},_n=["onFocusin","onKeydown"],En={class:"item-top"},kn={class:"item-icon"},Bn=["src","alt"],In={key:1,class:"auth-emoji"},An={class:"item-title"},Rn={class:"item-actions"},Tn={key:0,class:"badge badge-current"},Mn={key:1,class:"badge badge-current"},Nn={key:2,class:"badge badge-current"},Pn=["onClick","disabled","title"],Dn={class:"item-details"},Ln={class:"credential-dates"},qn={class:"date-value"},Fn={class:"date-value"},Un={class:"date-value"},Sr={__name:"CredentialList",props:{credentials:{type:Array,default:()=>[]},aaguidInfo:{type:Object,default:()=>({})},loading:{type:Boolean,default:!1},allowDelete:{type:Boolean,default:!1},hoveredCredentialUuid:{type:String,default:null},hoveredSessionCredentialUuid:{type:String,default:null},navigationDisabled:{type:Boolean,default:!1}},emits:["delete","credentialHover","navigate-out"],setup(e,{emit:s}){const r=e,t=s,n=h=>{t("credentialHover",h)},o=h=>{h.currentTarget.contains(h.relatedTarget)||t("credentialHover",null)},i=h=>{h.currentTarget.matches(":focus")||(h.currentTarget.focus(),h.stopPropagation())},a=(h,d)=>{Pt(h,()=>{r.allowDelete&&!d.is_current_session&&t("delete",d)})},u=h=>{if(r.navigationDisabled)return;const d=h.currentTarget;if(h.target===d){const g=d.querySelector(".credential-item");g&&g.focus()}},l=h=>{r.navigationDisabled||Dt(h,d=>t("navigate-out",d))},f=(h,d)=>{if(a(h,d),h.defaultPrevented||r.navigationDisabled)return;const g=z(h);if(g){h.preventDefault();const N=h.currentTarget.closest(".credential-list");Nt(N,h.currentTarget,g,{itemSelector:".credential-item"})==="boundary"&&t("navigate-out",g)}},w=h=>{const d=r.aaguidInfo?.[h.aaguid];return d?d.name:"Unknown Authenticator"},b=h=>{const d=r.aaguidInfo?.[h.aaguid];if(!d)return null;const N=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"icon_dark":"icon_light";return d[N]||null};return(h,d)=>(T(),R("div",{class:"credential-list",tabindex:"0",onFocusin:u,onKeydown:l},[e.loading?(T(),R("div",Cn,[...d[2]||(d[2]=[v("p",null,"Loading credentials...",-1)])])):e.credentials?.length?(T(!0),R(oe,{key:2},ce(e.credentials,g=>(T(),R("div",{key:g.credential_uuid,class:se(["credential-item",{"current-session":g.is_current_session&&!e.hoveredCredentialUuid&&!e.hoveredSessionCredentialUuid,"is-hovered":e.hoveredCredentialUuid===g.credential_uuid,"is-linked-session":e.hoveredSessionCredentialUuid===g.credential_uuid}]),tabindex:"-1",onMousedown:d[0]||(d[0]=de(()=>{},["prevent"])),onClickCapture:i,onFocusin:N=>n(g.credential_uuid),onFocusout:d[1]||(d[1]=N=>o(N)),onKeydown:N=>f(N,g)},[v("div",En,[v("div",kn,[b(g)?(T(),R("img",{key:0,src:b(g),alt:w(g),class:"auth-icon",width:"32",height:"32"},null,8,Bn)):(T(),R("span",In,"🔑"))]),v("h4",An,D(w(g)),1),v("div",Rn,[g.is_current_session&&!e.hoveredCredentialUuid&&!e.hoveredSessionCredentialUuid?(T(),R("span",Tn,"Current")):e.hoveredCredentialUuid===g.credential_uuid?(T(),R("span",Mn,"Selected")):e.hoveredSessionCredentialUuid===g.credential_uuid?(T(),R("span",Nn,"Linked")):L("",!0),e.allowDelete?(T(),R("button",{key:3,onClick:N=>h.$emit("delete",g),class:"btn-card-delete",disabled:g.is_current_session,title:g.is_current_session?"Cannot delete current session credential":"Delete passkey and terminate any linked sessions.",tabindex:"-1"},"❌",8,Pn)):L("",!0)])]),v("div",Dn,[v("div",Ln,[d[4]||(d[4]=v("span",{class:"date-label"},"Created:",-1)),v("span",qn,D(U(G)(g.created_at)),1),d[5]||(d[5]=v("span",{class:"date-label"},"Last used:",-1)),v("span",Fn,D(U(G)(g.last_used)),1),d[6]||(d[6]=v("span",{class:"date-label"},"Last verified:",-1)),v("span",Un,D(U(G)(g.last_verified)),1)])])],42,_n))),128)):(T(),R("div",Sn,[...d[3]||(d[3]=[v("p",null,"No passkeys found.",-1)])]))],32))}},Kn={class:"user-name-heading"},Vn={class:"user-name-row"},Hn=["title"],On={key:0,class:"org-role-sub"},jn={key:0,class:"org-line"},zn={key:1,class:"role-line"},xn={class:"user-details"},$n={class:"date-value"},Jn={class:"date-value"},Yn={class:"date-value"},Qn={key:1,class:"user-info-extra"},Gn={__name:"UserBasicInfo",props:{name:{type:String,required:!0},visits:{type:[Number,String],default:0},createdAt:{type:[String,Number,Date],default:null},lastSeen:{type:[String,Number,Date],default:null},updateEndpoint:{type:String,default:null},canEdit:{type:Boolean,default:!0},loading:{type:Boolean,default:!1},orgDisplayName:{type:String,default:""},roleName:{type:String,default:""}},emits:["saved","editName"],setup(e,{emit:s}){const r=e,t=s;$e();const n=x(()=>!!r.name);return(o,i)=>n.value?(T(),R("div",{key:0,class:se(["user-info",{"has-extra":o.$slots.default}])},[v("h3",Kn,[i[1]||(i[1]=v("span",{class:"icon"},"👤",-1)),v("span",Vn,[v("span",{class:"display-name",title:e.name},D(e.name),9,Hn),e.canEdit&&e.updateEndpoint?(T(),R("button",{key:0,class:"mini-btn",onClick:i[0]||(i[0]=a=>t("editName")),title:"Edit name"},"✏️")):L("",!0)])]),e.orgDisplayName||e.roleName?(T(),R("div",On,[e.orgDisplayName?(T(),R("div",jn,D(e.orgDisplayName),1)):L("",!0),e.roleName?(T(),R("div",zn,D(e.roleName),1)):L("",!0)])):L("",!0),v("div",xn,[i[2]||(i[2]=v("span",{class:"date-label"},[v("strong",null,"Visits:")],-1)),v("span",$n,D(e.visits||0),1),i[3]||(i[3]=v("span",{class:"date-label"},[v("strong",null,"Registered:")],-1)),v("span",Jn,D(U(G)(e.createdAt)),1),i[4]||(i[4]=v("span",{class:"date-label"},[v("strong",null,"Last seen:")],-1)),v("span",Yn,D(U(G)(e.lastSeen)),1)]),o.$slots.default?(T(),R("div",Qn,[Lt(o.$slots,"default",{},void 0)])):L("",!0)],2)):L("",!0)}},_r=Y(Gn,[["__scopeId","data-v-ce373d6c"]]),Wn={__name:"Modal",props:{focusFallback:{type:[HTMLElement,Object],default:null},focusIndex:{type:Number,default:-1},focusSiblingSelector:{type:String,default:""}},emits:["close"],setup(e){const s=e,r=F(null),t=F(null),n=()=>{const i=t.value;if(!i)return;if(document.body.contains(i)&&!i.disabled){i.focus();return}if(s.focusSiblingSelector&&s.focusIndex>=0){const u=[s.focusFallback?.$el||s.focusFallback,i.closest("[data-nav-group]"),i.parentElement?.closest("section"),document.querySelector(".view-root")].filter(Boolean);for(const l of u){if(!l)continue;const f=l.querySelectorAll(s.focusSiblingSelector);if(f.length>0){const w=Math.min(s.focusIndex,f.length-1),b=f[w];if(b&&!b.disabled){b.focus();return}}}}const a=s.focusFallback?.$el||s.focusFallback;if(a&&document.body.contains(a)){const u=a.querySelector?.('button:not([disabled]), a, [tabindex="0"]')||a;if(u?.focus){u.focus();return}}},o=i=>{const a=z(i);if(!a)return;const u=i.target,l=u.closest(".modal-actions");if(l&&(a==="left"||a==="right"))i.preventDefault(),Mt(l,u,a,{itemSelector:"button"});else if(a==="up"&&l){i.preventDefault();const w=(l.closest("form")||l.closest(".modal-form"))?.querySelectorAll("input, textarea, select, button:not(.modal-actions button)");w&&w.length>0&&w[w.length-1].focus()}else if(a==="down"&&!l){const f=u.closest("form")||u.closest(".modal-form");if(f){i.preventDefault();const w=f.querySelector(".modal-actions");w&&sn(w,{primarySelector:".btn-primary",itemSelector:"button"})}}};return qt(()=>{t.value=document.activeElement,ze(()=>{if(r.value){r.value.showModal();const i=r.value.querySelector(".modal-actions .btn-primary");i&&i.setAttribute("data-nav-primary",""),rn(r.value)}})}),Ft(()=>{n()}),(i,a)=>(T(),R("dialog",{ref_key:"dialog",ref:r,onClose:a[0]||(a[0]=u=>i.$emit("close")),onKeydown:o},[Lt(i.$slots,"default",{},void 0)],544))}},Er=Y(Wn,[["__scopeId","data-v-2ebcbb0a"]]),Zn={class:"name-edit-form"},Xn=["for"],eo=["id","type","placeholder","disabled"],to={key:0,class:"error small"},no=["disabled"],oo=["disabled"],ro={__name:"NameEditForm",props:{modelValue:{type:String,default:""},label:{type:String,default:"Name"},placeholder:{type:String,default:""},submitText:{type:String,default:"Save"},cancelText:{type:String,default:"Cancel"},busy:{type:Boolean,default:!1},error:{type:String,default:""},autoFocus:{type:Boolean,default:!0},autoSelect:{type:Boolean,default:!0},inputId:{type:String,default:null},inputType:{type:String,default:"text"}},emits:["update:modelValue","cancel"],setup(e,{emit:s}){const r=e,t=s,n=F(null),o=`name-edit-${Math.random().toString(36).slice(2,10)}`,i=x({get:()=>r.modelValue,set:f=>t("update:modelValue",f)}),a=x(()=>r.inputId||o),u=f=>{if(z(f)==="up"){f.preventDefault(),n.value?.focus();return}};function l(){t("cancel")}return(f,w)=>(T(),R("div",Zn,[v("label",{for:a.value},[xe(D(e.label)+" ",1),an(v("input",{id:a.value,ref_key:"inputRef",ref:n,type:e.inputType,placeholder:e.placeholder,"onUpdate:modelValue":w[0]||(w[0]=b=>i.value=b),disabled:e.busy,required:""},null,8,eo),[[un,i.value]])],8,Xn),e.error?(T(),R("div",to,D(e.error),1)):L("",!0),v("div",{class:"modal-actions",onKeydown:u},[v("button",{type:"button",class:"btn-secondary",onClick:l,disabled:e.busy},D(e.cancelText),9,no),v("button",{type:"submit",class:"btn-primary",disabled:e.busy,"data-nav-primary":""},D(e.submitText),9,oo)],32)]))}},kr=Y(ro,[["__scopeId","data-v-b73321cf"]]),so={class:"section-block","data-component":"session-list-section"},io={class:"section-header"},ao={class:"section-description"},uo={class:"section-body"},lo=["onKeydown"],co=["href"],fo={class:"session-list"},ho=["onFocusin","onKeydown"],go={class:"item-top"},mo={class:"item-title"},yo={class:"item-actions"},po={key:0,class:"badge badge-current"},wo={key:1,class:"badge badge-current"},bo={key:2,class:"badge badge-current"},vo={key:3,class:"badge"},Co=["onClick","disabled","title"],So={class:"item-details"},_o={class:"session-dates"},Eo={class:"date-label"},ko=["onClick"],Bo={key:1,class:"empty-state"},Br={__name:"SessionList",props:{sessions:{type:Array,default:()=>[]},emptyMessage:{type:String,default:"You currently have no other active sessions."},sectionDescription:{type:String,default:"Review where you're signed in and end any sessions you no longer recognize."},terminatingSessions:{type:Object,default:()=>({})},hoveredCredentialUuid:{type:String,default:null},navigationDisabled:{type:Boolean,default:!1}},emits:["terminate","sessionHover","navigate-out"],setup(e,{emit:s}){const r=e,t=s,n=$e(),o=F(null),i=F(null),a=c=>{i.value=c,o.value=c.ip||null,t("sessionHover",c)},u=c=>{c.currentTarget.contains(c.relatedTarget)||(i.value=null,o.value=null,t("sessionHover",null))},l=c=>{c.currentTarget.matches(":focus")||(c.currentTarget.focus(),c.stopPropagation())},f=c=>!!r.terminatingSessions[c],w=(c,S)=>{const I=c.currentTarget,p=I.querySelector(".session-list")?.querySelectorAll(".session-item"),y=Array.from(document.querySelectorAll(".session-group")),m=y.indexOf(I);if(c.key==="Enter"&&c.target===I){S&&I.querySelector("a")?.click();return}if(r.navigationDisabled)return;const _=z(c);if(["down","right"].includes(_)&&c.target===I){c.preventDefault(),p?.[0]?.focus();return}if(["up","left"].includes(_)&&c.target===I){c.preventDefault(),m>0?y[m-1].focus():t("navigate-out","up");return}Dt(c,E=>t("navigate-out",E))},b=(c,S)=>{if(Pt(c,()=>{f(S.id)||t("terminate",S)}),c.defaultPrevented||r.navigationDisabled)return;const I=z(c);if(I){c.preventDefault();const C=c.currentTarget.closest(".session-group"),p=C.querySelector(".session-list");if(Nt(p,c.currentTarget,I,{itemSelector:".session-item"})==="boundary"){if(I==="left"||I==="up")C?.focus();else if(I==="down"||I==="right"){const m=Array.from(document.querySelectorAll(".session-group")),_=m.indexOf(C);_<m.length-1?m[_+1].focus():t("navigate-out","down")}}}c.key==="Escape"&&(c.preventDefault(),c.currentTarget.closest(".session-group")?.focus())},h=c=>`${c.includes(":")?"http":"https"}://${c}`,d=async c=>{if(c)try{await navigator.clipboard.writeText(c),n.showMessage("Full IP copied to clipboard!","success",2e3)}catch(S){console.error("Failed to copy IP:",S),n.showMessage("Failed to copy IP","error",3e3)}},g=c=>ae(c)??c,N=x(()=>{if(o.value)return ae(o.value);const c=r.sessions.find(S=>S.is_current);return c?ae(c.ip):null}),A=c=>N.value&&ae(c)===N.value,P=x(()=>{const c={};for(const p of r.sessions){const y=p.host||"";c[y]||(c[y]={sessions:[],isCurrentSite:!1}),c[y].sessions.push(p),p.is_current_host&&(c[y].isCurrentSite=!0)}for(const p in c)c[p].sessions.sort((y,m)=>new Date(m.last_renewed)-new Date(y.last_renewed));const S=new Intl.Collator(void 0,{numeric:!0,sensitivity:"base"}),I=Object.keys(c).sort(S.compare),C={};for(const p of I)C[p]=c[p];return C});return(c,S)=>(T(),R("section",so,[v("div",io,[S[2]||(S[2]=v("h2",null,"Active Sessions",-1)),v("p",ao,D(e.sectionDescription),1)]),v("div",uo,[v("div",null,[Array.isArray(e.sessions)&&e.sessions.length?(T(!0),R(oe,{key:0},ce(P.value,(I,C)=>(T(),R("div",{key:C,class:"session-group",tabindex:"0",onKeydown:p=>w(p,C)},[v("span",{class:se(["session-group-host",{"is-current-site":I.isCurrentSite}])},[S[3]||(S[3]=v("span",{class:"session-group-icon"},"🌐",-1)),C?(T(),R("a",{key:0,href:h(C),tabindex:"-1",target:"_blank",rel:"noopener noreferrer"},D(C),9,co)):(T(),R(oe,{key:1},[xe("Unbound host")],64))],2),v("div",fo,[(T(!0),R(oe,null,ce(I.sessions,p=>(T(),R("div",{key:p.id,class:se(["session-item",{"is-current":p.is_current&&!o.value&&!e.hoveredCredentialUuid,"is-hovered":i.value?.id===p.id,"is-linked-credential":e.hoveredCredentialUuid===p.credential_uuid}]),tabindex:"-1",onMousedown:S[0]||(S[0]=de(()=>{},["prevent"])),onClickCapture:l,onFocusin:y=>a(p),onFocusout:S[1]||(S[1]=y=>u(y)),onKeydown:y=>b(y,p)},[v("div",go,[v("h4",mo,D(p.user_agent),1),v("div",yo,[p.is_current&&!o.value&&!e.hoveredCredentialUuid?(T(),R("span",po,"Current")):i.value?.id===p.id?(T(),R("span",wo,"Selected")):e.hoveredCredentialUuid===p.credential_uuid?(T(),R("span",bo,"Linked")):!e.hoveredCredentialUuid&&A(p.ip)?(T(),R("span",vo,"Same IP")):L("",!0),v("button",{onClick:y=>c.$emit("terminate",p),class:"btn-card-delete",disabled:f(p.id),title:f(p.id)?"Terminating...":"Terminate session",tabindex:"-1"},"❌",8,Co)])]),v("div",So,[v("div",_o,[v("span",Eo,D(U(G)(p.last_renewed)),1),v("span",{class:"date-value",onClick:y=>d(p.ip),title:"Click to copy full IP"},D(g(p.ip)),9,ko)])])],42,ho))),128))])],40,lo))),128)):(T(),R("div",Bo,[v("p",null,D(e.emptyMessage),1)]))])])]))}};function Io(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var te={},we,et;function Ao(){return et||(et=1,we=function(){return typeof Promise=="function"&&Promise.prototype&&Promise.prototype.then}),we}var be={},$={},tt;function W(){if(tt)return $;tt=1;let e;const s=[0,26,44,70,100,134,172,196,242,292,346,404,466,532,581,655,733,815,901,991,1085,1156,1258,1364,1474,1588,1706,1828,1921,2051,2185,2323,2465,2611,2761,2876,3034,3196,3362,3532,3706];return $.getSymbolSize=function(t){if(!t)throw new Error('"version" cannot be null or undefined');if(t<1||t>40)throw new Error('"version" should be in range from 1 to 40');return t*4+17},$.getSymbolTotalCodewords=function(t){return s[t]},$.getBCHDigit=function(r){let t=0;for(;r!==0;)t++,r>>>=1;return t},$.setToSJISFunction=function(t){if(typeof t!="function")throw new Error('"toSJISFunc" is not a valid function.');e=t},$.isKanjiModeEnabled=function(){return typeof e<"u"},$.toSJIS=function(t){return e(t)},$}var ve={},nt;function Je(){return nt||(nt=1,(function(e){e.L={bit:1},e.M={bit:0},e.Q={bit:3},e.H={bit:2};function s(r){if(typeof r!="string")throw new Error("Param is not a string");switch(r.toLowerCase()){case"l":case"low":return e.L;case"m":case"medium":return e.M;case"q":case"quartile":return e.Q;case"h":case"high":return e.H;default:throw new Error("Unknown EC Level: "+r)}}e.isValid=function(t){return t&&typeof t.bit<"u"&&t.bit>=0&&t.bit<4},e.from=function(t,n){if(e.isValid(t))return t;try{return s(t)}catch{return n}}})(ve)),ve}var Ce,ot;function Ro(){if(ot)return Ce;ot=1;function e(){this.buffer=[],this.length=0}return e.prototype={get:function(s){const r=Math.floor(s/8);return(this.buffer[r]>>>7-s%8&1)===1},put:function(s,r){for(let t=0;t<r;t++)this.putBit((s>>>r-t-1&1)===1)},getLengthInBits:function(){return this.length},putBit:function(s){const r=Math.floor(this.length/8);this.buffer.length<=r&&this.buffer.push(0),s&&(this.buffer[r]|=128>>>this.length%8),this.length++}},Ce=e,Ce}var Se,rt;function To(){if(rt)return Se;rt=1;function e(s){if(!s||s<1)throw new Error("BitMatrix size must be defined and greater than 0");this.size=s,this.data=new Uint8Array(s*s),this.reservedBit=new Uint8Array(s*s)}return e.prototype.set=function(s,r,t,n){const o=s*this.size+r;this.data[o]=t,n&&(this.reservedBit[o]=!0)},e.prototype.get=function(s,r){return this.data[s*this.size+r]},e.prototype.xor=function(s,r,t){this.data[s*this.size+r]^=t},e.prototype.isReserved=function(s,r){return this.reservedBit[s*this.size+r]},Se=e,Se}var _e={},st;function Mo(){return st||(st=1,(function(e){const s=W().getSymbolSize;e.getRowColCoords=function(t){if(t===1)return[];const n=Math.floor(t/7)+2,o=s(t),i=o===145?26:Math.ceil((o-13)/(2*n-2))*2,a=[o-7];for(let u=1;u<n-1;u++)a[u]=a[u-1]-i;return a.push(6),a.reverse()},e.getPositions=function(t){const n=[],o=e.getRowColCoords(t),i=o.length;for(let a=0;a<i;a++)for(let u=0;u<i;u++)a===0&&u===0||a===0&&u===i-1||a===i-1&&u===0||n.push([o[a],o[u]]);return n}})(_e)),_e}var Ee={},it;function No(){if(it)return Ee;it=1;const e=W().getSymbolSize,s=7;return Ee.getPositions=function(t){const n=e(t);return[[0,0],[n-s,0],[0,n-s]]},Ee}var ke={},at;function Po(){return at||(at=1,(function(e){e.Patterns={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7};const s={N1:3,N2:3,N3:40,N4:10};e.isValid=function(n){return n!=null&&n!==""&&!isNaN(n)&&n>=0&&n<=7},e.from=function(n){return e.isValid(n)?parseInt(n,10):void 0},e.getPenaltyN1=function(n){const o=n.size;let i=0,a=0,u=0,l=null,f=null;for(let w=0;w<o;w++){a=u=0,l=f=null;for(let b=0;b<o;b++){let h=n.get(w,b);h===l?a++:(a>=5&&(i+=s.N1+(a-5)),l=h,a=1),h=n.get(b,w),h===f?u++:(u>=5&&(i+=s.N1+(u-5)),f=h,u=1)}a>=5&&(i+=s.N1+(a-5)),u>=5&&(i+=s.N1+(u-5))}return i},e.getPenaltyN2=function(n){const o=n.size;let i=0;for(let a=0;a<o-1;a++)for(let u=0;u<o-1;u++){const l=n.get(a,u)+n.get(a,u+1)+n.get(a+1,u)+n.get(a+1,u+1);(l===4||l===0)&&i++}return i*s.N2},e.getPenaltyN3=function(n){const o=n.size;let i=0,a=0,u=0;for(let l=0;l<o;l++){a=u=0;for(let f=0;f<o;f++)a=a<<1&2047|n.get(l,f),f>=10&&(a===1488||a===93)&&i++,u=u<<1&2047|n.get(f,l),f>=10&&(u===1488||u===93)&&i++}return i*s.N3},e.getPenaltyN4=function(n){let o=0;const i=n.data.length;for(let u=0;u<i;u++)o+=n.data[u];return Math.abs(Math.ceil(o*100/i/5)-10)*s.N4};function r(t,n,o){switch(t){case e.Patterns.PATTERN000:return(n+o)%2===0;case e.Patterns.PATTERN001:return n%2===0;case e.Patterns.PATTERN010:return o%3===0;case e.Patterns.PATTERN011:return(n+o)%3===0;case e.Patterns.PATTERN100:return(Math.floor(n/2)+Math.floor(o/3))%2===0;case e.Patterns.PATTERN101:return n*o%2+n*o%3===0;case e.Patterns.PATTERN110:return(n*o%2+n*o%3)%2===0;case e.Patterns.PATTERN111:return(n*o%3+(n+o)%2)%2===0;default:throw new Error("bad maskPattern:"+t)}}e.applyMask=function(n,o){const i=o.size;for(let a=0;a<i;a++)for(let u=0;u<i;u++)o.isReserved(u,a)||o.xor(u,a,r(n,u,a))},e.getBestMask=function(n,o){const i=Object.keys(e.Patterns).length;let a=0,u=1/0;for(let l=0;l<i;l++){o(l),e.applyMask(l,n);const f=e.getPenaltyN1(n)+e.getPenaltyN2(n)+e.getPenaltyN3(n)+e.getPenaltyN4(n);e.applyMask(l,n),f<u&&(u=f,a=l)}return a}})(ke)),ke}var ue={},ut;function Ot(){if(ut)return ue;ut=1;const e=Je(),s=[1,1,1,1,1,1,1,1,1,1,2,2,1,2,2,4,1,2,4,4,2,4,4,4,2,4,6,5,2,4,6,6,2,5,8,8,4,5,8,8,4,5,8,11,4,8,10,11,4,9,12,16,4,9,16,16,6,10,12,18,6,10,17,16,6,11,16,19,6,13,18,21,7,14,21,25,8,16,20,25,8,17,23,25,9,17,23,34,9,18,25,30,10,20,27,32,12,21,29,35,12,23,34,37,12,25,34,40,13,26,35,42,14,28,38,45,15,29,40,48,16,31,43,51,17,33,45,54,18,35,48,57,19,37,51,60,19,38,53,63,20,40,56,66,21,43,59,70,22,45,62,74,24,47,65,77,25,49,68,81],r=[7,10,13,17,10,16,22,28,15,26,36,44,20,36,52,64,26,48,72,88,36,64,96,112,40,72,108,130,48,88,132,156,60,110,160,192,72,130,192,224,80,150,224,264,96,176,260,308,104,198,288,352,120,216,320,384,132,240,360,432,144,280,408,480,168,308,448,532,180,338,504,588,196,364,546,650,224,416,600,700,224,442,644,750,252,476,690,816,270,504,750,900,300,560,810,960,312,588,870,1050,336,644,952,1110,360,700,1020,1200,390,728,1050,1260,420,784,1140,1350,450,812,1200,1440,480,868,1290,1530,510,924,1350,1620,540,980,1440,1710,570,1036,1530,1800,570,1064,1590,1890,600,1120,1680,1980,630,1204,1770,2100,660,1260,1860,2220,720,1316,1950,2310,750,1372,2040,2430];return ue.getBlocksCount=function(n,o){switch(o){case e.L:return s[(n-1)*4+0];case e.M:return s[(n-1)*4+1];case e.Q:return s[(n-1)*4+2];case e.H:return s[(n-1)*4+3];default:return}},ue.getTotalCodewordsCount=function(n,o){switch(o){case e.L:return r[(n-1)*4+0];case e.M:return r[(n-1)*4+1];case e.Q:return r[(n-1)*4+2];case e.H:return r[(n-1)*4+3];default:return}},ue}var Be={},ne={},lt;function Do(){if(lt)return ne;lt=1;const e=new Uint8Array(512),s=new Uint8Array(256);return(function(){let t=1;for(let n=0;n<255;n++)e[n]=t,s[t]=n,t<<=1,t&256&&(t^=285);for(let n=255;n<512;n++)e[n]=e[n-255]})(),ne.log=function(t){if(t<1)throw new Error("log("+t+")");return s[t]},ne.exp=function(t){return e[t]},ne.mul=function(t,n){return t===0||n===0?0:e[s[t]+s[n]]},ne}var ct;function Lo(){return ct||(ct=1,(function(e){const s=Do();e.mul=function(t,n){const o=new Uint8Array(t.length+n.length-1);for(let i=0;i<t.length;i++)for(let a=0;a<n.length;a++)o[i+a]^=s.mul(t[i],n[a]);return o},e.mod=function(t,n){let o=new Uint8Array(t);for(;o.length-n.length>=0;){const i=o[0];for(let u=0;u<n.length;u++)o[u]^=s.mul(n[u],i);let a=0;for(;a<o.length&&o[a]===0;)a++;o=o.slice(a)}return o},e.generateECPolynomial=function(t){let n=new Uint8Array([1]);for(let o=0;o<t;o++)n=e.mul(n,new Uint8Array([1,s.exp(o)]));return n}})(Be)),Be}var Ie,dt;function qo(){if(dt)return Ie;dt=1;const e=Lo();function s(r){this.genPoly=void 0,this.degree=r,this.degree&&this.initialize(this.degree)}return s.prototype.initialize=function(t){this.degree=t,this.genPoly=e.generateECPolynomial(this.degree)},s.prototype.encode=function(t){if(!this.genPoly)throw new Error("Encoder not initialized");const n=new Uint8Array(t.length+this.degree);n.set(t);const o=e.mod(n,this.genPoly),i=this.degree-o.length;if(i>0){const a=new Uint8Array(this.degree);return a.set(o,i),a}return o},Ie=s,Ie}var Ae={},Re={},Te={},ft;function jt(){return ft||(ft=1,Te.isValid=function(s){return!isNaN(s)&&s>=1&&s<=40}),Te}var H={},ht;function zt(){if(ht)return H;ht=1;const e="[0-9]+",s="[A-Z $%*+\\-./:]+";let r="(?:[u3000-u303F]|[u3040-u309F]|[u30A0-u30FF]|[uFF00-uFFEF]|[u4E00-u9FAF]|[u2605-u2606]|[u2190-u2195]|u203B|[u2010u2015u2018u2019u2025u2026u201Cu201Du2225u2260]|[u0391-u0451]|[u00A7u00A8u00B1u00B4u00D7u00F7])+";r=r.replace(/u/g,"\\u");const t="(?:(?![A-Z0-9 $%*+\\-./:]|"+r+`)(?:.|[\r
2
- ]))+`;H.KANJI=new RegExp(r,"g"),H.BYTE_KANJI=new RegExp("[^A-Z0-9 $%*+\\-./:]+","g"),H.BYTE=new RegExp(t,"g"),H.NUMERIC=new RegExp(e,"g"),H.ALPHANUMERIC=new RegExp(s,"g");const n=new RegExp("^"+r+"$"),o=new RegExp("^"+e+"$"),i=new RegExp("^[A-Z0-9 $%*+\\-./:]+$");return H.testKanji=function(u){return n.test(u)},H.testNumeric=function(u){return o.test(u)},H.testAlphanumeric=function(u){return i.test(u)},H}var gt;function Z(){return gt||(gt=1,(function(e){const s=jt(),r=zt();e.NUMERIC={id:"Numeric",bit:1,ccBits:[10,12,14]},e.ALPHANUMERIC={id:"Alphanumeric",bit:2,ccBits:[9,11,13]},e.BYTE={id:"Byte",bit:4,ccBits:[8,16,16]},e.KANJI={id:"Kanji",bit:8,ccBits:[8,10,12]},e.MIXED={bit:-1},e.getCharCountIndicator=function(o,i){if(!o.ccBits)throw new Error("Invalid mode: "+o);if(!s.isValid(i))throw new Error("Invalid version: "+i);return i>=1&&i<10?o.ccBits[0]:i<27?o.ccBits[1]:o.ccBits[2]},e.getBestModeForData=function(o){return r.testNumeric(o)?e.NUMERIC:r.testAlphanumeric(o)?e.ALPHANUMERIC:r.testKanji(o)?e.KANJI:e.BYTE},e.toString=function(o){if(o&&o.id)return o.id;throw new Error("Invalid mode")},e.isValid=function(o){return o&&o.bit&&o.ccBits};function t(n){if(typeof n!="string")throw new Error("Param is not a string");switch(n.toLowerCase()){case"numeric":return e.NUMERIC;case"alphanumeric":return e.ALPHANUMERIC;case"kanji":return e.KANJI;case"byte":return e.BYTE;default:throw new Error("Unknown mode: "+n)}}e.from=function(o,i){if(e.isValid(o))return o;try{return t(o)}catch{return i}}})(Re)),Re}var mt;function Fo(){return mt||(mt=1,(function(e){const s=W(),r=Ot(),t=Je(),n=Z(),o=jt(),i=7973,a=s.getBCHDigit(i);function u(b,h,d){for(let g=1;g<=40;g++)if(h<=e.getCapacity(g,d,b))return g}function l(b,h){return n.getCharCountIndicator(b,h)+4}function f(b,h){let d=0;return b.forEach(function(g){const N=l(g.mode,h);d+=N+g.getBitsLength()}),d}function w(b,h){for(let d=1;d<=40;d++)if(f(b,d)<=e.getCapacity(d,h,n.MIXED))return d}e.from=function(h,d){return o.isValid(h)?parseInt(h,10):d},e.getCapacity=function(h,d,g){if(!o.isValid(h))throw new Error("Invalid QR Code version");typeof g>"u"&&(g=n.BYTE);const N=s.getSymbolTotalCodewords(h),A=r.getTotalCodewordsCount(h,d),P=(N-A)*8;if(g===n.MIXED)return P;const c=P-l(g,h);switch(g){case n.NUMERIC:return Math.floor(c/10*3);case n.ALPHANUMERIC:return Math.floor(c/11*2);case n.KANJI:return Math.floor(c/13);case n.BYTE:default:return Math.floor(c/8)}},e.getBestVersionForData=function(h,d){let g;const N=t.from(d,t.M);if(Array.isArray(h)){if(h.length>1)return w(h,N);if(h.length===0)return 1;g=h[0]}else g=h;return u(g.mode,g.getLength(),N)},e.getEncodedBits=function(h){if(!o.isValid(h)||h<7)throw new Error("Invalid QR Code version");let d=h<<12;for(;s.getBCHDigit(d)-a>=0;)d^=i<<s.getBCHDigit(d)-a;return h<<12|d}})(Ae)),Ae}var Me={},yt;function Uo(){if(yt)return Me;yt=1;const e=W(),s=1335,r=21522,t=e.getBCHDigit(s);return Me.getEncodedBits=function(o,i){const a=o.bit<<3|i;let u=a<<10;for(;e.getBCHDigit(u)-t>=0;)u^=s<<e.getBCHDigit(u)-t;return(a<<10|u)^r},Me}var Ne={},Pe,pt;function Ko(){if(pt)return Pe;pt=1;const e=Z();function s(r){this.mode=e.NUMERIC,this.data=r.toString()}return s.getBitsLength=function(t){return 10*Math.floor(t/3)+(t%3?t%3*3+1:0)},s.prototype.getLength=function(){return this.data.length},s.prototype.getBitsLength=function(){return s.getBitsLength(this.data.length)},s.prototype.write=function(t){let n,o,i;for(n=0;n+3<=this.data.length;n+=3)o=this.data.substr(n,3),i=parseInt(o,10),t.put(i,10);const a=this.data.length-n;a>0&&(o=this.data.substr(n),i=parseInt(o,10),t.put(i,a*3+1))},Pe=s,Pe}var De,wt;function Vo(){if(wt)return De;wt=1;const e=Z(),s=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"," ","$","%","*","+","-",".","/",":"];function r(t){this.mode=e.ALPHANUMERIC,this.data=t}return r.getBitsLength=function(n){return 11*Math.floor(n/2)+6*(n%2)},r.prototype.getLength=function(){return this.data.length},r.prototype.getBitsLength=function(){return r.getBitsLength(this.data.length)},r.prototype.write=function(n){let o;for(o=0;o+2<=this.data.length;o+=2){let i=s.indexOf(this.data[o])*45;i+=s.indexOf(this.data[o+1]),n.put(i,11)}this.data.length%2&&n.put(s.indexOf(this.data[o]),6)},De=r,De}var Le,bt;function Ho(){if(bt)return Le;bt=1;const e=Z();function s(r){this.mode=e.BYTE,typeof r=="string"?this.data=new TextEncoder().encode(r):this.data=new Uint8Array(r)}return s.getBitsLength=function(t){return t*8},s.prototype.getLength=function(){return this.data.length},s.prototype.getBitsLength=function(){return s.getBitsLength(this.data.length)},s.prototype.write=function(r){for(let t=0,n=this.data.length;t<n;t++)r.put(this.data[t],8)},Le=s,Le}var qe,vt;function Oo(){if(vt)return qe;vt=1;const e=Z(),s=W();function r(t){this.mode=e.KANJI,this.data=t}return r.getBitsLength=function(n){return n*13},r.prototype.getLength=function(){return this.data.length},r.prototype.getBitsLength=function(){return r.getBitsLength(this.data.length)},r.prototype.write=function(t){let n;for(n=0;n<this.data.length;n++){let o=s.toSJIS(this.data[n]);if(o>=33088&&o<=40956)o-=33088;else if(o>=57408&&o<=60351)o-=49472;else throw new Error("Invalid SJIS character: "+this.data[n]+`
3
- Make sure your charset is UTF-8`);o=(o>>>8&255)*192+(o&255),t.put(o,13)}},qe=r,qe}var Fe={exports:{}},Ct;function jo(){return Ct||(Ct=1,(function(e){var s={single_source_shortest_paths:function(r,t,n){var o={},i={};i[t]=0;var a=s.PriorityQueue.make();a.push(t,0);for(var u,l,f,w,b,h,d,g,N;!a.empty();){u=a.pop(),l=u.value,w=u.cost,b=r[l]||{};for(f in b)b.hasOwnProperty(f)&&(h=b[f],d=w+h,g=i[f],N=typeof i[f]>"u",(N||g>d)&&(i[f]=d,a.push(f,d),o[f]=l))}if(typeof n<"u"&&typeof i[n]>"u"){var A=["Could not find a path from ",t," to ",n,"."].join("");throw new Error(A)}return o},extract_shortest_path_from_predecessor_list:function(r,t){for(var n=[],o=t;o;)n.push(o),r[o],o=r[o];return n.reverse(),n},find_path:function(r,t,n){var o=s.single_source_shortest_paths(r,t,n);return s.extract_shortest_path_from_predecessor_list(o,n)},PriorityQueue:{make:function(r){var t=s.PriorityQueue,n={},o;r=r||{};for(o in t)t.hasOwnProperty(o)&&(n[o]=t[o]);return n.queue=[],n.sorter=r.sorter||t.default_sorter,n},default_sorter:function(r,t){return r.cost-t.cost},push:function(r,t){var n={value:r,cost:t};this.queue.push(n),this.queue.sort(this.sorter)},pop:function(){return this.queue.shift()},empty:function(){return this.queue.length===0}}};e.exports=s})(Fe)),Fe.exports}var St;function zo(){return St||(St=1,(function(e){const s=Z(),r=Ko(),t=Vo(),n=Ho(),o=Oo(),i=zt(),a=W(),u=jo();function l(A){return unescape(encodeURIComponent(A)).length}function f(A,P,c){const S=[];let I;for(;(I=A.exec(c))!==null;)S.push({data:I[0],index:I.index,mode:P,length:I[0].length});return S}function w(A){const P=f(i.NUMERIC,s.NUMERIC,A),c=f(i.ALPHANUMERIC,s.ALPHANUMERIC,A);let S,I;return a.isKanjiModeEnabled()?(S=f(i.BYTE,s.BYTE,A),I=f(i.KANJI,s.KANJI,A)):(S=f(i.BYTE_KANJI,s.BYTE,A),I=[]),P.concat(c,S,I).sort(function(p,y){return p.index-y.index}).map(function(p){return{data:p.data,mode:p.mode,length:p.length}})}function b(A,P){switch(P){case s.NUMERIC:return r.getBitsLength(A);case s.ALPHANUMERIC:return t.getBitsLength(A);case s.KANJI:return o.getBitsLength(A);case s.BYTE:return n.getBitsLength(A)}}function h(A){return A.reduce(function(P,c){const S=P.length-1>=0?P[P.length-1]:null;return S&&S.mode===c.mode?(P[P.length-1].data+=c.data,P):(P.push(c),P)},[])}function d(A){const P=[];for(let c=0;c<A.length;c++){const S=A[c];switch(S.mode){case s.NUMERIC:P.push([S,{data:S.data,mode:s.ALPHANUMERIC,length:S.length},{data:S.data,mode:s.BYTE,length:S.length}]);break;case s.ALPHANUMERIC:P.push([S,{data:S.data,mode:s.BYTE,length:S.length}]);break;case s.KANJI:P.push([S,{data:S.data,mode:s.BYTE,length:l(S.data)}]);break;case s.BYTE:P.push([{data:S.data,mode:s.BYTE,length:l(S.data)}])}}return P}function g(A,P){const c={},S={start:{}};let I=["start"];for(let C=0;C<A.length;C++){const p=A[C],y=[];for(let m=0;m<p.length;m++){const _=p[m],E=""+C+m;y.push(E),c[E]={node:_,lastCount:0},S[E]={};for(let k=0;k<I.length;k++){const B=I[k];c[B]&&c[B].node.mode===_.mode?(S[B][E]=b(c[B].lastCount+_.length,_.mode)-b(c[B].lastCount,_.mode),c[B].lastCount+=_.length):(c[B]&&(c[B].lastCount=_.length),S[B][E]=b(_.length,_.mode)+4+s.getCharCountIndicator(_.mode,P))}}I=y}for(let C=0;C<I.length;C++)S[I[C]].end=0;return{map:S,table:c}}function N(A,P){let c;const S=s.getBestModeForData(A);if(c=s.from(P,S),c!==s.BYTE&&c.bit<S.bit)throw new Error('"'+A+'" cannot be encoded with mode '+s.toString(c)+`.
4
- Suggested mode is: `+s.toString(S));switch(c===s.KANJI&&!a.isKanjiModeEnabled()&&(c=s.BYTE),c){case s.NUMERIC:return new r(A);case s.ALPHANUMERIC:return new t(A);case s.KANJI:return new o(A);case s.BYTE:return new n(A)}}e.fromArray=function(P){return P.reduce(function(c,S){return typeof S=="string"?c.push(N(S,null)):S.data&&c.push(N(S.data,S.mode)),c},[])},e.fromString=function(P,c){const S=w(P,a.isKanjiModeEnabled()),I=d(S),C=g(I,c),p=u.find_path(C.map,"start","end"),y=[];for(let m=1;m<p.length-1;m++)y.push(C.table[p[m]].node);return e.fromArray(h(y))},e.rawSplit=function(P){return e.fromArray(w(P,a.isKanjiModeEnabled()))}})(Ne)),Ne}var _t;function xo(){if(_t)return be;_t=1;const e=W(),s=Je(),r=Ro(),t=To(),n=Mo(),o=No(),i=Po(),a=Ot(),u=qo(),l=Fo(),f=Uo(),w=Z(),b=zo();function h(C,p){const y=C.size,m=o.getPositions(p);for(let _=0;_<m.length;_++){const E=m[_][0],k=m[_][1];for(let B=-1;B<=7;B++)if(!(E+B<=-1||y<=E+B))for(let M=-1;M<=7;M++)k+M<=-1||y<=k+M||(B>=0&&B<=6&&(M===0||M===6)||M>=0&&M<=6&&(B===0||B===6)||B>=2&&B<=4&&M>=2&&M<=4?C.set(E+B,k+M,!0,!0):C.set(E+B,k+M,!1,!0))}}function d(C){const p=C.size;for(let y=8;y<p-8;y++){const m=y%2===0;C.set(y,6,m,!0),C.set(6,y,m,!0)}}function g(C,p){const y=n.getPositions(p);for(let m=0;m<y.length;m++){const _=y[m][0],E=y[m][1];for(let k=-2;k<=2;k++)for(let B=-2;B<=2;B++)k===-2||k===2||B===-2||B===2||k===0&&B===0?C.set(_+k,E+B,!0,!0):C.set(_+k,E+B,!1,!0)}}function N(C,p){const y=C.size,m=l.getEncodedBits(p);let _,E,k;for(let B=0;B<18;B++)_=Math.floor(B/3),E=B%3+y-8-3,k=(m>>B&1)===1,C.set(_,E,k,!0),C.set(E,_,k,!0)}function A(C,p,y){const m=C.size,_=f.getEncodedBits(p,y);let E,k;for(E=0;E<15;E++)k=(_>>E&1)===1,E<6?C.set(E,8,k,!0):E<8?C.set(E+1,8,k,!0):C.set(m-15+E,8,k,!0),E<8?C.set(8,m-E-1,k,!0):E<9?C.set(8,15-E-1+1,k,!0):C.set(8,15-E-1,k,!0);C.set(m-8,8,1,!0)}function P(C,p){const y=C.size;let m=-1,_=y-1,E=7,k=0;for(let B=y-1;B>0;B-=2)for(B===6&&B--;;){for(let M=0;M<2;M++)if(!C.isReserved(_,B-M)){let V=!1;k<p.length&&(V=(p[k]>>>E&1)===1),C.set(_,B-M,V),E--,E===-1&&(k++,E=7)}if(_+=m,_<0||y<=_){_-=m,m=-m;break}}}function c(C,p,y){const m=new r;y.forEach(function(M){m.put(M.mode.bit,4),m.put(M.getLength(),w.getCharCountIndicator(M.mode,C)),M.write(m)});const _=e.getSymbolTotalCodewords(C),E=a.getTotalCodewordsCount(C,p),k=(_-E)*8;for(m.getLengthInBits()+4<=k&&m.put(0,4);m.getLengthInBits()%8!==0;)m.putBit(0);const B=(k-m.getLengthInBits())/8;for(let M=0;M<B;M++)m.put(M%2?17:236,8);return S(m,C,p)}function S(C,p,y){const m=e.getSymbolTotalCodewords(p),_=a.getTotalCodewordsCount(p,y),E=m-_,k=a.getBlocksCount(p,y),B=m%k,M=k-B,V=Math.floor(m/k),K=Math.floor(E/k),q=K+1,Ye=V-K,$t=new u(Ye);let he=0;const ie=new Array(k),Qe=new Array(k);let ge=0;const Jt=new Uint8Array(C.buffer);for(let X=0;X<k;X++){const ye=X<M?K:q;ie[X]=Jt.slice(he,he+ye),Qe[X]=$t.encode(ie[X]),he+=ye,ge=Math.max(ge,ye)}const me=new Uint8Array(m);let Ge=0,O,j;for(O=0;O<ge;O++)for(j=0;j<k;j++)O<ie[j].length&&(me[Ge++]=ie[j][O]);for(O=0;O<Ye;O++)for(j=0;j<k;j++)me[Ge++]=Qe[j][O];return me}function I(C,p,y,m){let _;if(Array.isArray(C))_=b.fromArray(C);else if(typeof C=="string"){let V=p;if(!V){const K=b.rawSplit(C);V=l.getBestVersionForData(K,y)}_=b.fromString(C,V||40)}else throw new Error("Invalid data");const E=l.getBestVersionForData(_,y);if(!E)throw new Error("The amount of data is too big to be stored in a QR Code");if(!p)p=E;else if(p<E)throw new Error(`
5
- The chosen QR Code version cannot contain this amount of data.
6
- Minimum version required to store current data is: `+E+`.
7
- `);const k=c(p,y,_),B=e.getSymbolSize(p),M=new t(B);return h(M,p),d(M),g(M,p),A(M,y,0),p>=7&&N(M,p),P(M,k),isNaN(m)&&(m=i.getBestMask(M,A.bind(null,M,y))),i.applyMask(m,M),A(M,y,m),{modules:M,version:p,errorCorrectionLevel:y,maskPattern:m,segments:_}}return be.create=function(p,y){if(typeof p>"u"||p==="")throw new Error("No input text");let m=s.M,_,E;return typeof y<"u"&&(m=s.from(y.errorCorrectionLevel,s.M),_=l.from(y.version),E=i.from(y.maskPattern),y.toSJISFunc&&e.setToSJISFunction(y.toSJISFunc)),I(p,_,m,E)},be}var Ue={},Ke={},Et;function xt(){return Et||(Et=1,(function(e){function s(r){if(typeof r=="number"&&(r=r.toString()),typeof r!="string")throw new Error("Color should be defined as hex string");let t=r.slice().replace("#","").split("");if(t.length<3||t.length===5||t.length>8)throw new Error("Invalid hex color: "+r);(t.length===3||t.length===4)&&(t=Array.prototype.concat.apply([],t.map(function(o){return[o,o]}))),t.length===6&&t.push("F","F");const n=parseInt(t.join(""),16);return{r:n>>24&255,g:n>>16&255,b:n>>8&255,a:n&255,hex:"#"+t.slice(0,6).join("")}}e.getOptions=function(t){t||(t={}),t.color||(t.color={});const n=typeof t.margin>"u"||t.margin===null||t.margin<0?4:t.margin,o=t.width&&t.width>=21?t.width:void 0,i=t.scale||4;return{width:o,scale:o?4:i,margin:n,color:{dark:s(t.color.dark||"#000000ff"),light:s(t.color.light||"#ffffffff")},type:t.type,rendererOpts:t.rendererOpts||{}}},e.getScale=function(t,n){return n.width&&n.width>=t+n.margin*2?n.width/(t+n.margin*2):n.scale},e.getImageWidth=function(t,n){const o=e.getScale(t,n);return Math.floor((t+n.margin*2)*o)},e.qrToImageData=function(t,n,o){const i=n.modules.size,a=n.modules.data,u=e.getScale(i,o),l=Math.floor((i+o.margin*2)*u),f=o.margin*u,w=[o.color.light,o.color.dark];for(let b=0;b<l;b++)for(let h=0;h<l;h++){let d=(b*l+h)*4,g=o.color.light;if(b>=f&&h>=f&&b<l-f&&h<l-f){const N=Math.floor((b-f)/u),A=Math.floor((h-f)/u);g=w[a[N*i+A]?1:0]}t[d++]=g.r,t[d++]=g.g,t[d++]=g.b,t[d]=g.a}}})(Ke)),Ke}var kt;function $o(){return kt||(kt=1,(function(e){const s=xt();function r(n,o,i){n.clearRect(0,0,o.width,o.height),o.style||(o.style={}),o.height=i,o.width=i,o.style.height=i+"px",o.style.width=i+"px"}function t(){try{return document.createElement("canvas")}catch{throw new Error("You need to specify a canvas element")}}e.render=function(o,i,a){let u=a,l=i;typeof u>"u"&&(!i||!i.getContext)&&(u=i,i=void 0),i||(l=t()),u=s.getOptions(u);const f=s.getImageWidth(o.modules.size,u),w=l.getContext("2d"),b=w.createImageData(f,f);return s.qrToImageData(b.data,o,u),r(w,l,f),w.putImageData(b,0,0),l},e.renderToDataURL=function(o,i,a){let u=a;typeof u>"u"&&(!i||!i.getContext)&&(u=i,i=void 0),u||(u={});const l=e.render(o,i,u),f=u.type||"image/png",w=u.rendererOpts||{};return l.toDataURL(f,w.quality)}})(Ue)),Ue}var Ve={},Bt;function Jo(){if(Bt)return Ve;Bt=1;const e=xt();function s(n,o){const i=n.a/255,a=o+'="'+n.hex+'"';return i<1?a+" "+o+'-opacity="'+i.toFixed(2).slice(1)+'"':a}function r(n,o,i){let a=n+o;return typeof i<"u"&&(a+=" "+i),a}function t(n,o,i){let a="",u=0,l=!1,f=0;for(let w=0;w<n.length;w++){const b=Math.floor(w%o),h=Math.floor(w/o);!b&&!l&&(l=!0),n[w]?(f++,w>0&&b>0&&n[w-1]||(a+=l?r("M",b+i,.5+h+i):r("m",u,0),u=0,l=!1),b+1<o&&n[w+1]||(a+=r("h",f),f=0)):u++}return a}return Ve.render=function(o,i,a){const u=e.getOptions(i),l=o.modules.size,f=o.modules.data,w=l+u.margin*2,b=u.color.light.a?"<path "+s(u.color.light,"fill")+' d="M0 0h'+w+"v"+w+'H0z"/>':"",h="<path "+s(u.color.dark,"stroke")+' d="'+t(f,l,u.margin)+'"/>',d='viewBox="0 0 '+w+" "+w+'"',N='<svg xmlns="http://www.w3.org/2000/svg" '+(u.width?'width="'+u.width+'" height="'+u.width+'" ':"")+d+' shape-rendering="crispEdges">'+b+h+`</svg>
8
- `;return typeof a=="function"&&a(null,N),N},Ve}var It;function Yo(){if(It)return te;It=1;const e=Ao(),s=xo(),r=$o(),t=Jo();function n(o,i,a,u,l){const f=[].slice.call(arguments,1),w=f.length,b=typeof f[w-1]=="function";if(!b&&!e())throw new Error("Callback required as last argument");if(b){if(w<2)throw new Error("Too few arguments provided");w===2?(l=a,a=i,i=u=void 0):w===3&&(i.getContext&&typeof l>"u"?(l=u,u=void 0):(l=u,u=a,a=i,i=void 0))}else{if(w<1)throw new Error("Too few arguments provided");return w===1?(a=i,i=u=void 0):w===2&&!i.getContext&&(u=a,a=i,i=void 0),new Promise(function(h,d){try{const g=s.create(a,u);h(o(g,i,u))}catch(g){d(g)}})}try{const h=s.create(a,u);l(null,o(h,i,u))}catch(h){l(h)}}return te.create=s.create,te.toCanvas=n.bind(null,r.render),te.toDataURL=n.bind(null,r.renderToDataURL),te.toString=n.bind(null,function(o,i,a){return t.render(o,a)}),te}var Qo=Yo();const Go=Io(Qo),Wo={class:"qr-display"},Zo={class:"qr-section"},Xo=["href","onKeydown"],er={key:0,class:"link-text"},tr={__name:"QRCodeDisplay",props:{url:{type:String,required:!0},showLink:{type:Boolean,default:!1}},emits:["copied"],setup(e,{emit:s}){const r=e,t=s,n=F(null),o=x(()=>r.url?r.url.replace(/^https?:\/\//,""):"");function i(){if(!(!r.url||!n.value))try{n.value.getContext("2d").clearRect(0,0,n.value.width,n.value.height),Go.toCanvas(n.value,r.url,{scale:6,margin:0,color:{dark:"#000000",light:"#FFFFFF"}}),n.value.removeAttribute("style")}catch(u){console.error("QR code generation failed:",u)}}async function a(){if(r.url)try{await navigator.clipboard.writeText(r.url),t("copied")}catch(u){console.error("Failed to copy link:",u)}}return He(()=>r.url,()=>{i()},{immediate:!0}),He(n,()=>{n.value&&r.url&&i()},{immediate:!0}),(u,l)=>(T(),R("div",Wo,[v("div",Zo,[v("a",{href:e.url,onClick:de(a,["prevent"]),class:"qr-link",title:"Click to copy link",tabindex:"0",onKeydown:ln(de(a,["prevent"]),["enter"])},[v("canvas",{ref_key:"qrCanvas",ref:n,class:"qr-code"},null,512),e.showLink&&e.url?(T(),R("div",er,D(o.value),1)):L("",!0)],40,Xo)])]))}},nr=Y(tr,[["__scopeId","data-v-727427c4"]]),or={class:"device-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"regTitle"},rr={class:"reg-header-row"},sr={id:"regTitle",class:"reg-title"},ir={key:0},ar={key:1},ur={class:"device-link-section"},lr={key:0,class:"expiry-note"},cr={__name:"RegistrationLinkModal",props:{endpoint:{type:String,required:!0},userName:{type:String,default:""}},emits:["close","copied"],setup(e,{emit:s}){const r=e,t=s,n=F(null),o=F(null),i=F(null),a=F(null),u=F(null);async function l(){try{const d=await Q(r.endpoint,{method:"POST"});if(d.url){if(o.value=d.url,i.value=d.expires?new Date(d.expires):null,await ze(),n.value){n.value.showModal();const g=a.value;(g?.querySelector(".btn-primary")||g?.querySelector("button"))?.focus()}}else t("close")}catch{t("close")}}function f(){t("copied")}const w=d=>{const g=z(d)},b=d=>{const g=z(d);g&&(d.preventDefault(),(g==="down"||g==="up")&&a.value?.querySelector("button")?.focus())},h=d=>{const g=z(d);g&&(d.preventDefault(),(g==="up"||g==="down")&&document.querySelector(".qr-link")?.focus())};return qt(()=>{u.value=document.activeElement,l()}),Ft(()=>{const d=u.value;d&&document.body.contains(d)&&!d.disabled&&d.focus()}),(d,g)=>(T(),R("dialog",{ref_key:"dialog",ref:n,onClose:g[2]||(g[2]=N=>d.$emit("close")),onKeydown:w},[v("div",or,[v("div",rr,[v("h2",sr,[g[3]||(g[3]=xe(" 📱 ",-1)),e.userName?(T(),R("span",ir,"Registration for "+D(e.userName),1)):(T(),R("span",ar,"Add Another Device"))]),v("button",{class:"icon-btn",onClick:g[0]||(g[0]=N=>d.$emit("close")),"aria-label":"Close",tabindex:"-1"},"✕")]),v("div",ur,[g[4]||(g[4]=v("p",{class:"reg-help"}," Scan this QR code on the new device, or copy the link and open it there. ",-1)),cn(nr,{url:o.value,"show-link":!0,onCopied:f,onKeydown:b},null,8,["url"]),i.value?(T(),R("p",lr," This link expires "+D(U(G)(i.value).toLowerCase())+". ",1)):L("",!0)]),v("div",{class:"reg-actions",ref_key:"actionsRow",ref:a,onKeydown:h},[v("button",{class:"btn-secondary",onClick:g[1]||(g[1]=N=>d.$emit("close"))},"Close")],544)])],544))}},Ir=Y(cr,[["__scopeId","data-v-e04dd463"]]),dr={class:"loading-container"},fr={__name:"LoadingView",props:{message:{type:String,default:"Loading..."}},setup(e){return(s,r)=>(T(),R("div",dr,[r[0]||(r[0]=v("div",{class:"loading-spinner"},null,-1)),v("p",null,D(e.message),1)]))}},Ar=Y(fr,[["__scopeId","data-v-130f5abf"]]),hr={class:"message-container"},gr={class:"message-content"},mr={class:"button-row"},yr={__name:"AccessDenied",emits:["reload"],setup(e){return(s,r)=>(T(),R("div",hr,[v("div",gr,[r[2]||(r[2]=v("h2",null,"🔒 Access Denied",-1)),v("div",mr,[v("button",{class:"btn-secondary",onClick:r[0]||(r[0]=(...t)=>U(We)&&U(We)(...t))},"Back"),v("button",{class:"btn-primary",onClick:r[1]||(r[1]=t=>s.$emit("reload"))},"Reload Page")])])]))}},Rr=Y(yr,[["__scopeId","data-v-744305d5"]]);export{Rr as A,Cr as B,Ar as L,Er as M,kr as N,Ir as R,_r as U,Sr as _,Br as a,vr as b,br as c,$e as u};
@@ -1 +0,0 @@
1
- .permissions-section[data-v-fabb2aac]{margin-bottom:var(--space-xl)}.permissions-section h2[data-v-fabb2aac]{margin-bottom:var(--space-md)}.actions[data-v-fabb2aac]{display:flex;flex-wrap:wrap;gap:var(--space-sm);align-items:center}.actions button[data-v-fabb2aac]{width:auto}.org-table a[data-v-fabb2aac]{text-decoration:none;color:var(--color-link)}.org-table a[data-v-fabb2aac]:hover{text-decoration:underline}.org-table .center[data-v-fabb2aac]{width:6rem;min-width:6rem}.org-table .role-names[data-v-fabb2aac]{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.perm-name-cell[data-v-fabb2aac]{display:flex;flex-direction:column;gap:.3rem}.perm-title[data-v-fabb2aac]{font-weight:600;color:var(--color-heading)}.perm-id-info[data-v-fabb2aac]{font-size:.8rem;color:var(--color-text-muted)}.icon-btn[data-v-fabb2aac]{background:none;border:none;color:var(--color-text-muted);padding:.2rem;border-radius:var(--radius-sm);cursor:pointer;transition:background .2s ease,color .2s ease}.icon-btn[data-v-fabb2aac]:hover{color:var(--color-heading);background:var(--color-surface-muted)}.delete-icon[data-v-fabb2aac]{color:var(--color-danger)}.delete-icon[data-v-fabb2aac]:hover{background:var(--color-danger-bg);color:var(--color-danger-text)}.matrix-wrapper[data-v-fabb2aac]{margin:var(--space-md) 0;padding:var(--space-lg)}.matrix-scroll[data-v-fabb2aac]{overflow-x:auto}.matrix-hint[data-v-fabb2aac]{font-size:.8rem;color:var(--color-text-muted)}.perm-matrix-grid[data-v-fabb2aac]{display:inline-grid;gap:.25rem;align-items:stretch}.perm-matrix-grid[data-v-fabb2aac]>*{padding:.35rem .45rem;font-size:.75rem}.perm-matrix-grid .grid-head[data-v-fabb2aac]{color:var(--color-text-muted);text-transform:uppercase;font-weight:600;letter-spacing:.05em}.perm-matrix-grid .perm-head[data-v-fabb2aac]{display:flex;align-items:flex-end;justify-content:flex-start;padding:.35rem .45rem;font-size:.75rem}.perm-matrix-grid .org-head[data-v-fabb2aac]{display:flex;align-items:flex-end;justify-content:center}.perm-matrix-grid .org-head span[data-v-fabb2aac]{writing-mode:vertical-rl;transform:rotate(180deg);font-size:.65rem}.perm-name[data-v-fabb2aac]{font-weight:600;color:var(--color-heading);padding:.35rem .45rem;font-size:.75rem}.display-text[data-v-fabb2aac]{margin-right:var(--space-xs)}.edit-display-btn[data-v-fabb2aac]{padding:.1rem .2rem;font-size:.8rem}.edit-org-btn[data-v-fabb2aac]{padding:.1rem .2rem;font-size:.8rem;margin-left:var(--space-xs)}.perm-actions[data-v-fabb2aac],.center[data-v-fabb2aac]{text-align:center}.muted[data-v-fabb2aac]{color:var(--color-text-muted)}.card.surface[data-v-25f08cdd]{padding:var(--space-lg)}.org-title[data-v-25f08cdd]{display:flex;align-items:center;gap:var(--space-sm);margin-bottom:var(--space-lg)}.org-name[data-v-25f08cdd]{font-size:1.5rem;font-weight:600;color:var(--color-heading)}.icon-btn[data-v-25f08cdd]{background:none;border:none;color:var(--color-text-muted);padding:.2rem;border-radius:var(--radius-sm);cursor:pointer;transition:background .2s ease,color .2s ease}.icon-btn[data-v-25f08cdd]:hover{color:var(--color-heading);background:var(--color-surface-muted)}.matrix-wrapper[data-v-25f08cdd]{margin:var(--space-md) 0;padding:var(--space-lg)}.matrix-scroll[data-v-25f08cdd]{overflow-x:auto}.matrix-hint[data-v-25f08cdd]{font-size:.8rem;color:var(--color-text-muted)}.perm-matrix-grid[data-v-25f08cdd]{display:inline-grid;gap:.25rem;align-items:stretch}.perm-matrix-grid[data-v-25f08cdd]>*{padding:.35rem .45rem;font-size:.75rem}.perm-matrix-grid .grid-head[data-v-25f08cdd]{color:var(--color-text-muted);text-transform:uppercase;font-weight:600;letter-spacing:.05em}.perm-matrix-grid .perm-head[data-v-25f08cdd]{display:flex;align-items:flex-end;justify-content:flex-start;padding:.35rem .45rem;font-size:.75rem}.perm-matrix-grid .role-head[data-v-25f08cdd]{display:flex;align-items:flex-end;justify-content:center}.perm-matrix-grid .role-head span[data-v-25f08cdd]{writing-mode:vertical-rl;transform:rotate(180deg);font-size:.65rem}.perm-matrix-grid .add-role-head[data-v-25f08cdd]{cursor:pointer}.perm-name[data-v-25f08cdd]{font-weight:600;color:var(--color-heading);padding:.35rem .45rem;font-size:.75rem}.roles-grid[data-v-25f08cdd]{display:flex;gap:var(--space-lg);margin-top:var(--space-lg)}.role-column[data-v-25f08cdd]{flex:1;min-width:200px;border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-md)}.role-header[data-v-25f08cdd]{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--space-md)}.role-name[data-v-25f08cdd]{display:flex;align-items:center;gap:var(--space-xs);font-size:1.1rem;color:var(--color-heading)}.role-actions[data-v-25f08cdd]{display:flex;gap:var(--space-xs)}.plus-btn[data-v-25f08cdd]{background:var(--color-accent-soft);color:var(--color-accent);border:none;border-radius:var(--radius-sm);padding:.25rem .45rem;font-size:1.1rem;cursor:pointer}.plus-btn[data-v-25f08cdd]:hover{background:#2563eb2e}.user-list[data-v-25f08cdd]{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-xs)}.user-chip[data-v-25f08cdd]{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);padding:.45rem .6rem;display:flex;justify-content:space-between;gap:var(--space-sm);cursor:grab}.user-chip[data-v-25f08cdd]:focus{outline:2px solid var(--color-accent);outline-offset:1px}.user-chip .meta[data-v-25f08cdd]{font-size:.7rem;color:var(--color-text-muted)}.empty-role[data-v-25f08cdd]{border:1px dashed var(--color-border-strong);border-radius:var(--radius-md);padding:var(--space-sm);display:flex;flex-direction:column;gap:var(--space-xs);align-items:flex-start}.empty-text[data-v-25f08cdd]{margin:0}.delete-icon[data-v-25f08cdd]{color:var(--color-danger)}.delete-icon[data-v-25f08cdd]:hover{background:var(--color-danger-bg);color:var(--color-danger-text)}.muted[data-v-25f08cdd]{color:var(--color-text-muted)}@media(max-width:720px){.roles-grid[data-v-25f08cdd]{flex-direction:column}}.user-detail[data-v-70289426]{display:flex;flex-direction:column;gap:var(--space-lg)}.actions[data-v-70289426]{display:flex;flex-wrap:wrap;gap:var(--space-sm);align-items:center}.ancillary-actions[data-v-70289426]{margin-top:-.5rem}.reg-token-btn[data-v-70289426]{align-self:flex-start}.registration-actions[data-v-70289426]{display:flex;flex-direction:column;gap:.5rem}.icon-btn[data-v-70289426]{background:none;border:none;color:var(--color-text-muted);padding:.2rem;border-radius:var(--radius-sm);cursor:pointer;transition:background .2s ease,color .2s ease}.icon-btn[data-v-70289426]:hover{color:var(--color-heading);background:var(--color-surface-muted)}.matrix-hint[data-v-70289426]{font-size:.8rem;color:var(--color-text-muted)}.error[data-v-70289426]{color:var(--color-danger-text)}.small[data-v-70289426]{font-size:.9rem}.muted[data-v-70289426]{color:var(--color-text-muted)}.error[data-v-b4640c15]{color:var(--color-danger-text)}.small[data-v-b4640c15]{font-size:.9rem}.muted[data-v-b4640c15]{color:var(--color-text-muted)}.view-admin[data-v-3974e020]{padding-bottom:var(--space-3xl)}.view-header[data-v-3974e020]{display:flex;flex-direction:column;gap:var(--space-sm)}.admin-section[data-v-3974e020]{margin-top:var(--space-xl)}.admin-section-body[data-v-3974e020],.admin-panels[data-v-3974e020]{display:flex;flex-direction:column;gap:var(--space-xl)}
@@ -1 +0,0 @@
1
- import{_ as re,r as $,c as H,b as v,d as m,e as a,f as x,t as A,F as U,i as J,j as we,C as X,B as _,D as ie,l as $e,L as he,y as V,x as ae,E as pe,H as M,A as je,k as z,h as ne,v as oe,z as be,o as Ge,m as He,a as Je,q as We,u as Ye,w as Ze,J as Qe,K as Xe}from"./_plugin-vue_export-helper-rKFEraYH.js";import{u as De,U as et,_ as tt,a as st,R as nt,N as ce,M as ot,b as at,L as it,A as rt,B as lt,c as ut}from"./AccessDenied-C-lL9vbN.js";import"./helpers-DzjFIx78.js";const dt={key:0},ct=["onClick"],mt=["onClick"],ft={class:"role-names"},gt={class:"center"},yt={key:0,class:"center"},vt=["onClick"],ht={key:0,class:"permissions-section"},pt={class:"matrix-scroll"},bt=["title"],wt=["title"],$t={class:"display-text"},Dt=["checked","onChange"],kt={class:"perm-name-cell"},Rt={class:"perm-title"},Ot={class:"display-text"},St=["onClick"],Ct={class:"perm-id-info"},Ut={class:"id-text"},At={class:"perm-members center"},Pt={class:"perm-actions center"},Mt=["onClick"],Et={__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:I,emit:K}){const b=n,y=K,R=$(null),E=$(null),C=$(null),q=$(null),N=$(null),L=$(null),T=H(()=>[...b.orgs].sort((s,t)=>{const o=s.display_name.localeCompare(t.display_name);return o!==0?o:s.uuid.localeCompare(t.uuid)})),w=H(()=>[...b.permissions].sort((s,t)=>s.id.localeCompare(t.id)));function W(s){return s.roles.slice().sort((t,o)=>t.display_name.localeCompare(o.display_name)).map(t=>t.display_name).join(", ")}function h(s,t){if(b.navigationDisabled)return;const o=_(s);if(!o)return;const r=s.target,d=r.closest("tr");if(!d)return;const D=d.closest("tbody");if(!D)return;const c=Array.from(D.querySelectorAll("tr")),u=c.indexOf(d);if(u===-1)return;if(o==="left"||o==="right"){s.preventDefault();const S=Array.from(d.querySelectorAll("a, button:not([disabled])")),F=S.indexOf(r);if(F===-1)return;o==="left"&&F>0?S[F-1].focus():o==="right"&&F<S.length-1&&S[F+1].focus();return}let k=u;if(o==="up"&&u>0)k=u-1;else if(o==="down"&&u<c.length-1)k=u+1;else if(o==="up"&&u===0){s.preventDefault(),t==="org"?X(E.value,{itemSelector:"button"}):t==="perm"&&X(N.value,{itemSelector:"button"});return}else if(o==="down"&&u===c.length-1){if(s.preventDefault(),t==="org"&&b.info.is_global_admin&&q.value){const S=q.value.querySelector('input[type="checkbox"]');S?S.focus():X(N.value,{itemSelector:"button"})}return}if(k!==u){s.preventDefault();const F=c[k].querySelector("a, button:not([disabled])");F&&F.focus()}}function Y(s){if(b.navigationDisabled)return;const t=_(s);if(t){if(s.preventDefault(),t==="left"||t==="right")ie(E.value,s.target,t,{itemSelector:"button"});else if(t==="up")y("navigateOut","up");else if(t==="down"){const o=C.value?.querySelector("tbody tr a, tbody tr button:not([disabled])");o&&o.focus()}}}function Z(s){if(b.navigationDisabled)return;const t=_(s);if(!t)return;const o=s.target;if(o.tagName!=="INPUT")return;s.preventDefault();const r=Array.from(q.value.querySelectorAll('input[type="checkbox"]')),d=r.indexOf(o);if(d===-1)return;const D=T.value.length,c=w.value.length;if(D===0||c===0)return;const u=Math.floor(d/D),k=d%D;let S=d;if(t==="left")k>0&&(S=d-1);else if(t==="right")k<D-1&&(S=d+1);else if(t==="up")if(u>0)S=d-D;else{const ee=C.value?.querySelector("tbody tr:last-child")?.querySelector("a, button:not([disabled])");ee&&ee.focus();return}else if(t==="down")if(u<c-1)S=d+D;else{X(N.value,{itemSelector:"button"});return}S!==d&&r[S]&&r[S].focus()}function Q(s){if(b.navigationDisabled)return;const t=_(s);if(t){if(s.preventDefault(),t==="left"||t==="right")ie(N.value,s.target,t,{itemSelector:"button"});else if(t==="up"){const o=q.value?.querySelectorAll('input[type="checkbox"]');if(o?.length){const r=T.value.length,D=(w.value.length-1)*r;o[D]?o[D].focus():o[0].focus()}else{const d=C.value?.querySelector("tbody tr:last-child")?.querySelector("a, button:not([disabled])");d&&d.focus()}}else if(t==="down"){const o=L.value?.querySelector("tbody tr button:not([disabled])");o&&o.focus()}}}function f(){if(b.info.is_global_admin)X(E.value,{itemSelector:"button"});else{const s=C.value?.querySelector("tbody tr a, tbody tr button:not([disabled])");s&&s.focus()}}return I({focusFirstElement:f}),(s,t)=>(m(),v(U,null,[a("div",{class:"permissions-section",ref_key:"orgSection",ref:R},[a("h2",null,A(n.info.is_global_admin?"Organizations":"Your Organizations"),1),a("div",{class:"actions",ref_key:"orgActionsRef",ref:E,onKeydown:Y},[n.info.is_global_admin?(m(),v("button",{key:0,onClick:t[0]||(t[0]=o=>s.$emit("createOrg"))},"+ Create Org")):x("",!0)],544),a("table",{class:"org-table",ref_key:"orgTableRef",ref:C,onKeydown:t[1]||(t[1]=o=>h(o,"org"))},[a("thead",null,[a("tr",null,[t[4]||(t[4]=a("th",null,"Name",-1)),t[5]||(t[5]=a("th",null,"Roles",-1)),t[6]||(t[6]=a("th",null,"Members",-1)),n.info.is_global_admin?(m(),v("th",dt,"Actions")):x("",!0)])]),a("tbody",null,[(m(!0),v(U,null,J(T.value,o=>(m(),v("tr",{key:o.uuid},[a("td",null,[a("a",{href:"#org/{{o.uuid}}",onClick:$e(r=>s.$emit("openOrg",o),["prevent"])},A(o.display_name),9,ct),n.info.is_global_admin||n.info.is_org_admin?(m(),v("button",{key:0,onClick:r=>s.$emit("updateOrg",o),class:"icon-btn edit-org-btn","aria-label":"Rename organization",title:"Rename organization"},"✏️",8,mt)):x("",!0)]),a("td",ft,A(W(o)),1),a("td",gt,A(o.roles.reduce((r,d)=>r+d.users.length,0)),1),n.info.is_global_admin?(m(),v("td",yt,[a("button",{onClick:r=>s.$emit("deleteOrg",o),class:"icon-btn delete-icon","aria-label":"Delete organization",title:"Delete organization"},"❌",8,vt)])):x("",!0)]))),128))])],544)],512),n.info.is_global_admin?(m(),v("div",ht,[t[10]||(t[10]=a("h2",null,"Permissions",-1)),a("div",{class:"matrix-wrapper",ref_key:"permMatrixRef",ref:q,onKeydown:Z},[a("div",pt,[a("div",{class:"perm-matrix-grid",style:we({gridTemplateColumns:"minmax(180px, 1fr) "+T.value.map(()=>"2.2rem").join(" ")})},[t[7]||(t[7]=a("div",{class:"grid-head perm-head"},"Permission",-1)),(m(!0),v(U,null,J(T.value,o=>(m(),v("div",{key:"head-"+o.uuid,class:"grid-head org-head",title:o.display_name},[a("span",null,A(o.display_name),1)],8,bt))),128)),(m(!0),v(U,null,J(w.value,o=>(m(),v(U,{key:o.id},[a("div",{class:"perm-name",title:o.id},[a("span",$t,A(o.display_name),1)],8,wt),(m(!0),v(U,null,J(T.value,r=>(m(),v("div",{key:r.uuid+"-"+o.id,class:"matrix-cell"},[a("input",{type:"checkbox",checked:r.permissions.includes(o.id),onChange:d=>s.$emit("toggleOrgPermission",r,o.id,d.target.checked)},null,40,Dt)]))),128))],64))),128))],4)]),t[8]||(t[8]=a("p",{class:"matrix-hint muted"},"Toggle which permissions each organization can grant to its members.",-1))],544),a("div",{class:"actions",ref_key:"permActionsRef",ref:N,onKeydown:Q},[n.info.is_global_admin?(m(),v("button",{key:0,onClick:t[2]||(t[2]=o=>s.$emit("openDialog","perm-create",{display_name:"",id:""}))},"+ Create Permission")):x("",!0)],544),a("table",{class:"org-table",ref_key:"permTableRef",ref:L,onKeydown:t[3]||(t[3]=o=>h(o,"perm"))},[t[9]||(t[9]=a("thead",null,[a("tr",null,[a("th",{scope:"col"},"Permission"),a("th",{scope:"col",class:"center"},"Members"),a("th",{scope:"col",class:"center"},"Actions")])],-1)),a("tbody",null,[(m(!0),v(U,null,J(w.value,o=>(m(),v("tr",{key:o.id},[a("td",kt,[a("div",Rt,[a("span",Ot,A(o.display_name),1),a("button",{onClick:r=>s.$emit("renamePermissionDisplay",o),class:"icon-btn edit-display-btn","aria-label":"Edit display name",title:"Edit display name"},"✏️",8,St)]),a("div",Ct,[a("span",Ut,A(o.id),1)])]),a("td",At,A(n.permissionSummary[o.id]?.userCount||0),1),a("td",Pt,[a("button",{onClick:r=>s.$emit("deletePermission",o),class:"icon-btn delete-icon","aria-label":"Delete permission",title:"Delete permission"},"❌",8,Mt)])]))),128))])],544)])):x("",!0)],64))}},qt=re(Et,[["__scopeId","data-v-fabb2aac"]]),Nt=["title"],Tt={class:"org-name"},xt={class:"matrix-scroll"},It=["title"],Ft=["title"],Lt=["checked","onChange"],Kt=["onDrop"],Bt=["onKeydown"],zt=["title"],_t=["onClick"],Vt={class:"role-actions"},jt=["onClick"],Gt=["onDragstart","onClick","onKeydown","title"],Ht={class:"name"},Jt={class:"meta"},Wt=["onKeydown"],Yt=["onClick"],Zt={__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:I,emit:K}){const b=n,y=K,R=$(null),E=$(null),C=$(null),q=H(()=>[...b.selectedOrg.roles].sort((f,s)=>{const t=f.display_name.toLowerCase(),o=s.display_name.toLowerCase();return t!==o?t.localeCompare(o):f.uuid.localeCompare(s.uuid)}));function N(f){return b.permissions.find(s=>s.id===f)?.display_name||f}function L(f,s,t){y("toggleRolePermission",f,s,t)}function T(f){if(b.navigationDisabled)return;const s=_(f);if(s){if(f.preventDefault(),s==="left"||s==="right")ie(R.value,f.target,s,{itemSelector:"button"});else if(s==="up")y("navigateOut","up");else if(s==="down"){const t=E.value?.querySelector('input[type="checkbox"]');t?t.focus():Z()}}}function w(f){if(b.navigationDisabled)return;const s=_(f);if(!s)return;const t=f.target;if(t.tagName!=="INPUT")return;f.preventDefault();const o=Array.from(E.value.querySelectorAll('input[type="checkbox"]')),r=o.indexOf(t);if(r===-1)return;const d=q.value.length,D=b.selectedOrg.permissions.length,c=Math.floor(r/d),u=r%d;let k=r;if(s==="left"&&u>0)k=r-1;else if(s==="right"&&u<d-1)k=r+1;else if(s==="up"&&c>0)k=r-d;else if(s==="down"&&c<D-1)k=r+d;else if(s==="up"&&c===0){const S=R.value?.querySelector("button");S&&S.focus();return}else if(s==="down"&&c===D-1){Z();return}k!==r&&o[k]&&o[k].focus()}function W(f){if(b.navigationDisabled)return;const s=_(f);if(!s)return;const t=f.target;if(!t.classList.contains("user-chip"))return;const o=t.closest(".user-list");if(!o)return;const r=Array.from(o.querySelectorAll(".user-chip")),d=r.indexOf(t);if(d!==-1){if(s==="up"&&d>0){f.preventDefault(),r[d-1].focus();return}else if(s==="down"&&d<r.length-1){f.preventDefault(),r[d+1].focus();return}if(s==="up"&&d===0){f.preventDefault();const c=o.closest(".role-column")?.querySelector(".role-header button");c&&c.focus();return}if(!(s==="down"&&d===r.length-1)&&(s==="left"||s==="right")){f.preventDefault();const D=Array.from(C.value?.querySelectorAll(".role-column")||[]),c=o.closest(".role-column"),u=D.indexOf(c);let k=s==="left"?u-1:u+1;if(k>=0&&k<D.length){const S=D[k],F=S.querySelectorAll(".user-chip"),ee=Math.min(d,F.length-1);if(F[ee])F[ee].focus();else{const le=S.querySelector(".plus-btn");le&&le.focus()}}else if(s==="left"&&u===0){const S=E.value?.querySelector('input[type="checkbox"]:last-of-type');S&&S.focus()}}}}function h(f,s){if(b.navigationDisabled)return;const t=_(f);if(!t)return;const o=Array.from(C.value?.querySelectorAll(".role-column")||[]);if(t==="left"||t==="right"){f.preventDefault();const r=f.currentTarget.querySelectorAll("button:not([disabled])"),d=Array.from(r).indexOf(f.target);if(t==="left"&&d>0)r[d-1].focus();else if(t==="right"&&d<r.length-1)r[d+1].focus();else if(t==="left"&&d===0&&s>0){const c=o[s-1]?.querySelectorAll(".role-header button");c?.length&&c[c.length-1].focus()}else if(t==="right"&&d===r.length-1&&s<o.length-1){const c=o[s+1]?.querySelector(".role-header button");c&&c.focus()}}else if(t==="up"){f.preventDefault();const r=E.value?.querySelectorAll('input[type="checkbox"]');if(r?.length){const d=q.value.length,c=(b.selectedOrg.permissions.length-1)*d+s;r[c]?r[c].focus():r[r.length-1].focus()}else{const d=R.value?.querySelector("button");d&&d.focus()}}else if(t==="down"){f.preventDefault();const d=o[s]?.querySelector(".user-chip");d&&d.focus()}}function Y(f,s){if(b.navigationDisabled)return;const t=_(f);if(!t)return;const o=Array.from(C.value?.querySelectorAll(".role-column")||[]);if(t==="up"){f.preventDefault();const d=o[s]?.querySelector(".role-header button");d&&d.focus()}else if(t==="left"&&s>0){f.preventDefault();const r=o[s-1],d=r?.querySelector(".empty-role button"),D=r?.querySelector(".user-chip:last-child");d?d.focus():D&&D.focus()}else if(t==="right"&&s<o.length-1){f.preventDefault();const r=o[s+1],d=r?.querySelector(".empty-role button"),D=r?.querySelector(".user-chip");d?d.focus():D&&D.focus()}}function Z(){const s=C.value?.querySelector(".role-column")?.querySelector(".role-header button");s&&s.focus()}function Q(){const f=R.value?.querySelector("button");f&&f.focus()}return I({focusFirstElement:Q}),(f,s)=>(m(),v(U,null,[a("h2",{class:"org-title",ref_key:"orgTitleRef",ref:R,onKeydown:T,title:n.selectedOrg.uuid},[a("span",Tt,A(n.selectedOrg.display_name),1),a("button",{onClick:s[0]||(s[0]=t=>f.$emit("updateOrg",n.selectedOrg)),class:"icon-btn","aria-label":"Rename organization",title:"Rename organization"},"✏️")],40,Nt),a("div",{class:"matrix-wrapper",ref_key:"permMatrixRef",ref:E,onKeydown:w},[a("div",xt,[a("div",{class:"perm-matrix-grid",style:we({gridTemplateColumns:"minmax(180px, 1fr) "+q.value.map(()=>"2.2rem").join(" ")+" 2.2rem"})},[s[5]||(s[5]=a("div",{class:"grid-head perm-head"},"Permission",-1)),(m(!0),v(U,null,J(q.value,t=>(m(),v("div",{key:"head-"+t.uuid,class:"grid-head role-head",title:t.display_name},[a("span",null,A(t.display_name),1)],8,It))),128)),a("div",{class:"grid-head role-head add-role-head",title:"Add role",onClick:s[1]||(s[1]=t=>f.$emit("createRole",n.selectedOrg)),role:"button",tabindex:"0",onKeydown:s[2]||(s[2]=he(t=>f.$emit("createRole",n.selectedOrg),["enter"]))},"➕",32),(m(!0),v(U,null,J(n.selectedOrg.permissions,t=>(m(),v(U,{key:t},[a("div",{class:"perm-name",title:t},A(N(t)),9,Ft),(m(!0),v(U,null,J(q.value,o=>(m(),v("div",{key:o.uuid+"-"+t,class:"matrix-cell"},[a("input",{type:"checkbox",checked:o.permissions.includes(t),onChange:r=>L(o,t,r.target.checked)},null,40,Lt)]))),128)),s[4]||(s[4]=a("div",{class:"matrix-cell add-role-cell"},null,-1))],64))),128))],4)]),s[6]||(s[6]=a("p",{class:"matrix-hint muted"},"Toggle which permissions each role grants.",-1))],544),a("div",{class:"roles-grid",ref_key:"rolesGridRef",ref:C},[(m(!0),v(U,null,J(q.value,(t,o)=>(m(),v("div",{key:t.uuid,class:"role-column",onDragover:s[3]||(s[3]=r=>f.$emit("onRoleDragOver",r)),onDrop:r=>f.$emit("onRoleDrop",r,n.selectedOrg,t)},[a("div",{class:"role-header",onKeydown:r=>h(r,o)},[a("strong",{class:"role-name",title:t.uuid},[a("span",null,A(t.display_name),1),a("button",{onClick:r=>f.$emit("updateRole",t),class:"icon-btn","aria-label":"Edit role",title:"Edit role"},"✏️",8,_t)],8,zt),a("div",Vt,[a("button",{onClick:r=>f.$emit("createUserInRole",n.selectedOrg,t),class:"plus-btn","aria-label":"Add user",title:"Add user"},"➕",8,jt)])],40,Bt),t.users.length>0?(m(),v("ul",{key:0,class:"user-list",onKeydown:W},[(m(!0),v(U,null,J(t.users.slice().sort((r,d)=>{const D=r.display_name.toLowerCase(),c=d.display_name.toLowerCase();return D!==c?D.localeCompare(c):r.uuid.localeCompare(d.uuid)}),r=>(m(),v("li",{key:r.uuid,class:"user-chip",tabindex:"0",draggable:"true",onDragstart:d=>f.$emit("onUserDragStart",d,r,n.selectedOrg.uuid),onClick:d=>f.$emit("openUser",r),onKeydown:he(d=>f.$emit("openUser",r),["enter"]),title:r.uuid},[a("span",Ht,A(r.display_name),1),a("span",Jt,A(r.last_seen?new Date(r.last_seen).toLocaleDateString():"—"),1)],40,Gt))),128))],32)):(m(),v("div",{key:1,class:"empty-role",onKeydown:r=>Y(r,o)},[s[7]||(s[7]=a("p",{class:"empty-text muted"},"No members",-1)),a("button",{onClick:r=>f.$emit("deleteRole",t),class:"icon-btn delete-icon","aria-label":"Delete empty role",title:"Delete role"},"❌",8,Yt)],40,Wt))],40,Kt))),128))],512)],64))}},Qt=re(Zt,[["__scopeId","data-v-25f08cdd"]]),Xt={class:"user-detail"},es={key:0,class:"error small"},ts=["disabled"],ss={class:"section-block","data-section":"registered-passkeys"},ns={class:"section-body"},os={__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:I,emit:K}){const b=n,y=K,R=De(),E=$({}),C=$(null),q=$(null),N=$(null),L=$(null),T=$(null),w=$(null),W=$(null),h=H(()=>b.showRegModal);function Y(){R.showMessage(`📋 Link copied! Send it to ${b.selectedUser.display_name}.`),y("closeRegModal")}function Z(){y("editUserName",b.selectedUser)}async function Q(c){try{const u=await M(`/auth/api/admin/orgs/${b.selectedUser.org_uuid}/users/${b.selectedUser.uuid}/credentials/${c.credential_uuid}`,{method:"DELETE"});u.status==="ok"?y("onUserNameSaved"):console.error("Failed to delete credential",u)}catch(u){console.error("Delete credential error",u)}}async function f(c){const u=c?.id;if(u){E.value={...E.value,[u]:!0};try{const k=await M(`/auth/api/admin/orgs/${b.selectedUser.org_uuid}/users/${b.selectedUser.uuid}/sessions/${u}`,{method:"DELETE"});if(k.status==="ok"){if(k.current_session_terminated){sessionStorage.clear(),location.reload();return}y("refreshUserDetail"),R.showMessage("Session terminated","success",2500)}else R.showMessage(k.detail||"Failed to terminate session","error")}catch(k){console.error("Terminate session error",k),R.showMessage(k.message||"Failed to terminate session","error")}finally{const k={...E.value};delete k[u],E.value=k}}}function s(c){if(h.value||b.navigationDisabled)return;const u=_(c);u&&(c.preventDefault(),u==="left"||u==="right"?ie(N.value,c.target,u,{itemSelector:".mini-btn"}):u==="up"?y("navigateOut","up"):u==="down"&&X(L.value,{itemSelector:"button"}))}function t(c){if(h.value||b.navigationDisabled)return;const u=_(c);u&&(c.preventDefault(),u==="left"||u==="right"?ie(L.value,c.target,u,{itemSelector:"button"}):u==="up"?X(N.value,{itemSelector:".mini-btn"}):u==="down"&&T.value?.$el?.focus())}function o(c){h.value||b.navigationDisabled||(c==="up"?X(L.value,{itemSelector:"button"}):c==="down"&&pe(w.value?.$el,0,{itemSelector:".session-group"}))}function r(c){if(!(h.value||b.navigationDisabled)){if(c==="up")T.value?.$el?.focus();else if(c==="down"){const u=W.value?.querySelector("button");u&&u.focus()}}}function d(c){if(h.value||b.navigationDisabled)return;const u=_(c);u&&(c.preventDefault(),u==="up"&&pe(w.value?.$el,-1,{itemSelector:".session-group"}))}function D(){X(N.value,{itemSelector:".mini-btn"})}return I({focusFirstElement:D}),(c,u)=>(m(),v("div",Xt,[a("div",{ref_key:"userInfoRef",ref:N,onKeydown:s},[n.userDetail&&!n.userDetail.error?(m(),V(et,{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_uuid}/users/${n.selectedUser.uuid}/display-name`,onSaved:u[0]||(u[0]=k=>c.$emit("onUserNameSaved")),onEditName:Z},null,8,["name","visits","created-at","last-seen","loading","org-display-name","role-name","update-endpoint"])):x("",!0)],544),n.userDetail?.error?(m(),v("div",es,A(n.userDetail.error),1)):x("",!0),n.userDetail&&!n.userDetail.error?(m(),v(U,{key:1},[a("div",{class:"registration-actions",ref_key:"regActionsRef",ref:L,onKeydown:t},[a("button",{class:"btn-secondary reg-token-btn",onClick:u[1]||(u[1]=k=>c.$emit("generateUserRegistrationLink",n.selectedUser)),disabled:n.loading},"Generate Registration Token",8,ts),u[6]||(u[6]=a("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),a("section",ss,[u[7]||(u[7]=a("div",{class:"section-header"},[a("h2",null,"Registered Passkeys")],-1)),a("div",ns,[ae(tt,{ref_key:"credentialListRef",ref:T,credentials:n.userDetail.credentials,"aaguid-info":n.userDetail.aaguid_info,"allow-delete":!0,"hovered-credential-uuid":C.value,"hovered-session-credential-uuid":q.value?.credential_uuid,"navigation-disabled":h.value,onDelete:Q,onCredentialHover:u[2]||(u[2]=k=>C.value=k),onNavigateOut:o},null,8,["credentials","aaguid-info","hovered-credential-uuid","hovered-session-credential-uuid","navigation-disabled"])])]),ae(st,{ref_key:"sessionListRef",ref:w,sessions:n.userDetail.sessions||[],"terminating-sessions":E.value,"hovered-credential-uuid":C.value,"navigation-disabled":h.value,"empty-message":"This user has no active sessions.","section-description":"View and manage the active sessions for this user.",onTerminate:f,onSessionHover:u[3]||(u[3]=k=>q.value=k),onNavigateOut:r},null,8,["sessions","terminating-sessions","hovered-credential-uuid","navigation-disabled"])],64)):x("",!0),a("div",{class:"actions ancillary-actions",ref_key:"backButtonRef",ref:W,onKeydown:d},[n.selectedOrg?(m(),v("button",{key:0,onClick:u[4]||(u[4]=k=>c.$emit("openOrg",n.selectedOrg)),class:"icon-btn",title:"Back to Org"},"↩️")):x("",!0)],544),n.showRegModal?(m(),V(nt,{key:2,endpoint:`/auth/api/admin/orgs/${n.selectedUser.org_uuid}/users/${n.selectedUser.uuid}/create-link`,"user-name":n.userDetail?.display_name||n.selectedUser.display_name,onClose:u[5]||(u[5]=k=>c.$emit("closeRegModal")),onCopied:Y},null,8,["endpoint","user-name"])):x("",!0)]))}},as=re(os,[["__scopeId","data-v-70289426"]]),is={class:"modal-title"},rs={key:0},ls={key:2},us={class:"small muted"},ds=["placeholder","pattern"],cs={key:7},ms={key:8,class:"error small"},fs={key:9,class:"modal-actions"},gs=["disabled"],ys=["disabled"],vs={__name:"AdminDialogs",props:{dialog:Object,PERMISSION_ID_PATTERN:String},emits:["submitDialog","closeDialog"],setup(n,{emit:I}){const K=new Set(["org-update","role-update","user-update-name"]);return(b,y)=>n.dialog.type?(m(),V(ot,{key:0,onClose:y[13]||(y[13]=R=>b.$emit("closeDialog"))},{default:je(()=>[a("h3",is,[n.dialog.type==="org-create"?(m(),v(U,{key:0},[z("Create Organization")],64)):n.dialog.type==="org-update"?(m(),v(U,{key:1},[z("Rename Organization")],64)):n.dialog.type==="role-create"?(m(),v(U,{key:2},[z("Create Role")],64)):n.dialog.type==="role-update"?(m(),v(U,{key:3},[z("Edit Role")],64)):n.dialog.type==="user-create"?(m(),v(U,{key:4},[z("Add User To Role")],64)):n.dialog.type==="user-update-name"?(m(),v(U,{key:5},[z("Edit User Name")],64)):n.dialog.type==="perm-create"||n.dialog.type==="perm-display"?(m(),v(U,{key:6},[z(A(n.dialog.type==="perm-create"?"Create Permission":"Edit Permission Display"),1)],64)):n.dialog.type==="confirm"?(m(),v(U,{key:7},[z("Confirm")],64)):x("",!0)]),a("form",{onSubmit:y[12]||(y[12]=$e(R=>b.$emit("submitDialog"),["prevent"])),class:"modal-form"},[n.dialog.type==="org-create"?(m(),v("label",rs,[y[14]||(y[14]=z("Name ",-1)),ne(a("input",{ref:"nameInput","onUpdate:modelValue":y[0]||(y[0]=R=>n.dialog.data.name=R),required:""},null,512),[[oe,n.dialog.data.name]])])):n.dialog.type==="org-update"?(m(),V(ce,{key:1,label:"Organization Name",modelValue:n.dialog.data.name,"onUpdate:modelValue":y[1]||(y[1]=R=>n.dialog.data.name=R),busy:n.dialog.busy,error:n.dialog.error,onCancel:y[2]||(y[2]=R=>b.$emit("closeDialog"))},null,8,["modelValue","busy","error"])):n.dialog.type==="role-create"?(m(),v("label",ls,[y[15]||(y[15]=z("Role Name ",-1)),ne(a("input",{"onUpdate:modelValue":y[3]||(y[3]=R=>n.dialog.data.name=R),placeholder:"Role name",required:""},null,512),[[oe,n.dialog.data.name]])])):n.dialog.type==="role-update"?(m(),V(ce,{key:3,label:"Role Name",modelValue:n.dialog.data.name,"onUpdate:modelValue":y[4]||(y[4]=R=>n.dialog.data.name=R),busy:n.dialog.busy,error:n.dialog.error,onCancel:y[5]||(y[5]=R=>b.$emit("closeDialog"))},null,8,["modelValue","busy","error"])):n.dialog.type==="user-create"?(m(),v(U,{key:4},[a("p",us,"Role: "+A(n.dialog.data.role.display_name),1),a("label",null,[y[16]||(y[16]=z("Display Name ",-1)),ne(a("input",{"onUpdate:modelValue":y[6]||(y[6]=R=>n.dialog.data.name=R),placeholder:"User display name",required:""},null,512),[[oe,n.dialog.data.name]])])],64)):n.dialog.type==="user-update-name"?(m(),V(ce,{key:5,label:"Display Name",modelValue:n.dialog.data.name,"onUpdate:modelValue":y[7]||(y[7]=R=>n.dialog.data.name=R),busy:n.dialog.busy,error:n.dialog.error,onCancel:y[8]||(y[8]=R=>b.$emit("closeDialog"))},null,8,["modelValue","busy","error"])):n.dialog.type==="perm-create"||n.dialog.type==="perm-display"?(m(),v(U,{key:6},[a("label",null,[y[17]||(y[17]=z("Display Name ",-1)),ne(a("input",{ref:"displayNameInput","onUpdate:modelValue":y[9]||(y[9]=R=>n.dialog.data.display_name=R),required:""},null,512),[[oe,n.dialog.data.display_name]])]),a("label",null,[y[18]||(y[18]=z("Permission ID ",-1)),ne(a("input",{"onUpdate:modelValue":y[10]||(y[10]=R=>n.dialog.data.id=R),placeholder:n.dialog.type==="perm-create"?"yourapp:permission":n.dialog.data.permission.id,required:"",pattern:n.PERMISSION_ID_PATTERN,title:"Allowed: A-Za-z0-9:._~-","data-form-type":"other"},null,8,ds),[[oe,n.dialog.data.id]])]),y[19]||(y[19]=a("p",{class:"small muted"},"The permission ID is used for permission checks in the application. Changing it may break deployed applications that reference this permission.",-1))],64)):n.dialog.type==="confirm"?(m(),v("p",cs,A(n.dialog.data.message),1)):x("",!0),n.dialog.error&&!be(K).has(n.dialog.type)?(m(),v("div",ms,A(n.dialog.error),1)):x("",!0),be(K).has(n.dialog.type)?x("",!0):(m(),v("div",fs,[a("button",{type:"button",class:"btn-secondary",onClick:y[11]||(y[11]=R=>b.$emit("closeDialog")),disabled:n.dialog.busy}," Cancel ",8,gs),a("button",{type:"submit",class:"btn-primary",disabled:n.dialog.busy},A(n.dialog.type==="confirm"?"OK":"Save"),9,ys)]))],32)]),_:1})):x("",!0)}},hs=re(vs,[["__scopeId","data-v-b4640c15"]]),ps={class:"app-shell admin-shell"},bs={class:"app-main"},ws={key:2,class:"view-root view-root--wide view-admin"},$s={class:"view-header"},Ds={class:"section-block admin-section"},ks={class:"section-body admin-section-body"},Rs={key:0,class:"surface surface--tight error"},Os={key:1,class:"admin-panels"},Ss="^[A-Za-z0-9:._~-]+$",Cs={__name:"AdminApp",setup(n){const I=$(null),K=$(!0),b=$("Loading..."),y=$(!1),R=$(!1),E=$(null),C=$([]),q=$([]),N=$(null),L=$(null),T=$(null),w=De(),W=$(null);$(null),$(""),$(null),$("");const h=$({type:null,data:null,busy:!1,error:""}),Y=$(null),Z=$(null),Q=$(null),f=$(null),s=$(null),t=H(()=>h.value.type!==null||ue.value);function o(e){if(!W.value)return;const i=e.target.closest(".org-add-menu"),l=e.target.closest(".add-org-btn");!i&&!l&&(W.value=null)}Ge(async()=>{document.addEventListener("click",o),window.addEventListener("hashchange",D);const e=await He();e?.rp_name&&(document.title=e.rp_name+" Admin"),await S()}),Je(()=>{document.removeEventListener("click",o),window.removeEventListener("hashchange",D)});const r=H(()=>{const e={};for(const l of C.value){const g={uuid:l.uuid,display_name:l.display_name},p=new Set(l.permissions||[]);for(const O of l.permissions||[])e[O]||(e[O]={orgs:[],orgSet:new Set,userCount:0}),e[O].orgSet.has(l.uuid)||(e[O].orgs.push(g),e[O].orgSet.add(l.uuid));for(const O of l.roles)for(const P of O.permissions)p.has(P)&&(e[P]||(e[P]={orgs:[],orgSet:new Set,userCount:0}),e[P].orgSet.has(l.uuid)||(e[P].orgs.push(g),e[P].orgSet.add(l.uuid)),e[P].userCount+=O.users.length)}const i={};for(const[l,g]of Object.entries(e))i[l]={orgs:g.orgs.sort((p,O)=>p.display_name.localeCompare(O.display_name)),userCount:g.userCount};return i});function d(e){j("perm-display",{permission:e,id:e.id,display_name:e.display_name})}function D(){const e=window.location.hash||"";N.value=null,L.value=null,e.startsWith("#org/")?N.value=e.slice(5):e.startsWith("#user/")&&(L.value=e.slice(6))}async function c(){const e=await M("/auth/api/admin/orgs");C.value=e.map(i=>{const l=i.roles.map(p=>({...p,org_uuid:i.uuid,users:[]})),g=Object.fromEntries(l.map(p=>[p.display_name,p]));for(const p of i.users||[])g[p.role]&&g[p.role].users.push(p);return{...i,roles:l}})}async function u(){q.value=await M("/auth/api/admin/permissions")}async function k(){I.value=await M("/auth/api/user-info",{method:"POST"}),y.value=!0}async function S(){K.value=!0,b.value="Loading...",E.value=null;try{await Promise.all([c(),u()]),await k(),!I.value.is_global_admin&&I.value.is_org_admin&&C.value.length===1&&(!window.location.hash||window.location.hash==="#overview")?(N.value=C.value[0].uuid,window.location.hash=`#org/${N.value}`,w.showMessage(`Navigating to ${C.value[0].display_name} Administration`,"info",3e3)):D()}catch(e){e.name==="AuthCancelledError"?R.value=!0:E.value=e.message}finally{K.value=!1}}function F(){j("org-create",{})}function ee(e){j("org-update",{org:e,name:e.display_name})}function le(e){j("user-update-name",{user:e,name:e.display_name})}async function me(e){await M(`/auth/api/admin/orgs/${e}`,{method:"DELETE"}),await Promise.all([c(),u()])}function Re(e){if(!I.value?.is_global_admin){w.showMessage("Global admin only");return}if(e.roles.reduce((p,O)=>p+O.users.length,0)===0){me(e.uuid).then(()=>{w.showMessage(`Organization "${e.display_name}" deleted.`,"success",2500)}).catch(p=>{w.showMessage(p.message||"Failed to delete organization","error")});return}const g=e.roles.filter(p=>p.users.length>0).map(p=>`${p.users.length} ${p.display_name}`).join(", ");j("confirm",{message:`Delete organization "${e.display_name}", including accounts of ${g})?`,action:async()=>{await me(e.uuid)}})}function Oe(e,i){j("user-create",{org:e,role:i})}async function Se(e,i,l){if(i.role!==l)try{await M(`/auth/api/admin/orgs/${e.uuid}/users/${i.uuid}/role`,{method:"PUT",body:{role:l}}),await c()}catch(g){w.showMessage(g.message||"Failed to update user role")}}function Ce(e,i,l){e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",JSON.stringify({user_uuid:i.uuid,org_uuid:l}))}function Ue(e){e.preventDefault(),e.dataTransfer.dropEffect="move"}function Ae(e,i,l){e.preventDefault();try{const g=JSON.parse(e.dataTransfer.getData("text/plain"));if(g.org_uuid!==i.uuid)return;const p=i.roles.flatMap(O=>O.users).find(O=>O.uuid===g.user_uuid);p&&Se(i,p,l.display_name)}catch{}}function Pe(e){j("role-create",{org:e})}function Me(e){j("role-update",{role:e,name:e.display_name})}function Ee(e){M(`/auth/api/admin/orgs/${e.org_uuid}/roles/${e.uuid}`,{method:"DELETE"}).then(()=>{w.showMessage(`Role "${e.display_name}" deleted.`,"success",2500),c()}).catch(i=>{w.showMessage(i.message||"Failed to delete role","error")})}async function qe(e,i,l){const g=l?[...e.permissions,i]:e.permissions.filter(O=>O!==i),p=[...e.permissions];e.permissions=g;try{await M(`/auth/api/admin/orgs/${e.org_uuid}/roles/${e.uuid}`,{method:"PUT",body:{display_name:e.display_name,permissions:g}}),await c()}catch(O){w.showMessage(O.message||"Failed to update role permission"),e.permissions=p}}async function fe(e){const i=new URLSearchParams({permission_id:e});await M(`/auth/api/admin/permission?${i.toString()}`,{method:"DELETE"}),await u()}function Ne(e){const i=r.value[e.id]?.userCount||0;let l=0;for(const O of C.value)for(const P of O.roles)P.permissions.includes(e.id)&&l++;if(l===0){fe(e.id).then(()=>{w.showMessage(`Permission "${e.display_name}" deleted.`,"success",2500)}).catch(O=>{w.showMessage(O.message||"Failed to delete permission","error")});return}const g=[];l>0&&g.push(`${l} role${l!==1?"s":""}`),i>0&&g.push(`${i} user${i!==1?"s":""}`);const p=g.join(", ");j("confirm",{message:`Delete permission "${e.display_name}" (${p})?`,action:async()=>{await fe(e.id)}})}function Te(){window.location.reload()}const se=H(()=>C.value.find(e=>e.uuid===N.value)||null);function ge(e){window.location.hash=`#org/${e.uuid}`}function xe(){window.location.hash="#overview"}function Ie(e){window.location.hash=`#user/${e.uuid}`}const B=H(()=>{if(!L.value)return null;for(const e of C.value)for(const i of e.roles){const l=i.users.find(g=>g.uuid===L.value);if(l)return{...l,org_uuid:e.uuid,role_display_name:i.display_name}}return null}),Fe=H(()=>B.value?"Admin: User":se.value?"Admin: Org":(w.settings?.rp_name||"Master")+" Admin"),Le=H(()=>{const e=[{label:"Auth",href:We()},{label:"Admin",href:Ye()}];let i=null;B.value&&(i=C.value.find(g=>g.uuid===B.value.org_uuid)||null);const l=se.value||i;return l&&e.push({label:l.display_name,href:`#org/${l.uuid}`}),B.value&&e.push({label:B.value.display_name||"User",href:`#user/${B.value.uuid}`}),e});Ze(B,async e=>{if(!e){T.value=null;return}try{T.value=await M(`/auth/api/admin/orgs/${e.org_uuid}/users/${e.uuid}`)}catch(i){T.value={error:i.message}}});const ue=$(!1);function Ke(e){ue.value=!0}async function Be(e,i,l){const g=e.permissions.includes(i);if(l&&g||!l&&!g)return;const p=l?[...e.permissions,i]:e.permissions.filter(P=>P!==i),O=[...e.permissions];e.permissions=p;try{const P=new URLSearchParams({permission_id:i});await M(`/auth/api/admin/orgs/${e.uuid}/permission?${P.toString()}`,{method:l?"POST":"DELETE"}),await c()}catch(P){w.showMessage(P.message||"Failed to update organization permission"),e.permissions=O}}function j(e,i){const l=document.activeElement;if(Y.value=l,e==="confirm"&&l){const g=l.closest("tr");if(g){const p=g.closest("tbody");if(p){const O=Array.from(p.querySelectorAll("tr")),P=O.indexOf(g);h.value.focusContext={tbody:p,index:P,total:O.length,selector:"button:not([disabled]), a"}}}}h.value={...h.value,type:e,data:i,busy:!1,error:""}}function G(){const e=Y.value,i=h.value.focusContext;h.value={type:null,data:null,busy:!1,error:""},ze(e,i),Y.value=null}function ze(e,i){if(!e)return;if(document.body.contains(e)&&!e.disabled){e.focus();return}if(i?.tbody&&i.selector){const p=Array.from(i.tbody.querySelectorAll("tr"));if(p.length>0){const O=Math.min(i.index,p.length-1),te=p[O]?.querySelector(i.selector);if(te){te.focus();return}}}const l=document.querySelector(".admin-panels");if(!l)return;const g=l.querySelector('button:not([disabled]), a, input:not([disabled]), [tabindex="0"]');g&&g.focus()}function _e(e){if(t.value)return;const i=_(e);i&&i==="down"&&(e.preventDefault(),Q.value?Q.value.focusFirstElement?.():f.value?f.value.focusFirstElement?.():s.value&&s.value.focusFirstElement?.())}function de(e){t.value||e==="up"&&Z.value?.focusCurrent?.()}async function ye(){if(await c(),B.value)try{T.value=await M(`/auth/api/admin/orgs/${B.value.org_uuid}/users/${B.value.uuid}`)}catch(e){w.showMessage(e.message||"Failed to reload user","error")}}async function ve(){await ye(),w.showMessage("User renamed","success",1500)}async function Ve(){if(!(!h.value.type||h.value.busy)){h.value.busy=!0,h.value.error="";try{const e=h.value.type;if(e==="org-create"){const i=h.value.data.name?.trim();if(!i)throw new Error("Name required");G(),M("/auth/api/admin/orgs",{method:"POST",body:{display_name:i,permissions:[]}}).then(()=>{w.showMessage(`Organization "${i}" created.`,"success",2500),Promise.all([c(),u()])}).catch(l=>{w.showMessage(l.message||"Failed to create organization","error")});return}else if(e==="org-update"){const{org:i}=h.value.data,l=h.value.data.name?.trim();if(!l)throw new Error("Name required");G(),M(`/auth/api/admin/orgs/${i.uuid}`,{method:"PUT",body:{display_name:l,permissions:i.permissions}}).then(()=>{w.showMessage(`Organization renamed to "${l}".`,"success",2500),c()}).catch(g=>{w.showMessage(g.message||"Failed to update organization","error")});return}else if(e==="role-create"){const{org:i}=h.value.data,l=h.value.data.name?.trim();if(!l)throw new Error("Name required");G(),M(`/auth/api/admin/orgs/${i.uuid}/roles`,{method:"POST",body:{display_name:l,permissions:[]}}).then(()=>{w.showMessage(`Role "${l}" created.`,"success",2500),c()}).catch(g=>{w.showMessage(g.message||"Failed to create role","error")});return}else if(e==="role-update"){const{role:i}=h.value.data,l=h.value.data.name?.trim();if(!l)throw new Error("Name required");G(),M(`/auth/api/admin/orgs/${i.org_uuid}/roles/${i.uuid}`,{method:"PUT",body:{display_name:l,permissions:i.permissions}}).then(()=>{w.showMessage(`Role renamed to "${l}".`,"success",2500),c()}).catch(g=>{w.showMessage(g.message||"Failed to update role","error")});return}else if(e==="user-create"){const{org:i,role:l}=h.value.data,g=h.value.data.name?.trim();if(!g)throw new Error("Name required");G(),M(`/auth/api/admin/orgs/${i.uuid}/users`,{method:"POST",body:{display_name:g,role:l.display_name}}).then(()=>{w.showMessage(`User "${g}" added to ${l.display_name} role.`,"success",2500),c()}).catch(p=>{w.showMessage(p.message||"Failed to add user","error")});return}else if(e==="user-update-name"){const{user:i}=h.value.data,l=h.value.data.name?.trim();if(!l)throw new Error("Name required");G(),M(`/auth/api/admin/orgs/${i.org_uuid}/users/${i.uuid}/display-name`,{method:"PUT",body:{display_name:l}}).then(()=>{w.showMessage(`User renamed to "${l}".`,"success",2500),ve()}).catch(g=>{w.showMessage(g.message||"Failed to update user name","error")});return}else if(e==="perm-display"){const{permission:i}=h.value.data,l=h.value.data.id?.trim(),g=h.value.data.display_name?.trim();if(!g)throw new Error("Display name required");if(!l)throw new Error("ID required");G();let p;if(l!==i.id)p=M("/auth/api/admin/permission/rename",{method:"POST",body:{old_id:i.id,new_id:l,display_name:g}});else if(g!==i.display_name){const te=new URLSearchParams({permission_id:i.id,display_name:g});p=M(`/auth/api/admin/permission?${te.toString()}`,{method:"PUT"})}else return;p.then(()=>{w.showMessage(`Permission "${g}" updated.`,"success",2500),u()}).catch(te=>{w.showMessage(te.message||"Failed to update permission","error")});return}else if(e==="confirm"){const i=h.value.data.action;i&&await i()}G()}catch(e){h.value.error=e.message||"Error"}finally{h.value.busy=!1}}}return(e,i)=>(m(),v("div",ps,[ae(at),a("main",bs,[K.value?(m(),V(it,{key:0,message:b.value},null,8,["message"])):R.value?(m(),V(rt,{key:1,onReload:Te})):y.value&&(I.value?.is_global_admin||I.value?.is_org_admin)?(m(),v("section",ws,[a("header",$s,[a("h1",null,A(Fe.value),1),ae(lt,{ref_key:"breadcrumbsRef",ref:Z,entries:Le.value,onKeydown:_e},null,8,["entries"])]),a("section",Ds,[a("div",ks,[E.value?(m(),v("div",Rs,A(E.value),1)):(m(),v("div",Os,[!B.value&&!se.value&&(I.value.is_global_admin||I.value.is_org_admin)?(m(),V(qt,{key:0,ref_key:"adminOverviewRef",ref:Q,info:I.value,orgs:C.value,permissions:q.value,"navigation-disabled":t.value,"permission-summary":r.value,onCreateOrg:F,onOpenOrg:ge,onUpdateOrg:ee,onDeleteOrg:Re,onToggleOrgPermission:Be,onOpenDialog:j,onDeletePermission:Ne,onRenamePermissionDisplay:d,onNavigateOut:de},null,8,["info","orgs","permissions","navigation-disabled","permission-summary"])):B.value?(m(),V(as,{key:1,ref_key:"adminUserDetailRef",ref:s,"selected-user":B.value,"user-detail":T.value,"selected-org":se.value,loading:K.value,"show-reg-modal":ue.value,"navigation-disabled":t.value,onGenerateUserRegistrationLink:Ke,onGoOverview:xe,onOpenOrg:ge,onOnUserNameSaved:ve,onRefreshUserDetail:ye,onEditUserName:le,onCloseRegModal:i[0]||(i[0]=l=>ue.value=!1),onNavigateOut:de},null,8,["selected-user","user-detail","selected-org","loading","show-reg-modal","navigation-disabled"])):se.value?(m(),V(Qt,{key:2,ref_key:"adminOrgDetailRef",ref:f,"selected-org":se.value,permissions:q.value,"navigation-disabled":t.value,onUpdateOrg:ee,onCreateRole:Pe,onUpdateRole:Me,onDeleteRole:Ee,onCreateUserInRole:Oe,onOpenUser:Ie,onToggleRolePermission:qe,onOnRoleDragOver:Ue,onNavigateOut:de,onOnRoleDrop:Ae,onOnUserDragStart:Ce},null,8,["selected-org","permissions","navigation-disabled"])):x("",!0)]))])])])):x("",!0)]),ae(hs,{dialog:h.value,"permission-id-pattern":Ss,onSubmitDialog:Ve,onCloseDialog:G},null,8,["dialog"])]))}},Us=re(Cs,[["__scopeId","data-v-3974e020"]]),ke=Qe(Us);ke.use(ut());ke.mount("#admin-app");Xe();
@@ -1 +0,0 @@
1
- import{_ as Me,r as d,w as Ze,c as L,o as Ee,n as ye,a as Be,b as w,d as v,e as a,f as V,g as $e,h as lt,F as de,i as rt,j as Le,t as W,k as F,v as it,l as Je,m as ut,s as ct,p as dt,q as ft,u as vt,x as ae,y as G,z as C,A as Ke,B as fe,C as We,D as we,E as Ae,G as ht,H as Pe,I as pt,J as gt,K as mt}from"./_plugin-vue_export-helper-rKFEraYH.js";import{u as _e,B as yt,U as Ge,_ as wt,a as bt,N as _t,M as kt,R as It,L as St,b as xt,A as Ct,c as $t}from"./AccessDenied-C-lL9vbN.js";import{i as Fe,a as ee,g as je,b as qe,c as Oe,s as Ye}from"./pow-2N9bxgAo.js";import{g as be}from"./helpers-DzjFIx78.js";const Lt={class:"pairing-entry"},Wt={key:0,class:"input-row"},At={class:"slot-word"},Pt={class:"typed-prefix"},Mt={class:"hint-suffix"},Et={key:0,class:"cursor-overlay",style:{"--cursor-pos":0,"--word-len":0}},Bt=["placeholder"],Dt={key:0,class:"processing-status"},Nt={class:"processing-icon"},Tt={key:1,class:"device-info"},Vt={class:"device-permit-text"},Ut={class:"device-meta"},Rt={key:0,class:"error-message",style:{"margin-top":"0.5rem"}},zt={class:"button-row",style:{"margin-top":"0.75rem",display:"flex",gap:"0.5rem"}},Ht=["disabled"],Kt=["disabled"],Ft={__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(le,{expose:o,emit:E}){const A=E,_=d(!1),b=d(null),N=d(null);let f=null,T=null;try{T=_e()}catch{}const k=d(null),z=d(null),u=d(""),P=d(!1),B=d(""),g=d(null),h=d("");Ze(g,e=>{A("deviceInfoVisible",!!e)});const p=d(!1),I=d(!1),c=d(0),$=d(0),U=d(0),Q=d(!1),ve=d(!1);let te=0,se=!1,Z=null,ne=null,j=null,q=null,R=null,J=null;function oe(e,s="info",t=3e3){T&&T.showMessage(e,s,t)}async function re(){try{const e=await ut();N.value=e}catch(e){console.warn("Unable to load settings",e)}}function ie(e,s){if(!e||s<0)return{word:"",start:0,end:0};let t=s,r=s;for(;t>0&&/[a-zA-Z]/.test(e[t-1]);)t--;for(;r<e.length&&/[a-zA-Z]/.test(e[r]);)r++;return{word:e.slice(t,r),start:t,end:r}}function X(e){return e.trim().split(/[.\s]+/).filter(s=>s.length>0)}function D(e){const s=X(e),t=[];for(const r of s){let l=r.toLowerCase();for(;l.length>0&&t.length<3;){let x=null;for(let y=Math.min(l.length,6);y>=3;y--){const S=l.slice(0,y);if(ee(S)){x=S;break}}if(x)t.push(x),l=l.slice(x.length);else{t.push(l);break}}if(t.length>=3)break}return t}function he(e){const s=/[.\s]$/.test(e),t=D(e);return s?t.length:Math.max(0,t.length-1)}function pe(e){if(!e)return{valid:!0,segments:[]};const s=[],t=/[.\s]$/.test(e);let r,l=/([a-zA-Z]+)|([.\s]+)/g;for(;(r=l.exec(e))!==null;)r[1]?s.push({text:r[1],isWord:!0,start:r.index}):r[2]&&s.push({text:r[2],isWord:!1,start:r.index});const x=s.filter(S=>S.isWord);let y=!0;return x.forEach((S,H)=>{const M=H===x.length-1,Y=S.text.toLowerCase();M&&!t?S.invalid=!Fe(Y):S.invalid=!ee(Y),S.invalid&&(y=!1)}),{valid:y,segments:s}}L(()=>{const{segments:e}=pe(u.value);return e.map(s=>({text:s.text,invalid:s.invalid||!1}))});function ke(e){return pe(e).valid}function Ie(e){return D(e).length>0&&D(e).every(s=>ee(s))}function ge(e){if(/[.\s]$/.test(e))return"";const t=e.match(/[a-zA-Z]+$/);return t?t[0].toLowerCase():""}function Se(e,s){if(!e||s===0)return{wordIndex:0,charIndex:0};const t=e.slice(0,s),r=/[.\s]$/.test(t),l=D(t);if(l.length===0)return{wordIndex:0,charIndex:0};if(r)return{wordIndex:Math.min(l.length,2),charIndex:0};const x=l[l.length-1],y=l.length-1;D(e);const S=x.length;return y<2&&!ve.value&&ee(x)?{wordIndex:y+1,charIndex:0}:{wordIndex:Math.min(y,2),charIndex:S}}function ue(e,s){if(!e||s===0)return{wordIndex:0,charIndex:0};const t=e.slice(0,s),r=/[.\s]$/.test(t),l=D(t);if(l.length===0)return{wordIndex:0,charIndex:0};if(r)return{wordIndex:Math.min(l.length,2),charIndex:0};const x=l[l.length-1],y=l.length-1;return{wordIndex:Math.min(y,2),charIndex:x.length}}const i=L(()=>{const e=D(u.value),s=[],t=ge(u.value),r=h.value,l=/[.\s]$/.test(u.value),x=$.value!==U.value,y=ue(u.value,Math.min($.value,U.value)),S=ue(u.value,Math.max($.value,U.value)),H=x?ue(u.value,c.value):Se(u.value,c.value);for(let M=0;M<3;M++){const Y=H.wordIndex===M;let O=-1,K=-1;if(x&&(M>y.wordIndex&&M<S.wordIndex?(O=0,K=e[M]?.length??0):M===y.wordIndex&&M===S.wordIndex?(O=y.charIndex,K=S.charIndex):M===y.wordIndex?(O=y.charIndex,K=e[M]?.length??0):M===S.wordIndex&&(O=0,K=S.charIndex)),M<e.length){const me=e[M].toLowerCase(),ze=M===e.length-1,He=ze&&!l?!Fe(me):!ee(me);if(ze&&!l&&r&&t){const at=r.length;s.push({text:"",typedPrefix:t,hintSuffix:r.slice(t.length),invalid:He,hasCursor:Y,cursorCharIndex:Y?H.charIndex:-1,wordLen:at,selectionStartChar:O,selectionEndChar:K})}else s.push({text:me,invalid:He,hasCursor:Y,cursorCharIndex:Y?H.charIndex:-1,wordLen:me.length,selectionStartChar:O,selectionEndChar:K})}else s.push({text:"",invalid:!1,hasCursor:Y,cursorCharIndex:0,wordLen:0,selectionStartChar:O,selectionEndChar:K})}return s}),n=L(()=>$.value!==U.value),m=L(()=>{const e=D(u.value);return e.length===3&&e.every(s=>ee(s))});function De(e){return D(e).join(".")}function Xe(){if(!Z||j)return;const e=Oe(Z);j=Ye(e,ne).then(s=>{q=s,j=null})}async function Ne(){if(q){const s=q;return q=null,s}if(j){await j;const s=q;return q=null,s}if(!Z)throw new Error("No PoW challenge available");const e=Oe(Z);return await Ye(e,ne)}function Te(e){e?.challenge&&(Z=e.challenge,ne=e.work,q=null,j=null,Xe())}async function xe(){if(!(f||se)){se=!0;try{const e=N.value?.auth_host,s="/auth/ws/remote-auth/permit",t=e&&location.host!==e?`//${e}${s}`:s;f=await dt(t);const r=await f.receive_json();if(r.status&&r.detail)throw new Error(r.detail);if(!r.pow?.challenge)throw new Error("Server did not send PoW challenge");Te(r.pow)}catch(e){throw console.error("WebSocket connection error:",e),f=null,e}finally{se=!1}}}function et(e){if(e.key==="Tab"||e.key===" "||e.key==="Escape"){nt(e);return}setTimeout(Ve,0)}function Ve(){const e=k.value,s=e?.selectionStart??u.value.length,t=e?.selectionEnd??s,l=(e?.selectionDirection??"none")==="backward"?s:t;ve.value=l<te,te=l,c.value=l,U.value=t,$.value=s}function tt(){c.value=k.value?.selectionStart??u.value.length;const{word:e,end:s}=ie(u.value,c.value);if(he(u.value)>=3||!e||e.length<1||c.value!==s){h.value="";return}const r=je(e.toLowerCase());r&&r!==e.toLowerCase()?h.value=r:h.value=""}function Ue(){if(!h.value)return!1;const{word:e,start:s,end:t}=ie(u.value,c.value);if(!e)return!1;const r=u.value.slice(0,s),y=D(r).length===2?"":" ",S=u.value.slice(t);u.value=r+h.value+y+S.trimStart();const H=s+h.value.length+y.length;return ye(()=>{k.value?.setSelectionRange(H,H),c.value=H}),h.value="",!0}function ce(){c.value=k.value?.selectionStart??u.value.length;const e=c.value,t=u.value.slice(0,e).match(/([a-zA-Z]+) $/);if(t){const l=t[1].toLowerCase(),x=je(l);if(x&&x!==l&&!ee(l)){const y=e-t[0].length,S=u.value.slice(0,y),H=u.value.slice(e),O=D(S).length===2?"":" ";u.value=S+x+O+H;const K=y+x.length+O.length;ye(()=>{k.value?.setSelectionRange(K,K),c.value=K})}}tt(),R&&(clearTimeout(R),R=null),g.value=null,b.value=null,I.value=!1,p.value=!ke(u.value);const r=D(u.value);if(r.length>=1&&!f&&!se&&xe(),r.length===3){if(!Ie(u.value))return;R=setTimeout(()=>{st()},150)}}async function st(){if(!(P.value||_.value||!m.value||De(u.value)===J&&g.value)){P.value=!0,B.value="pow",b.value=null,I.value=!1;try{if(await xe(),!f)throw new Error("Failed to connect");const s=await Ne(),t=qe(s),r=De(u.value);if(!m.value)return;B.value="server",f.send_json({code:r,pow:t});const l=await f.receive_json();if(Te(l.pow),typeof l.status=="number"&&l.status>=400){oe(l.detail||"Request failed","error"),I.value=!0,g.value=null,J=null;return}l.status==="found"&&l.host?(g.value={host:l.host,user_agent_pretty:l.user_agent_pretty,client_ip:l.client_ip,action:l.action||"login"},J=r,ye(()=>{z.value?.focus()})):(oe("Unexpected response from server","error"),I.value=!0,g.value=null,J=null)}catch(s){console.error("Lookup error:",s),oe(s.message||"Lookup failed","error"),I.value=!0,g.value=null,J=null,f&&(f.close(),f=null)}finally{P.value=!1,B.value=""}}}function nt(e){if(e.key==="Escape"){u.value="",ce(),e.preventDefault();return}if(e.key==="Tab"){if(h.value&&Ue()){e.preventDefault(),ce();return}u.value.trim()&&e.preventDefault();return}e.key===" "&&h.value&&Ue()&&(e.preventDefault(),ce())}async function ot(){if(!(!g.value||_.value)){_.value=!0,b.value=null;try{if(f||await xe(),!f)throw new Error("Failed to connect");const e=await Ne(),s=qe(e);f.send_json({authenticate:!0,pow:s});const t=await f.receive_json();if(typeof t.status=="number"&&t.status>=400)throw new Error(t.detail||"Authentication failed");if(!t.optionsJSON)throw new Error(t.detail||"Failed to get authentication options");const r=await ct(t);f.send_json(r);const l=await f.receive_json();if(typeof l.status=="number"&&l.status>=400)throw new Error(l.detail||"Authentication failed");if(l.status==="success")oe("Device authenticated successfully!","success",3e3),A("completed"),Ce();else throw new Error(l.detail||"Authentication failed")}catch(e){console.error("Pairing error:",e);const s=e.name==="NotAllowedError"?"Passkey authentication was cancelled":e.message||"Authentication failed";b.value=s,A("error",s)}finally{_.value=!1,f&&(f.close(),f=null)}}}async function Re(){if(f){try{f.send_json({deny:!0}),await new Promise(e=>setTimeout(e,100))}catch(e){console.error("Error sending deny message:",e)}f.close(),f=null}Ce()}function Ce(){u.value="",b.value=null,I.value=!1,g.value=null,P.value=!1,B.value="",h.value="",p.value=!1,J=null,f&&(f.close(),f=null),Z=null,ne=null,j=null,q=null}return Ee(async()=>{await re(),ye(()=>{c.value=k.value?.selectionStart??0})}),Be(()=>{R&&(clearTimeout(R),R=null),f&&(f.close(),f=null)}),o({reset:Ce,deny:Re,code:u,handleInput:ce,loading:_,error:b}),(e,s)=>(v(),w("div",Lt,[a("form",{onSubmit:Je(ot,["prevent"]),class:"pairing-form"},[g.value?g.value?(v(),w("div",Tt,[a("p",Vt,[F("Permit "+W(g.value.action==="register"?"registration":"login")+" to ",1),a("strong",null,W(g.value.host),1)]),a("p",Ut,W(g.value.user_agent_pretty),1),b.value?(v(),w("p",Rt,W(b.value),1)):V("",!0),a("div",zt,[a("button",{type:"button",class:"btn-secondary",disabled:_.value,onClick:Re,style:{flex:"1"}}," Deny ",8,Ht),a("button",{ref_key:"submitBtnRef",ref:z,type:"submit",disabled:_.value,class:"btn-primary",style:{flex:"1"}},W(_.value?"Authenticating…":"Authorize"),9,Kt)])])):V("",!0):(v(),w("div",Wt,[a("div",{class:$e(["input-wrapper",{"has-error":I.value,"is-complete":g.value&&!I.value,focused:Q.value,"has-selection":n.value}])},[a("div",{class:$e(["slot-machine",{"has-error":I.value,"is-complete":g.value&&!I.value}]),"aria-hidden":"true"},[(v(!0),w(de,null,rt(i.value,(t,r)=>(v(),w("div",{key:r,class:$e(["slot-reel",{"invalid-word":t.invalid,empty:!t.text&&!t.typedPrefix}])},[a("div",At,[t.selectionStartChar>=0&&t.selectionEndChar>t.selectionStartChar?(v(),w("span",{key:0,class:"selection-overlay",style:Le({"--sel-start":t.selectionStartChar,"--sel-end":t.selectionEndChar,"--word-len":t.wordLen})},null,4)):V("",!0),t.typedPrefix?(v(),w(de,{key:1},[a("span",Pt,W(t.typedPrefix),1),a("span",Mt,W(t.hintSuffix),1),t.hasCursor?(v(),w("span",{key:0,class:"cursor-overlay",style:Le({"--cursor-pos":t.cursorCharIndex,"--word-len":t.wordLen})},null,4)):V("",!0)],64)):t.text?(v(),w(de,{key:2},[F(W(t.text)+" ",1),t.hasCursor?(v(),w("span",{key:0,class:"cursor-overlay",style:Le({"--cursor-pos":t.cursorCharIndex,"--word-len":t.wordLen})},null,4)):V("",!0)],64)):(v(),w(de,{key:3},[t.hasCursor?(v(),w("span",Et)):V("",!0)],64))])],2))),128))],2),lt(a("input",{ref_key:"inputRef",ref:k,"onUpdate:modelValue":s[0]||(s[0]=t=>u.value=t),type:"text",placeholder:le.placeholder,autocomplete:"off",autocapitalize:"none",autocorrect:"off",spellcheck:"false",class:"pairing-input hidden-input",onInput:ce,onKeydown:et,onMouseup:Ve,onFocus:s[1]||(s[1]=t=>Q.value=!0),onBlur:s[2]||(s[2]=t=>Q.value=!1)},null,40,Bt),[[it,u.value]])],2),B.value?(v(),w("div",Dt,[a("span",Nt,W(B.value==="pow"?"🔐":"📡"),1),s[3]||(s[3]=a("span",{class:"processing-spinner-small"},null,-1))])):V("",!0)]))],32)]))}},jt=Me(Ft,[["__scopeId","data-v-77a073d9"]]),qt={class:"view-root","data-view":"profile"},Ot={class:"view-header"},Yt={class:"remote-auth-inline"},Zt={key:0,class:"remote-auth-label"},Jt={class:"section-block"},Gt={class:"section-body"},Qt={class:"section-block"},Xt=["disabled"],es=["disabled"],ts=["disabled"],ss={key:0,class:"logout-note"},ns={key:1,class:"logout-note"},os={__name:"ProfileView",setup(le){const o=_e(),E=d(null),A=d(!1),_=d(!1),b=d(""),N=d(!1),f=d(null),T=d(null),k=d(!1),z=d(null),u=d(null),P=d(null),B=d(null),g=d(null),h=d(null),p=d(null),I=d(null),c=L(()=>A.value||_.value);Ze(A,i=>{i&&(b.value=o.userInfo?.user?.user_name||"")}),Ee(()=>{E.value=setInterval(()=>{o.userInfo&&(o.userInfo={...o.userInfo})},6e4)}),Be(()=>{E.value&&clearInterval(E.value)});const $=async()=>{try{await ht.register(null,null,()=>{o.showMessage("Adding new passkey...","info")}),await o.loadUserInfo(),o.showMessage("New passkey added successfully!","success",3e3)}catch(i){console.error("Failed to add new passkey:",i),o.showMessage(i.message,"error")}},U=()=>{o.showMessage("The other device is now signed in!","success",4e3),setTimeout(()=>z.value?.reset(),3e3)},Q=i=>{i.includes("cancelled")||o.showMessage(i,"error",4e3)},ve=()=>{o.showMessage("📋 Link copied! Send it to your other device."),_.value=!1},te=i=>{We(i,{primarySelector:".btn-primary",itemSelector:"button"})},se=i=>{if(c.value)return;const n=fe(i);n&&n==="down"&&(i.preventDefault(),We(I.value,{primarySelector:".mini-btn",itemSelector:".mini-btn, .pairing-input"}))},Z=i=>{if(c.value)return;const n=fe(i);if(!n)return;i.preventDefault(),n==="left"||n==="right"?we(I.value,i.target,n,{itemSelector:".mini-btn, .pairing-input"}):n==="up"?h.value?.focusCurrent?.():n==="down"&&u.value?.$el?.focus()},ne=i=>{c.value||(i==="down"||i==="right"?te(P.value):(i==="up"||i==="left")&&We(I.value,{primarySelector:".mini-btn",itemSelector:".mini-btn, .pairing-input"}))},j=i=>{if(c.value)return;const n=fe(i);n&&(i.preventDefault(),n==="left"||n==="right"?we(P.value,i.target,n,{itemSelector:"button"}):n==="up"?Ae(u.value?.$el,0,{itemSelector:".credential-item"}):n==="down"&&Ae(B.value?.$el,0,{itemSelector:".session-group"}))},q=i=>{c.value||(i==="up"?te(P.value):i==="down"&&te(g.value))},R=i=>{if(c.value)return;const n=fe(i);n&&(i.preventDefault(),n==="left"||n==="right"?we(g.value,i.target,n,{itemSelector:"button"}):n==="up"&&Ae(B.value?.$el,-1,{itemSelector:".session-group"}))},J=async i=>{const n=i?.credential_uuid;if(n)try{await o.deleteCredential(n),o.showMessage("Passkey deleted! You should also remove it from your password manager or device.","success",3e3)}catch(m){o.showMessage(`Failed to delete passkey: ${m.message}`,"error")}},oe=L(()=>o.settings?.rp_name||"this service"),re=L(()=>o.userInfo?.sessions||[]),ie=L(()=>re.value.find(n=>n.is_current)?.host||"this host"),X=d({}),D=async i=>{const n=i?.id;if(n){X.value={...X.value,[n]:!0};try{await o.terminateSession(n)}catch(m){o.showMessage(m.message||"Failed to terminate session","error",5e3)}finally{const m={...X.value};delete m[n],X.value=m}}},he=async()=>{await o.logoutEverywhere()},pe=async()=>{await o.logout()},ke=()=>{b.value=o.userInfo?.user?.user_name||"",A.value=!0},Ie=L(()=>!!(o.userInfo?.is_global_admin||o.userInfo?.is_org_admin)),ge=L(()=>re.value.length>1),Se=L(()=>{const i=[{label:"Auth",href:ft()}];return Ie.value&&i.push({label:"Admin",href:vt()}),i}),ue=async()=>{const i=b.value.trim();if(!i){o.showMessage("Name cannot be empty","error");return}try{N.value=!0,await Pe("/auth/api/user/display-name",{method:"PUT",body:{display_name:i}}),A.value=!1,await o.loadUserInfo(),o.showMessage("Name updated successfully!","success",3e3)}catch(n){o.showMessage(n.message||"Failed to update name","error")}finally{N.value=!1}};return(i,n)=>(v(),w("section",qt,[a("header",Ot,[n[10]||(n[10]=a("h1",null,"User Profile",-1)),ae(yt,{ref_key:"breadcrumbs",ref:h,entries:Se.value,onKeydown:se},null,8,["entries"]),n[11]||(n[11]=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:I},[C(o).userInfo?.user?(v(),G(Ge,{key:0,ref_key:"userBasicInfo",ref:p,name:C(o).userInfo.user.user_name,visits:C(o).userInfo.user.visits||0,"created-at":C(o).userInfo.user.created_at,"last-seen":C(o).userInfo.user.last_seen,loading:C(o).isLoading,"update-endpoint":"/auth/api/user/display-name",onSaved:n[1]||(n[1]=m=>C(o).loadUserInfo()),onEditName:ke,onKeydown:Z},{default:Ke(()=>[a("div",Yt,[k.value?V("",!0):(v(),w("label",Zt,"Code words:")),ae(jt,{ref_key:"pairingEntry",ref:z,title:"",description:"",onCompleted:U,onError:Q,onDeviceInfoVisible:n[0]||(n[0]=m=>k.value=m)},null,512)]),n[12]||(n[12]=a("p",{class:"remote-auth-description"},"Provided by another device requesting remote auth.",-1))]),_:1},8,["name","visits","created-at","last-seen","loading"])):V("",!0)],512),a("section",Jt,[n[13]||(n[13]=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",Gt,[ae(wt,{ref_key:"credentialList",ref:u,credentials:C(o).userInfo?.credentials||[],"aaguid-info":C(o).userInfo?.aaguid_info||{},loading:C(o).isLoading,"hovered-credential-uuid":f.value,"hovered-session-credential-uuid":T.value?.credential_uuid,"navigation-disabled":c.value,"allow-delete":"",onDelete:J,onCredentialHover:n[2]||(n[2]=m=>f.value=m),onNavigateOut:ne},null,8,["credentials","aaguid-info","loading","hovered-credential-uuid","hovered-session-credential-uuid","navigation-disabled"]),a("div",{class:"button-row",ref_key:"credentialButtons",ref:P},[a("button",{onClick:$,class:"btn-primary",onKeydown:j},"Register New",32),a("button",{onClick:n[3]||(n[3]=m=>_.value=!0),class:"btn-secondary",onKeydown:j},"Another Device",32)],512)])]),ae(bt,{ref_key:"sessionList",ref:B,sessions:re.value,"terminating-sessions":X.value,"hovered-credential-uuid":f.value,"navigation-disabled":c.value,onTerminate:D,onSessionHover:n[4]||(n[4]=m=>T.value=m),onNavigateOut:q,"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"]),A.value?(v(),G(kt,{key:0,onClose:n[7]||(n[7]=m=>A.value=!1)},{default:Ke(()=>[n[14]||(n[14]=a("h3",null,"Edit Display Name",-1)),a("form",{onSubmit:Je(ue,["prevent"]),class:"modal-form"},[ae(_t,{label:"Display Name",modelValue:b.value,"onUpdate:modelValue":n[5]||(n[5]=m=>b.value=m),busy:N.value,onCancel:n[6]||(n[6]=m=>A.value=!1)},null,8,["modelValue","busy"])],32)]),_:1})):V("",!0),a("section",Qt,[a("div",{class:"button-row",ref_key:"logoutButtons",ref:g},[a("button",{type:"button",class:"btn-secondary",onClick:n[8]||(n[8]=(...m)=>C(be)&&C(be)(...m)),onKeydown:R}," Back ",32),ge.value?(v(),w(de,{key:1},[a("button",{onClick:pe,class:"btn-danger",disabled:C(o).isLoading,onKeydown:R},"Logout",40,es),a("button",{onClick:he,class:"btn-danger",disabled:C(o).isLoading,onKeydown:R},"All",40,ts)],64)):(v(),w("button",{key:0,onClick:he,class:"btn-danger",disabled:C(o).isLoading,onKeydown:R},"Logout",40,Xt))],512),ge.value?(v(),w("p",ns,[n[16]||(n[16]=a("strong",null,"Logout",-1)),F(" this session on "+W(ie.value)+", or ",1),n[17]||(n[17]=a("strong",null,"All",-1)),F(" sessions across all sites and devices for "+W(oe.value)+". You'll need to log in again with your passkey afterwards.",1)])):(v(),w("p",ss,[n[15]||(n[15]=a("strong",null,"Logout",-1)),F(" from "+W(ie.value)+".",1)]))]),_.value?(v(),G(It,{key:1,endpoint:"/auth/api/user/create-link",onClose:n[9]||(n[9]=m=>_.value=!1),onCopied:ve})):V("",!0)]))}},as=Me(os,[["__scopeId","data-v-0f3403c1"]]),ls={class:"view-root host-view","data-view":"host-profile"},rs={class:"view-header"},is={class:"view-lede"},us={class:"section-body"},cs={key:1,class:"empty-state"},ds={class:"section-block"},fs={class:"section-body host-actions"},vs=["disabled"],hs=["disabled"],ps={class:"note"},gs={__name:"HostProfileView",props:{initializing:{type:Boolean,default:!1}},setup(le){const o=_e(),E=window.location.host,A=d(null),_=d(null),b=L(()=>o.userInfo?.user||null),N=L(()=>o.userInfo?.org?.display_name||""),f=L(()=>o.userInfo?.role?.display_name||""),T=L(()=>{const h=o.settings?.rp_name;return h?`${h} account`:"Account overview"}),k=L(()=>`You're signed in to ${E}.`),z=L(()=>o.settings?.auth_host||""),u=L(()=>{const h=z.value;if(!h)return"";let p=o.settings?.ui_base_path??"/auth/";return p.startsWith("/")||(p=`/${p}`),p.endsWith("/")||(p=`${p}/`),`${window.location.protocol||"https:"}//${h}${p}`}),P=()=>{u.value&&(window.location.href=u.value)},B=async()=>{await o.logout()},g=h=>{const p=fe(h);p&&(h.preventDefault(),(p==="left"||p==="right")&&we(_.value,h.target,p,{itemSelector:"button"}))};return(h,p)=>(v(),w("section",ls,[a("header",rs,[a("h1",null,W(T.value),1),a("p",is,W(k.value),1)]),a("section",{class:"section-block",ref_key:"userInfoSection",ref:A},[a("div",us,[b.value?(v(),G(Ge,{key:0,name:b.value.user_name,visits:b.value.visits||0,"created-at":b.value.created_at,"last-seen":b.value.last_seen,"org-display-name":N.value,"role-name":f.value,"can-edit":!1},null,8,["name","visits","created-at","last-seen","org-display-name","role-name"])):(v(),w("p",cs,W(le.initializing?"Loading your account…":"No active session found."),1))])],512),a("section",ds,[a("div",fs,[a("div",{class:"button-row",ref_key:"buttonRow",ref:_,onKeydown:g},[a("button",{type:"button",class:"btn-secondary",onClick:p[0]||(p[0]=(...I)=>C(be)&&C(be)(...I))}," Back "),a("button",{type:"button",class:"btn-danger",disabled:C(o).isLoading,onClick:B},W(C(o).isLoading?"Signing out…":"Logout"),9,vs),u.value?(v(),w("button",{key:0,type:"button",class:"btn-primary",disabled:C(o).isLoading,onClick:P}," Full Profile ",8,hs)):V("",!0)],544),a("p",ps,[p[1]||(p[1]=a("strong",null,"Logout",-1)),F(" from "+W(C(E))+", or access your ",1),p[2]||(p[2]=a("strong",null,"Full Profile",-1)),F(" at "+W(z.value)+" (you may need to sign in again).",1)])])])]))}},ms=Me(gs,[["__scopeId","data-v-054244d9"]]),ys={class:"app-shell"},ws={class:"app-main"},bs={__name:"App",setup(le){const o=_e(),E=d(!0),A=d("Loading..."),_=d(!1),b=d(!1);function N(c){if(!c)return null;const $=c.trim().toLowerCase();return $?$.replace(/:80$/,"").replace(/:443$/,""):null}const f=L(()=>{const c=o.settings?.auth_host;if(!c)return!1;const $=N(window.location.host),U=N(c);return $!==U});let T=null,k=null;async function z(){try{return o.userInfo=await Pe("/auth/api/user-info",{method:"POST"}),_.value=!0,E.value=!1,p(),!0}catch{return!1}}async function u(){P();const c=await pt("login");k=document.createElement("iframe"),k.id="auth-iframe",k.title="Authentication",k.allow="publickey-credentials-get; publickey-credentials-create",k.src=c,document.body.appendChild(k),A.value="Authentication required..."}function P(){k&&(k.remove(),k=null)}function B(){window.location.reload()}function g(c){const $=c.data;if($?.type)switch($.type){case"auth-success":P(),E.value=!0,A.value="Loading user profile...",z();break;case"auth-error":$.cancelled?console.log("Authentication cancelled by user"):o.showMessage($.message||"Authentication failed","error",5e3);break;case"auth-cancelled":console.log("Authentication cancelled");break;case"auth-back":P(),E.value=!1,b.value=!0,o.showMessage("Authentication cancelled","info",3e3);break;case"auth-close-request":P();break}}async function h(){try{await Pe("/auth/api/validate",{method:"POST",credentials:"include"})}catch(c){c.status===401?(console.log("Session expired, requiring re-authentication"),_.value=!1,E.value=!0,I(),u()):console.error("Session validation error:",c)}}function p(){I(),T=setInterval(h,120*1e3)}function I(){T&&(clearInterval(T),T=null)}return Ee(async()=>{window.addEventListener("message",g),await o.loadSettings();const c=o.settings?.rp_name;if(c){const U=o.settings?.auth_host,Q=U&&N(window.location.host)!==N(U);document.title=Q?`${c} · Account summary`:c}await z()||u()}),Be(()=>{window.removeEventListener("message",g),I(),P()}),(c,$)=>(v(),w("div",ys,[ae(xt),a("main",ws,[_.value&&f.value?(v(),G(ms,{key:0,initializing:E.value},null,8,["initializing"])):_.value?(v(),G(as,{key:1})):E.value?(v(),G(St,{key:2,message:A.value},null,8,["message"])):b.value?(v(),G(Ct,{key:3,onReload:B})):V("",!0)])]))}},Qe=gt(bs);Qe.use($t());Qe.mount("#app");mt();
paskia/util/tokens.py DELETED
@@ -1,44 +0,0 @@
1
- import hashlib
2
- import secrets
3
-
4
- import base64url
5
-
6
- from paskia.util.passphrase import is_well_formed
7
-
8
-
9
- def create_token() -> str:
10
- return secrets.token_urlsafe(12) # 16 characters Base64
11
-
12
-
13
- def session_key(token: str) -> bytes:
14
- if len(token) != 16:
15
- raise ValueError("Session token must be exactly 16 characters long")
16
- return b"sess" + base64url.dec(token)
17
-
18
-
19
- def encode_session_key(key: bytes) -> str:
20
- """Encode an opaque session key for external representation."""
21
- return base64url.enc(key)
22
-
23
-
24
- def decode_session_key(encoded: str) -> bytes:
25
- """Decode an opaque session key from its public representation."""
26
- if not encoded:
27
- raise ValueError("Invalid session identifier")
28
- try:
29
- raw = base64url.dec(encoded)
30
- except Exception as exc: # pragma: no cover - defensive
31
- raise ValueError("Invalid session identifier") from exc
32
- if not raw.startswith(b"sess"):
33
- raise ValueError("Invalid session identifier")
34
- return raw
35
-
36
-
37
- def reset_key(passphrase: str) -> bytes:
38
- if not is_well_formed(passphrase):
39
- raise ValueError(
40
- "Trying to reset with a session token in place of a passphrase"
41
- if len(passphrase) == 16
42
- else "Invalid passphrase format"
43
- )
44
- return b"rset" + hashlib.sha512(passphrase.encode()).digest()[:12]
@@ -1,22 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: paskia
3
- Version: 0.7.1
4
- Summary: Passkey Auth made easy: all sites and APIs can be guarded even without any changes on the protected site.
5
- Author: Leo Vasanko
6
- Keywords: FastAPI,auth_request,forward_auth
7
- Requires-Python: >=3.10
8
- Requires-Dist: aiosqlite>=0.19.0
9
- Requires-Dist: base64url>=1.0.0
10
- Requires-Dist: fastapi[standard]>=0.104.1
11
- Requires-Dist: pyjwt>=2.8.0
12
- Requires-Dist: sqlalchemy[asyncio]>=2.0.0
13
- Requires-Dist: user-agents>=2.2.0
14
- Requires-Dist: uuid7-standard>=1.0.0
15
- Requires-Dist: webauthn>=1.11.1
16
- Requires-Dist: websockets>=12.0
17
- Provides-Extra: dev
18
- Requires-Dist: coverage[toml]>=7.0.0; extra == 'dev'
19
- Requires-Dist: httpx>=0.27.0; extra == 'dev'
20
- Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
21
- Requires-Dist: pytest>=8.0.0; extra == 'dev'
22
- Requires-Dist: ruff>=0.1.0; extra == 'dev'
File without changes