Update the API to use new naming matching database.

This commit is contained in:
2026-01-27 02:22:29 +00:00
parent 9bdca1f43a
commit cb84a81a06
12 changed files with 40 additions and 40 deletions
+12 -12
View File
@@ -130,7 +130,7 @@ function parseHash() {
async function loadOrgs() { async function loadOrgs() {
const data = await apiJson('/auth/api/admin/orgs') const data = await apiJson('/auth/api/admin/orgs')
orgs.value = data.map(o => { orgs.value = data.map(o => {
const roles = o.roles.map(r => ({ ...r, org_uuid: o.uuid, users: [] })) const roles = o.roles.map(r => ({ ...r, org: o.uuid, users: [] }))
const roleMap = Object.fromEntries(roles.map(r => [r.display_name, r])) const roleMap = Object.fromEntries(roles.map(r => [r.display_name, r]))
for (const u of o.users || []) { for (const u of o.users || []) {
if (roleMap[u.role]) roleMap[u.role].users.push(u) if (roleMap[u.role]) roleMap[u.role].users.push(u)
@@ -242,9 +242,9 @@ async function moveUserToRole(org, user, targetRoleDisplayName) {
} }
} }
function onUserDragStart(e, user, org_uuid) { function onUserDragStart(e, user, org) {
e.dataTransfer.effectAllowed = 'move' e.dataTransfer.effectAllowed = 'move'
e.dataTransfer.setData('text/plain', JSON.stringify({ user_uuid: user.uuid, org_uuid })) e.dataTransfer.setData('text/plain', JSON.stringify({ user_uuid: user.uuid, org }))
} }
function onRoleDragOver(e) { function onRoleDragOver(e) {
@@ -256,7 +256,7 @@ function onRoleDrop(e, org, role) {
e.preventDefault() e.preventDefault()
try { try {
const data = JSON.parse(e.dataTransfer.getData('text/plain')) const data = JSON.parse(e.dataTransfer.getData('text/plain'))
if (data.org_uuid !== org.uuid) return // only within same org if (data.org !== org.uuid) return // only within same org
const user = org.roles.flatMap(r => r.users).find(u => u.uuid === data.user_uuid) const user = org.roles.flatMap(r => r.users).find(u => u.uuid === data.user_uuid)
if (user) moveUserToRole(org, user, role.display_name) if (user) moveUserToRole(org, user, role.display_name)
} catch (_) { /* ignore */ } } catch (_) { /* ignore */ }
@@ -269,7 +269,7 @@ function updateRole(role) { openDialog('role-update', { role, name: role.display
function deleteRole(role) { function deleteRole(role) {
// UI only allows deleting empty roles, so no confirmation needed // UI only allows deleting empty roles, so no confirmation needed
apiJson(`/auth/api/admin/orgs/${role.org_uuid}/roles/${role.uuid}`, { method: 'DELETE' }) apiJson(`/auth/api/admin/orgs/${role.org}/roles/${role.uuid}`, { method: 'DELETE' })
.then(() => { .then(() => {
authStore.showMessage(`Role "${role.display_name}" deleted.`, 'success', 2500) authStore.showMessage(`Role "${role.display_name}" deleted.`, 'success', 2500)
loadOrgs() loadOrgs()
@@ -289,7 +289,7 @@ async function toggleRolePermission(role, pid, checked) {
try { try {
const method = checked ? 'POST' : 'DELETE' const method = checked ? 'POST' : 'DELETE'
await apiJson(`/auth/api/admin/orgs/${role.org_uuid}/roles/${role.uuid}/permissions/${pid}`, { await apiJson(`/auth/api/admin/orgs/${role.org}/roles/${role.uuid}/permissions/${pid}`, {
method method
}) })
await loadOrgs() await loadOrgs()
@@ -360,7 +360,7 @@ const selectedUser = computed(() => {
for (const o of orgs.value) { for (const o of orgs.value) {
for (const r of o.roles) { for (const r of o.roles) {
const u = r.users.find(x => x.uuid === currentUserId.value) const u = r.users.find(x => x.uuid === currentUserId.value)
if (u) return { ...u, org_uuid: o.uuid, role_display_name: r.display_name } if (u) return { ...u, org: o.uuid, role_display_name: r.display_name }
} }
} }
return null return null
@@ -381,7 +381,7 @@ const breadcrumbEntries = computed(() => {
// Determine organization for user view if selectedOrg not explicitly chosen. // Determine organization for user view if selectedOrg not explicitly chosen.
let orgForUser = null let orgForUser = null
if (selectedUser.value) { if (selectedUser.value) {
orgForUser = orgs.value.find(o => o.uuid === selectedUser.value.org_uuid) || null orgForUser = orgs.value.find(o => o.uuid === selectedUser.value.org) || null
} }
const orgToShow = selectedOrg.value || orgForUser const orgToShow = selectedOrg.value || orgForUser
if (orgToShow) { if (orgToShow) {
@@ -396,7 +396,7 @@ const breadcrumbEntries = computed(() => {
watch(selectedUser, async (u) => { watch(selectedUser, async (u) => {
if (!u) { userDetail.value = null; return } if (!u) { userDetail.value = null; return }
try { try {
userDetail.value = await apiJson(`/auth/api/admin/orgs/${u.org_uuid}/users/${u.uuid}`) userDetail.value = await apiJson(`/auth/api/admin/orgs/${u.org}/users/${u.uuid}`)
} catch (e) { } catch (e) {
userDetail.value = { error: e.message } userDetail.value = { error: e.message }
} }
@@ -532,7 +532,7 @@ async function refreshUserDetail() {
await loadOrgs() await loadOrgs()
if (selectedUser.value) { if (selectedUser.value) {
try { try {
userDetail.value = await apiJson(`/auth/api/admin/orgs/${selectedUser.value.org_uuid}/users/${selectedUser.value.uuid}`) userDetail.value = await apiJson(`/auth/api/admin/orgs/${selectedUser.value.org}/users/${selectedUser.value.uuid}`)
} catch (e) { authStore.showMessage(e.message || 'Failed to reload user', 'error') } } catch (e) { authStore.showMessage(e.message || 'Failed to reload user', 'error') }
} }
} }
@@ -594,7 +594,7 @@ async function submitDialog() {
// Close dialog immediately, then perform async operation // Close dialog immediately, then perform async operation
closeDialog() closeDialog()
apiJson(`/auth/api/admin/orgs/${role.org_uuid}/roles/${role.uuid}`, { method: 'PATCH', body: { display_name: name } }) apiJson(`/auth/api/admin/orgs/${role.org}/roles/${role.uuid}`, { method: 'PATCH', body: { display_name: name } })
.then(() => { .then(() => {
authStore.showMessage(`Role renamed to "${name}".`, 'success', 2500) authStore.showMessage(`Role renamed to "${name}".`, 'success', 2500)
loadOrgs() loadOrgs()
@@ -622,7 +622,7 @@ async function submitDialog() {
// Close dialog immediately, then perform async operation // Close dialog immediately, then perform async operation
closeDialog() closeDialog()
apiJson(`/auth/api/admin/orgs/${user.org_uuid}/users/${user.uuid}/display-name`, { method: 'PATCH', body: { display_name: name } }) apiJson(`/auth/api/admin/orgs/${user.org}/users/${user.uuid}/display-name`, { method: 'PATCH', body: { display_name: name } })
.then(() => { .then(() => {
authStore.showMessage(`User renamed to "${name}".`, 'success', 2500) authStore.showMessage(`User renamed to "${name}".`, 'success', 2500)
onUserNameSaved() onUserNameSaved()
+5 -5
View File
@@ -45,7 +45,7 @@ function handleEditName() {
async function handleDelete(credential) { async function handleDelete(credential) {
try { try {
const data = await apiJson(`/auth/api/admin/orgs/${props.selectedUser.org_uuid}/users/${props.selectedUser.uuid}/credentials/${credential.credential_uuid}`, { method: 'DELETE' }) const data = await apiJson(`/auth/api/admin/orgs/${props.selectedUser.org}/users/${props.selectedUser.uuid}/credentials/${credential.credential}`, { method: 'DELETE' })
if (data.status === 'ok') { if (data.status === 'ok') {
emit('onUserNameSaved') // Reuse to refresh user detail emit('onUserNameSaved') // Reuse to refresh user detail
} else { } else {
@@ -61,7 +61,7 @@ async function handleTerminateSession(session) {
if (!sessionId) return if (!sessionId) return
terminatingSessions.value = { ...terminatingSessions.value, [sessionId]: true } terminatingSessions.value = { ...terminatingSessions.value, [sessionId]: true }
try { try {
const data = await apiJson(`/auth/api/admin/orgs/${props.selectedUser.org_uuid}/users/${props.selectedUser.uuid}/sessions/${sessionId}`, { method: 'DELETE' }) const data = await apiJson(`/auth/api/admin/orgs/${props.selectedUser.org}/users/${props.selectedUser.uuid}/sessions/${sessionId}`, { method: 'DELETE' })
if (data.status === 'ok') { if (data.status === 'ok') {
if (data.current_session_terminated) { if (data.current_session_terminated) {
sessionStorage.clear() sessionStorage.clear()
@@ -183,7 +183,7 @@ defineExpose({ focusFirstElement })
:loading="loading" :loading="loading"
:org-display-name="userDetail.org.display_name" :org-display-name="userDetail.org.display_name"
:role-name="userDetail.role" :role-name="userDetail.role"
:update-endpoint="`/auth/api/admin/orgs/${selectedUser.org_uuid}/users/${selectedUser.uuid}/display-name`" :update-endpoint="`/auth/api/admin/orgs/${selectedUser.org}/users/${selectedUser.uuid}/display-name`"
@saved="$emit('onUserNameSaved')" @saved="$emit('onUserNameSaved')"
@edit-name="handleEditName" @edit-name="handleEditName"
/> />
@@ -212,7 +212,7 @@ defineExpose({ focusFirstElement })
:aaguid-info="userDetail.aaguid_info" :aaguid-info="userDetail.aaguid_info"
:allow-delete="true" :allow-delete="true"
:hovered-credential-uuid="hoveredCredentialUuid" :hovered-credential-uuid="hoveredCredentialUuid"
:hovered-session-credential-uuid="hoveredSession?.credential_uuid" :hovered-session-credential-uuid="hoveredSession?.credential"
:navigation-disabled="hasActiveModal" :navigation-disabled="hasActiveModal"
@delete="handleDelete" @delete="handleDelete"
@credential-hover="hoveredCredentialUuid = $event" @credential-hover="hoveredCredentialUuid = $event"
@@ -238,7 +238,7 @@ defineExpose({ focusFirstElement })
</div> </div>
<RegistrationLinkModal <RegistrationLinkModal
v-if="showRegModal" v-if="showRegModal"
:endpoint="`/auth/api/admin/orgs/${selectedUser.org_uuid}/users/${selectedUser.uuid}/create-link`" :endpoint="`/auth/api/admin/orgs/${selectedUser.org}/users/${selectedUser.uuid}/create-link`"
:user-name="userDetail?.display_name || selectedUser.display_name" :user-name="userDetail?.display_name || selectedUser.display_name"
@close="$emit('closeRegModal')" @close="$emit('closeRegModal')"
@copied="onLinkCopied" @copied="onLinkCopied"
+6 -6
View File
@@ -5,16 +5,16 @@
<template v-else> <template v-else>
<div <div
v-for="credential in credentials" v-for="credential in credentials"
:key="credential.credential_uuid" :key="credential.credential"
:class="['credential-item', { :class="['credential-item', {
'current-session': credential.is_current_session && !hoveredCredentialUuid && !hoveredSessionCredentialUuid, 'current-session': credential.is_current_session && !hoveredCredentialUuid && !hoveredSessionCredentialUuid,
'is-hovered': hoveredCredentialUuid === credential.credential_uuid, 'is-hovered': hoveredCredentialUuid === credential.credential,
'is-linked-session': hoveredSessionCredentialUuid === credential.credential_uuid 'is-linked-session': hoveredSessionCredentialUuid === credential.credential
}]" }]"
tabindex="-1" tabindex="-1"
@mousedown.prevent @mousedown.prevent
@click.capture="handleCardClick" @click.capture="handleCardClick"
@focusin="handleCredentialFocus(credential.credential_uuid)" @focusin="handleCredentialFocus(credential.credential)"
@focusout="handleCredentialBlur($event)" @focusout="handleCredentialBlur($event)"
@keydown="handleItemKeydown($event, credential)" @keydown="handleItemKeydown($event, credential)"
> >
@@ -33,8 +33,8 @@
<h4 class="item-title">{{ getCredentialAuthName(credential) }}</h4> <h4 class="item-title">{{ getCredentialAuthName(credential) }}</h4>
<div class="item-actions"> <div class="item-actions">
<span v-if="credential.is_current_session && !hoveredCredentialUuid && !hoveredSessionCredentialUuid" class="badge badge-current">Current</span> <span v-if="credential.is_current_session && !hoveredCredentialUuid && !hoveredSessionCredentialUuid" class="badge badge-current">Current</span>
<span v-else-if="hoveredCredentialUuid === credential.credential_uuid" class="badge badge-current">Selected</span> <span v-else-if="hoveredCredentialUuid === credential.credential" class="badge badge-current">Selected</span>
<span v-else-if="hoveredSessionCredentialUuid === credential.credential_uuid" class="badge badge-current">Linked</span> <span v-else-if="hoveredSessionCredentialUuid === credential.credential" class="badge badge-current">Linked</span>
<button <button
v-if="allowDelete" v-if="allowDelete"
@click="$emit('delete', credential)" @click="$emit('delete', credential)"
+2 -2
View File
@@ -47,7 +47,7 @@
:aaguid-info="authStore.userInfo?.aaguid_info || {}" :aaguid-info="authStore.userInfo?.aaguid_info || {}"
:loading="authStore.isLoading" :loading="authStore.isLoading"
:hovered-credential-uuid="hoveredCredentialUuid" :hovered-credential-uuid="hoveredCredentialUuid"
:hovered-session-credential-uuid="hoveredSession?.credential_uuid" :hovered-session-credential-uuid="hoveredSession?.credential"
:navigation-disabled="hasActiveModal" :navigation-disabled="hasActiveModal"
allow-delete allow-delete
@delete="handleDelete" @delete="handleDelete"
@@ -292,7 +292,7 @@ const handleLogoutButtonKeydown = (event) => {
} }
const handleDelete = async (credential) => { const handleDelete = async (credential) => {
const credentialId = credential?.credential_uuid const credentialId = credential?.credential
if (!credentialId) return if (!credentialId) return
try { try {
await authStore.deleteCredential(credentialId) await authStore.deleteCredential(credentialId)
+1 -1
View File
@@ -279,7 +279,7 @@ onUnmounted(() => {
defineExpose({ defineExpose({
showMessage, showMessage,
isAuthenticated, isAuthenticated,
userInfo session
}) })
</script> </script>
+2 -2
View File
@@ -20,7 +20,7 @@
:class="['session-item', { :class="['session-item', {
'is-current': session.is_current && !hoveredIp && !hoveredCredentialUuid, 'is-current': session.is_current && !hoveredIp && !hoveredCredentialUuid,
'is-hovered': hoveredSession?.id === session.id, 'is-hovered': hoveredSession?.id === session.id,
'is-linked-credential': hoveredCredentialUuid === session.credential_uuid 'is-linked-credential': hoveredCredentialUuid === session.credential
}]" }]"
tabindex="-1" tabindex="-1"
@mousedown.prevent @mousedown.prevent
@@ -34,7 +34,7 @@
<div class="item-actions"> <div class="item-actions">
<span v-if="session.is_current && !hoveredIp && !hoveredCredentialUuid" class="badge badge-current">Current</span> <span v-if="session.is_current && !hoveredIp && !hoveredCredentialUuid" class="badge badge-current">Current</span>
<span v-else-if="hoveredSession?.id === session.id" class="badge badge-current">Selected</span> <span v-else-if="hoveredSession?.id === session.id" class="badge badge-current">Selected</span>
<span v-else-if="hoveredCredentialUuid === session.credential_uuid" class="badge badge-current">Linked</span> <span v-else-if="hoveredCredentialUuid === session.credential" class="badge badge-current">Linked</span>
<span v-else-if="!hoveredCredentialUuid && isSameHost(session.ip)" class="badge">Same IP</span> <span v-else-if="!hoveredCredentialUuid && isSameHost(session.ip)" class="badge">Same IP</span>
<button <button
@click="$emit('terminate', session)" @click="$emit('terminate', session)"
+3 -3
View File
@@ -99,7 +99,7 @@ async def admin_list_orgs(request: Request, auth=AUTH_COOKIE):
def role_to_dict(r): def role_to_dict(r):
return { return {
"uuid": str(r.uuid), "uuid": str(r.uuid),
"org_uuid": str(r.org_uuid), "org": str(r.org),
"display_name": r.display_name, "display_name": r.display_name,
"permissions": r.permissions, "permissions": r.permissions,
} }
@@ -600,7 +600,7 @@ async def admin_get_user_detail(
aaguids.add(aaguid_str) aaguids.add(aaguid_str)
creds.append( creds.append(
{ {
"credential_uuid": str(c.uuid), "credential": str(c.uuid),
"aaguid": aaguid_str, "aaguid": aaguid_str,
"created_at": ( "created_at": (
c.created_at.astimezone(timezone.utc) c.created_at.astimezone(timezone.utc)
@@ -656,7 +656,7 @@ async def admin_get_user_detail(
sessions_payload.append( sessions_payload.append(
{ {
"id": entry.key, "id": entry.key,
"credential_uuid": str(entry.credential_uuid), "credential": str(entry.credential),
"host": entry.host, "host": entry.host,
"ip": entry.ip, "ip": entry.ip,
"user_agent": useragent.compact_user_agent(entry.user_agent), "user_agent": useragent.compact_user_agent(entry.user_agent),
+1 -1
View File
@@ -268,5 +268,5 @@ async def api_set_session(
session.set_session_cookie(response, auth.credentials) session.set_session_cookie(response, auth.credentials)
return { return {
"message": "Session cookie set successfully", "message": "Session cookie set successfully",
"user_uuid": str(user.user_uuid), "user": str(user.user),
} }
+1 -1
View File
@@ -179,7 +179,7 @@ async def websocket_remote_auth_request(ws: WebSocket):
): ):
response = { response = {
"status": "authenticated", "status": "authenticated",
"user_uuid": str(result_data["user_uuid"]), "user": str(result_data["user_uuid"]),
} }
if result_data.get("session_token"): if result_data.get("session_token"):
response["session_token"] = result_data["session_token"] response["session_token"] = result_data["session_token"]
+3 -3
View File
@@ -93,8 +93,8 @@ async def websocket_register_add(
assert isinstance(auth, str) and len(auth) == 16 assert isinstance(auth, str) and len(auth) == 16
await ws.send_json( await ws.send_json(
{ {
"user_uuid": str(user.uuid), "user": str(user.uuid),
"credential_uuid": str(credential.uuid), "credential": str(credential.uuid),
"session_token": auth, "session_token": auth,
"message": "New credential added successfully", "message": "New credential added successfully",
} }
@@ -164,7 +164,7 @@ async def websocket_authenticate(ws: WebSocket, auth=AUTH_COOKIE):
await ws.send_json( await ws.send_json(
{ {
"user_uuid": str(stored_cred.user), "user": str(stored_cred.user),
"session_token": token, "session_token": token,
} }
) )
+3 -3
View File
@@ -57,13 +57,13 @@ async def format_user_info(
user_aaguids.add(aaguid_str) user_aaguids.add(aaguid_str)
credentials.append( credentials.append(
{ {
"credential_uuid": str(c.uuid), "credential": str(c.uuid),
"aaguid": aaguid_str, "aaguid": aaguid_str,
"created_at": _format_datetime(c.created_at), "created_at": _format_datetime(c.created_at),
"last_used": _format_datetime(c.last_used), "last_used": _format_datetime(c.last_used),
"last_verified": _format_datetime(c.last_verified), "last_verified": _format_datetime(c.last_verified),
"sign_count": c.sign_count, "sign_count": c.sign_count,
"is_current_session": session_record.credential_uuid == c.uuid, "is_current_session": session_record.credential == c.uuid,
} }
) )
@@ -80,7 +80,7 @@ async def format_user_info(
sessions_payload.append( sessions_payload.append(
{ {
"id": entry.key, "id": entry.key,
"credential_uuid": str(entry.credential_uuid), "credential": str(entry.credential),
"host": entry.host, "host": entry.host,
"ip": entry.ip, "ip": entry.ip,
"user_agent": useragent.compact_user_agent(entry.user_agent), "user_agent": useragent.compact_user_agent(entry.user_agent),
+1 -1
View File
@@ -317,7 +317,7 @@ class TestSetSessionEndpoint:
) )
assert response.status_code == 200 assert response.status_code == 200
data = response.json() data = response.json()
assert "user_uuid" in data assert "user" in data
# Check that Set-Cookie header is present # Check that Set-Cookie header is present
assert "set-cookie" in response.headers assert "set-cookie" in response.headers