feat(api tokens): add basic api token overview
This commit is contained in:
parent
7b57b10804
commit
a20eef2453
@ -139,6 +139,17 @@
|
||||
"system": "System",
|
||||
"dark": "Dark"
|
||||
}
|
||||
},
|
||||
"apiTokens": {
|
||||
"title": "API Tokens",
|
||||
"general": "API tokens allow you to use Vikunja's api without user credentials.",
|
||||
"apiDocs": "Check out the api docs",
|
||||
"createToken": "Create a token",
|
||||
"attributes": {
|
||||
"title": "Title",
|
||||
"expiresAt": "Expires at",
|
||||
"permissions": "Permissions"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deletion": {
|
||||
|
13
src/modelTypes/IApiToken.ts
Normal file
13
src/modelTypes/IApiToken.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import type {IAbstract} from '@/modelTypes/IAbstract'
|
||||
|
||||
export interface IApiPermission {
|
||||
[key: string]: string[]
|
||||
}
|
||||
|
||||
export interface IApiToken extends IAbstract {
|
||||
id: number
|
||||
token: string
|
||||
permissions: IApiPermission
|
||||
expiresAt: Date
|
||||
created: Date
|
||||
}
|
20
src/models/apiTokenModel.ts
Normal file
20
src/models/apiTokenModel.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import AbstractModel from '@/models/abstractModel'
|
||||
import type {IApiToken} from '@/modelTypes/IApiToken'
|
||||
|
||||
export default class ApiTokenModel extends AbstractModel<IApiToken> {
|
||||
id = 0
|
||||
token = ''
|
||||
permissions = null
|
||||
expiresAt: Date = null
|
||||
created: Date = null
|
||||
|
||||
constructor(data: Partial<IApiToken>) {
|
||||
super()
|
||||
|
||||
this.assignData(data)
|
||||
|
||||
this.expiresAt = new Date(this.expiresAt)
|
||||
this.created = new Date(this.created)
|
||||
this.updated = new Date(this.updated)
|
||||
}
|
||||
}
|
@ -65,6 +65,7 @@ const UserSettingsEmailUpdateComponent = () => import('@/views/user/settings/Ema
|
||||
const UserSettingsGeneralComponent = () => import('@/views/user/settings/General.vue')
|
||||
const UserSettingsPasswordUpdateComponent = () => import('@/views/user/settings/PasswordUpdate.vue')
|
||||
const UserSettingsTOTPComponent = () => import('@/views/user/settings/TOTP.vue')
|
||||
const UserSettingsApiTokensComponent = () => import('@/views/user/settings/ApiTokens.vue')
|
||||
|
||||
// Project Handling
|
||||
const NewProjectComponent = () => import('@/views/project/NewProject.vue')
|
||||
@ -183,6 +184,11 @@ const router = createRouter({
|
||||
name: 'user.settings.totp',
|
||||
component: UserSettingsTOTPComponent,
|
||||
},
|
||||
{
|
||||
path: '/user/settings/api-tokens',
|
||||
name: 'user.settings.apiTokens',
|
||||
component: UserSettingsApiTokensComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
25
src/services/apiToken.ts
Normal file
25
src/services/apiToken.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import AbstractService from '@/services/abstractService'
|
||||
import type {IApiToken} from '@/modelTypes/IApiToken'
|
||||
import ApiTokenModel from '@/models/apiTokenModel'
|
||||
|
||||
export default class ApiTokenService extends AbstractService<IApiToken> {
|
||||
constructor() {
|
||||
super({
|
||||
create: '/tokens',
|
||||
getAll: '/tokens',
|
||||
delete: '/tokens/{id}',
|
||||
})
|
||||
}
|
||||
|
||||
processModel(model: IApiToken) {
|
||||
return {
|
||||
...model,
|
||||
expiresAt: new Date(model.expiresAt).toISOString(),
|
||||
created: new Date(model.created).toISOString(),
|
||||
}
|
||||
}
|
||||
|
||||
modelFactory(data: Partial<IApiToken>) {
|
||||
return new ApiTokenModel(data)
|
||||
}
|
||||
}
|
@ -75,6 +75,10 @@ const navigationItems = computed(() => {
|
||||
routeName: 'user.settings.caldav',
|
||||
condition: caldavEnabled.value,
|
||||
},
|
||||
{
|
||||
title: t('user.settings.apiTokens.title'),
|
||||
routeName: 'user.settings.apiTokens',
|
||||
},
|
||||
{
|
||||
title: t('user.deletion.title'),
|
||||
routeName: 'user.settings.deletion',
|
||||
|
69
src/views/user/settings/ApiTokens.vue
Normal file
69
src/views/user/settings/ApiTokens.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import ApiTokenService from '@/services/apiToken'
|
||||
import {computed, onMounted, ref} from 'vue'
|
||||
import { formatDateShort } from '@/helpers/time/formatDate'
|
||||
import XButton from '@/components/input/button.vue'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
const service = new ApiTokenService()
|
||||
const tokens = ref([])
|
||||
|
||||
const apiDocsUrl = window.API_URL + '/docs'
|
||||
|
||||
onMounted(async () => {
|
||||
tokens.value = await service.getAll()
|
||||
})
|
||||
|
||||
function deleteToken() {
|
||||
}
|
||||
|
||||
function createToken() {
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card :title="$t('user.settings.apiTokens.title')">
|
||||
|
||||
<p>
|
||||
{{ $t('user.settings.apiTokens.general') }}
|
||||
<BaseButton :href="apiDocsUrl">{{ $t('user.settings.apiTokens.apiDocs') }}</BaseButton>.
|
||||
</p>
|
||||
|
||||
<table class="table" v-if="tokens.length > 0">
|
||||
<tr>
|
||||
<th>{{ $t('misc.id') }}</th>
|
||||
<th>{{ $t('user.settings.apiTokens.attributes.title') }}</th>
|
||||
<th>{{ $t('user.settings.apiTokens.attributes.permissions') }}</th>
|
||||
<th>{{ $t('user.settings.apiTokens.attributes.expiresAt') }}</th>
|
||||
<th>{{ $t('misc.created') }}</th>
|
||||
<th class="has-text-right">{{ $t('misc.actions') }}</th>
|
||||
</tr>
|
||||
<tr v-for="tk in tokens" :key="tk.id">
|
||||
<td>{{ tk.id }}</td>
|
||||
<td>{{ tk.title }}</td>
|
||||
<td>
|
||||
<template v-for="(v, p) in tk.permissions">
|
||||
<strong>{{ p }}:</strong>
|
||||
{{ v.join(', ') }}
|
||||
<br/>
|
||||
</template>
|
||||
</td>
|
||||
<td>{{ formatDateShort(tk.expiresAt) }}</td>
|
||||
<td>{{ formatDateShort(tk.created) }}</td>
|
||||
<td class="has-text-right">
|
||||
<x-button variant="secondary" @click="deleteToken(tk)">
|
||||
{{ $t('misc.delete') }}
|
||||
</x-button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<x-button icon="plus" class="mb-4" @click="createToken" :loading="service.loading">
|
||||
{{ $t('user.settings.apiTokens.createToken') }}
|
||||
</x-button>
|
||||
</card>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user