diff --git a/src/components/common/TokenManager.vue b/src/components/common/TokenManager.vue new file mode 100644 index 0000000..2eda461 --- /dev/null +++ b/src/components/common/TokenManager.vue @@ -0,0 +1,287 @@ + + + + + + + + Token Name + + + + + Role + + + {{ role.label }} + + + + + {{ error }} + + + + + + + + + + + Make sure to copy your token now. You won't be able to see it again! + + + + Your API Token + + {{ generatedToken.token }} + + + + + + + + + {{ copied ? 'Copied!' : 'Copy' }} + + + + + + + Cancel + + Generate Token + + Done + + + + + diff --git a/src/components/layout/Header.vue b/src/components/layout/Header.vue index b6a5a2a..12e96c5 100644 --- a/src/components/layout/Header.vue +++ b/src/components/layout/Header.vue @@ -39,6 +39,11 @@ function logout() { authStore.logout() router.push('/login') } + +function goToSettingsTokens() { + showUserMenu.value = false + router.push('/settings/tokens') +} @@ -104,6 +109,14 @@ function logout() { {{ authStore.user?.role }} + + + + + + API Tokens + + diff --git a/src/router/index.ts b/src/router/index.ts index d6cbd36..40ec2be 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -32,6 +32,12 @@ const router = createRouter({ component: () => import('@/views/DocumentView.vue'), meta: { requiresAuth: true } }, + { + path: '/settings/tokens', + name: 'settings-tokens', + component: () => import('@/views/SettingsTokens.vue'), + meta: { requiresAuth: true } + }, { path: '/:pathMatch(.*)*', redirect: '/dashboard' diff --git a/src/stores/auth.ts b/src/stores/auth.ts index 3f0507b..13f5bc9 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -1,6 +1,6 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' -import type { Agent, AuthResponse } from '@/types' +import type { Agent, AuthResponse, ApiToken, ApiTokenGenerateResponse, ApiTokenCreate } from '@/types' import { useApi } from '@/composables/useApi' import router from '@/router' @@ -58,6 +58,23 @@ export const useAuthStore = defineStore('auth', () => { fetchUser() } + async function fetchTokens(): Promise { + if (!token.value) return [] + try { + return await api.get('/auth/tokens') + } catch { + return [] + } + } + + async function generateToken(data: ApiTokenCreate): Promise { + return await api.post('/auth/token/generate', data) + } + + async function revokeToken(tokenId: string): Promise { + await api.delete(`/auth/tokens/${tokenId}`) + } + return { token, user, @@ -65,6 +82,9 @@ export const useAuthStore = defineStore('auth', () => { login, register, fetchUser, - logout + logout, + fetchTokens, + generateToken, + revokeToken } }) diff --git a/src/types/index.ts b/src/types/index.ts index ab41b28..cc74291 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -234,3 +234,24 @@ export interface TagForm { name: string color: string } + +// API Token types +export type TokenRole = 'researcher' | 'developer' | 'viewer' + +export interface ApiToken { + id: string + name: string + role: TokenRole + created_at: string +} + +export interface ApiTokenGenerateResponse { + token: string + name: string + role: TokenRole +} + +export interface ApiTokenCreate { + name: string + role: TokenRole +} diff --git a/src/views/SettingsTokens.vue b/src/views/SettingsTokens.vue new file mode 100644 index 0000000..62dc92c --- /dev/null +++ b/src/views/SettingsTokens.vue @@ -0,0 +1,411 @@ + + + + + + + API Tokens + + Manage your personal API tokens for programmatic access + + + + + + + + Create Token + + + + + + + Loading tokens... + + + + + + + + No API tokens yet + Create your first API token to access Claudia Docs programmatically + + Create Your First Token + + + + + + + + + Name + Role + Created + Actions + + + + + {{ token.name }} + + + {{ token.role }} + + + {{ formatDate(token.created_at) }} + + + + + + + Revoke + + + + + + + + + + + + + + Are you sure you want to revoke the token "{{ tokenToRevoke?.name }}"? + This action cannot be undone and any applications using this token will lose access. + + + Cancel + Revoke Token + + + + + + + + + + {{ toastMessage }} + + + + + +
{{ error }}
Make sure to copy your token now. You won't be able to see it again!
{{ generatedToken.token }}
+ Manage your personal API tokens for programmatic access +
Loading tokens...
Create your first API token to access Claudia Docs programmatically
+ Are you sure you want to revoke the token "{{ tokenToRevoke?.name }}"? + This action cannot be undone and any applications using this token will lose access. +