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.
- paskia/_version.py +2 -2
- paskia/authsession.py +12 -49
- paskia/bootstrap.py +30 -25
- paskia/db/__init__.py +163 -401
- paskia/db/background.py +128 -0
- paskia/db/jsonl.py +132 -0
- paskia/db/operations.py +1241 -0
- paskia/db/structs.py +148 -0
- paskia/fastapi/admin.py +456 -215
- paskia/fastapi/api.py +16 -15
- paskia/fastapi/authz.py +7 -2
- paskia/fastapi/mainapp.py +2 -1
- paskia/fastapi/remote.py +20 -20
- paskia/fastapi/reset.py +9 -10
- paskia/fastapi/user.py +10 -18
- paskia/fastapi/ws.py +22 -19
- paskia/frontend-build/auth/admin/index.html +3 -3
- paskia/frontend-build/auth/assets/AccessDenied-aTdCvz9k.js +8 -0
- paskia/frontend-build/auth/assets/admin-BeNu48FR.css +1 -0
- paskia/frontend-build/auth/assets/admin-tVs8oyLv.js +1 -0
- paskia/frontend-build/auth/assets/{auth-BU_O38k2.css → auth-BKX7shEe.css} +1 -1
- paskia/frontend-build/auth/assets/auth-Dk3q4pNS.js +1 -0
- paskia/frontend-build/auth/index.html +3 -3
- paskia/globals.py +7 -10
- paskia/migrate/__init__.py +274 -0
- paskia/migrate/sql.py +381 -0
- paskia/util/permutil.py +16 -5
- paskia/util/sessionutil.py +3 -2
- paskia/util/userinfo.py +12 -26
- paskia-0.8.0.dist-info/METADATA +94 -0
- {paskia-0.7.1.dist-info → paskia-0.8.0.dist-info}/RECORD +33 -29
- {paskia-0.7.1.dist-info → paskia-0.8.0.dist-info}/entry_points.txt +1 -0
- paskia/db/sql.py +0 -1424
- paskia/frontend-build/auth/assets/AccessDenied-C-lL9vbN.js +0 -8
- paskia/frontend-build/auth/assets/admin-Cs6Mg773.css +0 -1
- paskia/frontend-build/auth/assets/admin-Df5_Damp.js +0 -1
- paskia/frontend-build/auth/assets/auth-Df3pjeSS.js +0 -1
- paskia/util/tokens.py +0 -44
- paskia-0.7.1.dist-info/METADATA +0 -22
- {paskia-0.7.1.dist-info → paskia-0.8.0.dist-info}/WHEEL +0 -0
paskia/fastapi/api.py
CHANGED
|
@@ -13,19 +13,17 @@ from fastapi import (
|
|
|
13
13
|
from fastapi.responses import JSONResponse
|
|
14
14
|
from fastapi.security import HTTPBearer
|
|
15
15
|
|
|
16
|
+
from paskia import db
|
|
16
17
|
from paskia.authsession import (
|
|
17
18
|
EXPIRES,
|
|
18
19
|
get_reset,
|
|
19
20
|
get_session,
|
|
20
21
|
refresh_session_token,
|
|
21
|
-
session_expiry,
|
|
22
22
|
)
|
|
23
23
|
from paskia.fastapi import authz, session, user
|
|
24
24
|
from paskia.fastapi.session import AUTH_COOKIE, AUTH_COOKIE_NAME
|
|
25
|
-
from paskia.globals import db
|
|
26
25
|
from paskia.globals import passkey as global_passkey
|
|
27
26
|
from paskia.util import frontend, hostutil, htmlutil, passphrase, userinfo
|
|
28
|
-
from paskia.util.tokens import session_key
|
|
29
27
|
|
|
30
28
|
bearer_auth = HTTPBearer(auto_error=True)
|
|
31
29
|
|
|
@@ -77,6 +75,7 @@ async def validate_token(
|
|
|
77
75
|
request: Request,
|
|
78
76
|
response: Response,
|
|
79
77
|
perm: list[str] = Query([]),
|
|
78
|
+
max_age: str | None = Query(None),
|
|
80
79
|
auth=AUTH_COOKIE,
|
|
81
80
|
):
|
|
82
81
|
"""Validate the current session and extend its expiry.
|
|
@@ -86,14 +85,18 @@ async def validate_token(
|
|
|
86
85
|
refresh endpoint.
|
|
87
86
|
"""
|
|
88
87
|
try:
|
|
89
|
-
ctx = await authz.verify(
|
|
88
|
+
ctx = await authz.verify(
|
|
89
|
+
auth,
|
|
90
|
+
perm,
|
|
91
|
+
host=request.headers.get("host"),
|
|
92
|
+
max_age=max_age,
|
|
93
|
+
)
|
|
90
94
|
except HTTPException:
|
|
91
95
|
# Global handler will clear cookie if 401
|
|
92
96
|
raise
|
|
93
97
|
renewed = False
|
|
94
98
|
if auth:
|
|
95
|
-
|
|
96
|
-
consumed = EXPIRES - (current_expiry - datetime.now(timezone.utc))
|
|
99
|
+
consumed = EXPIRES - (ctx.session.expiry - datetime.now(timezone.utc))
|
|
97
100
|
if not timedelta(0) < consumed < _REFRESH_INTERVAL:
|
|
98
101
|
try:
|
|
99
102
|
await refresh_session_token(
|
|
@@ -143,7 +146,7 @@ async def forward_authentication(
|
|
|
143
146
|
)
|
|
144
147
|
role_permissions = set(ctx.role.permissions or [])
|
|
145
148
|
if ctx.permissions:
|
|
146
|
-
role_permissions.update(permission.
|
|
149
|
+
role_permissions.update(permission.scope for permission in ctx.permissions)
|
|
147
150
|
|
|
148
151
|
remote_headers: dict[str, str] = {
|
|
149
152
|
"Remote-User": str(ctx.user.uuid),
|
|
@@ -154,13 +157,11 @@ async def forward_authentication(
|
|
|
154
157
|
"Remote-Role": str(ctx.role.uuid),
|
|
155
158
|
"Remote-Role-Name": ctx.role.display_name,
|
|
156
159
|
"Remote-Session-Expires": (
|
|
157
|
-
|
|
158
|
-
.astimezone(timezone.utc)
|
|
160
|
+
ctx.session.expiry.astimezone(timezone.utc)
|
|
159
161
|
.isoformat()
|
|
160
162
|
.replace("+00:00", "Z")
|
|
161
|
-
if
|
|
162
|
-
else
|
|
163
|
-
.replace(tzinfo=timezone.utc)
|
|
163
|
+
if ctx.session.expiry.tzinfo
|
|
164
|
+
else ctx.session.expiry.replace(tzinfo=timezone.utc)
|
|
164
165
|
.isoformat()
|
|
165
166
|
.replace("+00:00", "Z")
|
|
166
167
|
),
|
|
@@ -221,7 +222,7 @@ async def api_token_info(token: str):
|
|
|
221
222
|
# Check if this is a reset token
|
|
222
223
|
try:
|
|
223
224
|
reset_token = await get_reset(token)
|
|
224
|
-
user =
|
|
225
|
+
user = db.get_user_by_uuid(reset_token.user_uuid)
|
|
225
226
|
return {
|
|
226
227
|
"type": "reset",
|
|
227
228
|
"user_name": user.display_name,
|
|
@@ -287,11 +288,11 @@ async def api_logout(request: Request, response: Response, auth=AUTH_COOKIE):
|
|
|
287
288
|
if not auth:
|
|
288
289
|
return {"message": "Already logged out"}
|
|
289
290
|
try:
|
|
290
|
-
await get_session(auth, host=request.headers.get("host"))
|
|
291
|
+
_s = await get_session(auth, host=request.headers.get("host"))
|
|
291
292
|
except ValueError:
|
|
292
293
|
return {"message": "Already logged out"}
|
|
293
294
|
with suppress(Exception):
|
|
294
|
-
|
|
295
|
+
db.delete_session(auth)
|
|
295
296
|
session.clear_session_cookie(response)
|
|
296
297
|
return {"message": "Logged out successfully"}
|
|
297
298
|
|
paskia/fastapi/authz.py
CHANGED
|
@@ -94,14 +94,19 @@ async def verify(
|
|
|
94
94
|
|
|
95
95
|
if not match(ctx, perm):
|
|
96
96
|
# Determine which permissions are missing for clearer diagnostics
|
|
97
|
-
|
|
97
|
+
effective_scopes = (
|
|
98
|
+
{p.scope for p in (ctx.permissions or [])}
|
|
99
|
+
if ctx.permissions
|
|
100
|
+
else set(ctx.role.permissions or [])
|
|
101
|
+
)
|
|
102
|
+
missing = sorted(set(perm) - effective_scopes)
|
|
98
103
|
logger.warning(
|
|
99
104
|
"Permission denied: user=%s role=%s missing=%s required=%s granted=%s", # noqa: E501
|
|
100
105
|
getattr(ctx.user, "uuid", "?"),
|
|
101
106
|
getattr(ctx.role, "display_name", "?"),
|
|
102
107
|
missing,
|
|
103
108
|
perm,
|
|
104
|
-
|
|
109
|
+
list(effective_scopes),
|
|
105
110
|
)
|
|
106
111
|
raise AuthException(
|
|
107
112
|
status_code=403, mode="forbidden", detail="Permission required"
|
paskia/fastapi/mainapp.py
CHANGED
|
@@ -50,7 +50,7 @@ async def lifespan(app: FastAPI): # pragma: no cover - startup path
|
|
|
50
50
|
yield
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
app = FastAPI(lifespan=lifespan)
|
|
53
|
+
app = FastAPI(lifespan=lifespan, redirect_slashes=False)
|
|
54
54
|
|
|
55
55
|
# Apply redirections to auth-host if configured (deny access to restricted endpoints, remove /auth/)
|
|
56
56
|
app.middleware("http")(auth_host.redirect_middleware)
|
|
@@ -96,6 +96,7 @@ async def admin_root_redirect():
|
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
@app.get("/admin/", include_in_schema=False)
|
|
99
|
+
@app.get("/auth/admin/", include_in_schema=False)
|
|
99
100
|
async def admin_root(request: Request, auth=AUTH_COOKIE):
|
|
100
101
|
return await admin.adminapp(request, auth) # Delegated to admin app
|
|
101
102
|
|
paskia/fastapi/remote.py
CHANGED
|
@@ -15,11 +15,10 @@ from uuid import UUID
|
|
|
15
15
|
import base64url
|
|
16
16
|
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
|
17
17
|
|
|
18
|
-
from paskia import remoteauth
|
|
19
|
-
from paskia.authsession import create_session
|
|
18
|
+
from paskia import db, remoteauth
|
|
20
19
|
from paskia.fastapi.session import infodict
|
|
21
20
|
from paskia.fastapi.wsutil import validate_origin, websocket_error_handler
|
|
22
|
-
from paskia.globals import
|
|
21
|
+
from paskia.globals import passkey
|
|
23
22
|
from paskia.util import passphrase, pow
|
|
24
23
|
|
|
25
24
|
# Create a FastAPI subapp for remote auth WebSocket endpoints
|
|
@@ -323,9 +322,7 @@ async def websocket_remote_auth_permit(ws: WebSocket):
|
|
|
323
322
|
|
|
324
323
|
# Fetch and verify credential
|
|
325
324
|
try:
|
|
326
|
-
stored_cred =
|
|
327
|
-
credential.raw_id
|
|
328
|
-
)
|
|
325
|
+
stored_cred = db.get_credential_by_id(credential.raw_id)
|
|
329
326
|
except ValueError:
|
|
330
327
|
raise ValueError(
|
|
331
328
|
f"This passkey is no longer registered with {passkey.instance.rp_name}"
|
|
@@ -336,9 +333,6 @@ async def websocket_remote_auth_permit(ws: WebSocket):
|
|
|
336
333
|
credential, webauthn_challenge, stored_cred, origin
|
|
337
334
|
)
|
|
338
335
|
|
|
339
|
-
# Update credential last_used
|
|
340
|
-
await db.instance.login(stored_cred.user_uuid, stored_cred)
|
|
341
|
-
|
|
342
336
|
# Create a session for the REQUESTING device
|
|
343
337
|
assert stored_cred.uuid is not None
|
|
344
338
|
|
|
@@ -348,34 +342,40 @@ async def websocket_remote_auth_permit(ws: WebSocket):
|
|
|
348
342
|
if request.action == "register":
|
|
349
343
|
# For registration, create a reset token for device addition
|
|
350
344
|
from paskia.authsession import expires
|
|
351
|
-
from paskia.util import
|
|
345
|
+
from paskia.util import hostutil
|
|
352
346
|
|
|
353
347
|
token_str = passphrase.generate()
|
|
354
348
|
expiry = expires()
|
|
355
|
-
|
|
349
|
+
db.create_reset_token(
|
|
356
350
|
user_uuid=stored_cred.user_uuid,
|
|
357
|
-
|
|
351
|
+
passphrase=token_str,
|
|
358
352
|
expiry=expiry,
|
|
359
353
|
token_type="device addition",
|
|
360
354
|
)
|
|
361
355
|
reset_token = token_str
|
|
362
|
-
# Also create a session so the device is logged in
|
|
363
|
-
|
|
364
|
-
session_token =
|
|
356
|
+
# Also create a session so the device is logged in
|
|
357
|
+
normalized_host = hostutil.normalize_host(request.host)
|
|
358
|
+
session_token = db.login(
|
|
365
359
|
user_uuid=stored_cred.user_uuid,
|
|
366
|
-
|
|
367
|
-
host=
|
|
360
|
+
credential=stored_cred,
|
|
361
|
+
host=normalized_host,
|
|
368
362
|
ip=request.ip,
|
|
369
363
|
user_agent=request.user_agent,
|
|
364
|
+
expiry=expires(),
|
|
370
365
|
)
|
|
371
366
|
else:
|
|
372
367
|
# Default login action
|
|
373
|
-
|
|
368
|
+
from paskia.authsession import expires
|
|
369
|
+
from paskia.util import hostutil
|
|
370
|
+
|
|
371
|
+
normalized_host = hostutil.normalize_host(request.host)
|
|
372
|
+
session_token = db.login(
|
|
374
373
|
user_uuid=stored_cred.user_uuid,
|
|
375
|
-
|
|
376
|
-
host=
|
|
374
|
+
credential=stored_cred,
|
|
375
|
+
host=normalized_host,
|
|
377
376
|
ip=request.ip,
|
|
378
377
|
user_agent=request.user_agent,
|
|
378
|
+
expiry=expires(),
|
|
379
379
|
)
|
|
380
380
|
|
|
381
381
|
# Complete the remote auth request (notifies the waiting device)
|
paskia/fastapi/reset.py
CHANGED
|
@@ -16,9 +16,8 @@ import asyncio
|
|
|
16
16
|
from uuid import UUID
|
|
17
17
|
|
|
18
18
|
from paskia import authsession as _authsession
|
|
19
|
-
from paskia import
|
|
19
|
+
from paskia import db as _db
|
|
20
20
|
from paskia.util import hostutil, passphrase
|
|
21
|
-
from paskia.util import tokens as _tokens
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
async def _resolve_targets(query: str | None):
|
|
@@ -27,9 +26,9 @@ async def _resolve_targets(query: str | None):
|
|
|
27
26
|
targets: list[tuple] = []
|
|
28
27
|
try:
|
|
29
28
|
q_uuid = UUID(query)
|
|
30
|
-
perm_orgs =
|
|
29
|
+
perm_orgs = _db.get_permission_organizations("auth:admin")
|
|
31
30
|
for o in perm_orgs:
|
|
32
|
-
users =
|
|
31
|
+
users = _db.get_organization_users(str(o.uuid))
|
|
33
32
|
for u, role_name in users:
|
|
34
33
|
if u.uuid == q_uuid:
|
|
35
34
|
return [(u, role_name)]
|
|
@@ -38,9 +37,9 @@ async def _resolve_targets(query: str | None):
|
|
|
38
37
|
pass
|
|
39
38
|
# Substring search
|
|
40
39
|
needle = query.lower()
|
|
41
|
-
perm_orgs =
|
|
40
|
+
perm_orgs = _db.get_permission_organizations("auth:admin")
|
|
42
41
|
for o in perm_orgs:
|
|
43
|
-
users =
|
|
42
|
+
users = _db.get_organization_users(str(o.uuid))
|
|
44
43
|
for u, role_name in users:
|
|
45
44
|
if needle in (u.display_name or "").lower():
|
|
46
45
|
targets.append((u, role_name))
|
|
@@ -53,10 +52,10 @@ async def _resolve_targets(query: str | None):
|
|
|
53
52
|
deduped.append((u, role_name))
|
|
54
53
|
return deduped
|
|
55
54
|
# No query -> master admin
|
|
56
|
-
perm_orgs =
|
|
55
|
+
perm_orgs = _db.get_permission_organizations("auth:admin")
|
|
57
56
|
if not perm_orgs:
|
|
58
57
|
return []
|
|
59
|
-
users =
|
|
58
|
+
users = _db.get_organization_users(str(perm_orgs[0].uuid))
|
|
60
59
|
admin_users = [pair for pair in users if pair[1] == "Administration"]
|
|
61
60
|
return admin_users[:1]
|
|
62
61
|
|
|
@@ -64,9 +63,9 @@ async def _resolve_targets(query: str | None):
|
|
|
64
63
|
async def _create_reset(user, role_name: str):
|
|
65
64
|
token = passphrase.generate()
|
|
66
65
|
expiry = _authsession.reset_expires()
|
|
67
|
-
|
|
66
|
+
_db.create_reset_token(
|
|
67
|
+
passphrase=token,
|
|
68
68
|
user_uuid=user.uuid,
|
|
69
|
-
key=_tokens.reset_key(token),
|
|
70
69
|
expiry=expiry,
|
|
71
70
|
token_type="manual reset",
|
|
72
71
|
)
|
paskia/fastapi/user.py
CHANGED
|
@@ -10,6 +10,7 @@ from fastapi import (
|
|
|
10
10
|
)
|
|
11
11
|
from fastapi.responses import JSONResponse
|
|
12
12
|
|
|
13
|
+
from paskia import db
|
|
13
14
|
from paskia.authsession import (
|
|
14
15
|
delete_credential,
|
|
15
16
|
expires,
|
|
@@ -17,9 +18,7 @@ from paskia.authsession import (
|
|
|
17
18
|
)
|
|
18
19
|
from paskia.fastapi import authz, session
|
|
19
20
|
from paskia.fastapi.session import AUTH_COOKIE
|
|
20
|
-
from paskia.
|
|
21
|
-
from paskia.util import hostutil, passphrase, tokens
|
|
22
|
-
from paskia.util.tokens import decode_session_key, session_key
|
|
21
|
+
from paskia.util import hostutil, passphrase
|
|
23
22
|
|
|
24
23
|
app = FastAPI()
|
|
25
24
|
|
|
@@ -33,7 +32,7 @@ async def auth_exception_handler(_request, exc: authz.AuthException):
|
|
|
33
32
|
)
|
|
34
33
|
|
|
35
34
|
|
|
36
|
-
@app.
|
|
35
|
+
@app.patch("/display-name")
|
|
37
36
|
async def user_update_display_name(
|
|
38
37
|
request: Request,
|
|
39
38
|
response: Response,
|
|
@@ -55,7 +54,7 @@ async def user_update_display_name(
|
|
|
55
54
|
raise HTTPException(status_code=400, detail="display_name required")
|
|
56
55
|
if len(new_name) > 64:
|
|
57
56
|
raise HTTPException(status_code=400, detail="display_name too long")
|
|
58
|
-
|
|
57
|
+
db.update_user_display_name(s.user_uuid, new_name)
|
|
59
58
|
return {"status": "ok"}
|
|
60
59
|
|
|
61
60
|
|
|
@@ -69,7 +68,7 @@ async def api_logout_all(request: Request, response: Response, auth=AUTH_COOKIE)
|
|
|
69
68
|
raise authz.AuthException(
|
|
70
69
|
status_code=401, detail="Session expired", mode="login"
|
|
71
70
|
)
|
|
72
|
-
|
|
71
|
+
db.delete_sessions_for_user(s.user_uuid)
|
|
73
72
|
session.clear_session_cookie(response)
|
|
74
73
|
return {"message": "Logged out from all hosts"}
|
|
75
74
|
|
|
@@ -92,19 +91,12 @@ async def api_delete_session(
|
|
|
92
91
|
status_code=401, detail="Session expired", mode="login"
|
|
93
92
|
) from exc
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
target_key = decode_session_key(session_id)
|
|
97
|
-
except ValueError as exc:
|
|
98
|
-
raise HTTPException(
|
|
99
|
-
status_code=400, detail="Invalid session identifier"
|
|
100
|
-
) from exc
|
|
101
|
-
|
|
102
|
-
target_session = await db.instance.get_session(target_key)
|
|
94
|
+
target_session = db.get_session(session_id)
|
|
103
95
|
if not target_session or target_session.user_uuid != current_session.user_uuid:
|
|
104
96
|
raise HTTPException(status_code=404, detail="Session not found")
|
|
105
97
|
|
|
106
|
-
|
|
107
|
-
current_terminated =
|
|
98
|
+
db.delete_session(session_id)
|
|
99
|
+
current_terminated = session_id == auth
|
|
108
100
|
if current_terminated:
|
|
109
101
|
session.clear_session_cookie(response) # explicit because 200
|
|
110
102
|
return {"status": "ok", "current_session_terminated": current_terminated}
|
|
@@ -144,9 +136,9 @@ async def api_create_link(
|
|
|
144
136
|
) from e
|
|
145
137
|
token = passphrase.generate()
|
|
146
138
|
expiry = expires()
|
|
147
|
-
|
|
139
|
+
db.create_reset_token(
|
|
148
140
|
user_uuid=s.user_uuid,
|
|
149
|
-
|
|
141
|
+
passphrase=token,
|
|
150
142
|
expiry=expiry,
|
|
151
143
|
token_type="device addition",
|
|
152
144
|
)
|
paskia/fastapi/ws.py
CHANGED
|
@@ -2,13 +2,13 @@ from uuid import UUID
|
|
|
2
2
|
|
|
3
3
|
from fastapi import FastAPI, WebSocket
|
|
4
4
|
|
|
5
|
-
from paskia
|
|
5
|
+
from paskia import db
|
|
6
|
+
from paskia.authsession import expires, get_reset, get_session
|
|
6
7
|
from paskia.fastapi import authz, remote
|
|
7
8
|
from paskia.fastapi.session import AUTH_COOKIE, infodict
|
|
8
9
|
from paskia.fastapi.wsutil import validate_origin, websocket_error_handler
|
|
9
|
-
from paskia.globals import
|
|
10
|
-
from paskia.util import passphrase
|
|
11
|
-
from paskia.util.tokens import create_token, session_key
|
|
10
|
+
from paskia.globals import passkey
|
|
11
|
+
from paskia.util import hostutil, passphrase
|
|
12
12
|
|
|
13
13
|
# Create a FastAPI subapp for WebSocket endpoints
|
|
14
14
|
app = FastAPI()
|
|
@@ -65,25 +65,23 @@ async def websocket_register_add(
|
|
|
65
65
|
s = ctx.session
|
|
66
66
|
|
|
67
67
|
# Get user information and determine effective user_name for this registration
|
|
68
|
-
user =
|
|
68
|
+
user = db.get_user_by_uuid(user_uuid)
|
|
69
69
|
user_name = user.display_name
|
|
70
70
|
if name is not None:
|
|
71
71
|
stripped = name.strip()
|
|
72
72
|
if stripped:
|
|
73
73
|
user_name = stripped
|
|
74
|
-
challenge_ids =
|
|
74
|
+
challenge_ids = db.get_credentials_by_user_uuid(user_uuid)
|
|
75
75
|
|
|
76
76
|
# WebAuthn registration
|
|
77
77
|
credential = await register_chat(ws, user_uuid, user_name, origin, challenge_ids)
|
|
78
78
|
|
|
79
79
|
# Create a new session and store everything in database
|
|
80
|
-
token = create_token()
|
|
81
80
|
metadata = infodict(ws, "authenticated")
|
|
82
|
-
|
|
81
|
+
token = db.create_credential_session( # type: ignore[attr-defined]
|
|
83
82
|
user_uuid=user_uuid,
|
|
84
83
|
credential=credential,
|
|
85
84
|
reset_key=(s.key if reset is not None else None),
|
|
86
|
-
session_key=session_key(token),
|
|
87
85
|
display_name=user_name,
|
|
88
86
|
host=host,
|
|
89
87
|
ip=metadata.get("ip"),
|
|
@@ -115,9 +113,7 @@ async def websocket_authenticate(ws: WebSocket, auth=AUTH_COOKIE):
|
|
|
115
113
|
try:
|
|
116
114
|
session = await get_session(auth, host=host)
|
|
117
115
|
session_user_uuid = session.user_uuid
|
|
118
|
-
credential_ids =
|
|
119
|
-
session_user_uuid
|
|
120
|
-
)
|
|
116
|
+
credential_ids = db.get_credentials_by_user_uuid(session_user_uuid)
|
|
121
117
|
except ValueError:
|
|
122
118
|
pass # Invalid/expired session - allow normal authentication
|
|
123
119
|
|
|
@@ -129,7 +125,7 @@ async def websocket_authenticate(ws: WebSocket, auth=AUTH_COOKIE):
|
|
|
129
125
|
credential = passkey.instance.auth_parse(await ws.receive_json())
|
|
130
126
|
# Fetch from the database by credential ID
|
|
131
127
|
try:
|
|
132
|
-
stored_cred =
|
|
128
|
+
stored_cred = db.get_credential_by_id(credential.raw_id)
|
|
133
129
|
except ValueError:
|
|
134
130
|
raise ValueError(
|
|
135
131
|
f"This passkey is no longer registered with {passkey.instance.rp_name}"
|
|
@@ -141,18 +137,25 @@ async def websocket_authenticate(ws: WebSocket, auth=AUTH_COOKIE):
|
|
|
141
137
|
|
|
142
138
|
# Verify the credential matches the stored data
|
|
143
139
|
passkey.instance.auth_verify(credential, challenge, stored_cred, origin)
|
|
144
|
-
# Update both credential and user's last_seen timestamp
|
|
145
|
-
await db.instance.login(stored_cred.user_uuid, stored_cred)
|
|
146
140
|
|
|
147
|
-
# Create
|
|
141
|
+
# Create session and update user/credential in a single transaction
|
|
148
142
|
assert stored_cred.uuid is not None
|
|
149
143
|
metadata = infodict(ws, "auth")
|
|
150
|
-
|
|
144
|
+
normalized_host = hostutil.normalize_host(host)
|
|
145
|
+
if not normalized_host:
|
|
146
|
+
raise ValueError("Host required for session creation")
|
|
147
|
+
hostname = normalized_host.split(":")[0]
|
|
148
|
+
rp_id = passkey.instance.rp_id
|
|
149
|
+
if not (hostname == rp_id or hostname.endswith(f".{rp_id}")):
|
|
150
|
+
raise ValueError(f"Host must be the same as or a subdomain of {rp_id}")
|
|
151
|
+
|
|
152
|
+
token = db.login(
|
|
151
153
|
user_uuid=stored_cred.user_uuid,
|
|
152
|
-
|
|
153
|
-
host=
|
|
154
|
+
credential=stored_cred,
|
|
155
|
+
host=normalized_host,
|
|
154
156
|
ip=metadata.get("ip") or "",
|
|
155
157
|
user_agent=metadata.get("user_agent") or "",
|
|
158
|
+
expiry=expires(),
|
|
156
159
|
)
|
|
157
160
|
|
|
158
161
|
await ws.send_json(
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Admin</title>
|
|
7
|
-
<script type="module" crossorigin src="/auth/assets/admin-
|
|
7
|
+
<script type="module" crossorigin src="/auth/assets/admin-tVs8oyLv.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/auth/assets/_plugin-vue_export-helper-rKFEraYH.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/auth/assets/helpers-DzjFIx78.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/auth/assets/AccessDenied-
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/auth/assets/AccessDenied-aTdCvz9k.js">
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/auth/assets/_plugin-vue_export-helper-BTzJAQlS.css">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/auth/assets/AccessDenied-Bc249ASC.css">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/auth/assets/admin-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/auth/assets/admin-BeNu48FR.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="admin-app"></div>
|
|
@@ -0,0 +1,8 @@
|
|
|
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=null){const t=r??(s==="error"?5e3:3e3);this.status={message:e,type:s,show:!0},t>0&&setTimeout(()=>{this.status.show=!1},t)},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};
|