From a20eef245374be7c19d0becc9533e3e37fdc8345 Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 1 Sep 2023 11:15:48 +0200 Subject: [PATCH] feat(api tokens): add basic api token overview --- src/i18n/lang/en.json | 11 +++++ src/modelTypes/IApiToken.ts | 13 +++++ src/models/apiTokenModel.ts | 20 ++++++++ src/router/index.ts | 6 +++ src/services/apiToken.ts | 25 ++++++++++ src/views/user/Settings.vue | 4 ++ src/views/user/settings/ApiTokens.vue | 69 +++++++++++++++++++++++++++ 7 files changed, 148 insertions(+) create mode 100644 src/modelTypes/IApiToken.ts create mode 100644 src/models/apiTokenModel.ts create mode 100644 src/services/apiToken.ts create mode 100644 src/views/user/settings/ApiTokens.vue diff --git a/src/i18n/lang/en.json b/src/i18n/lang/en.json index be6bf4ed6..538a62a75 100644 --- a/src/i18n/lang/en.json +++ b/src/i18n/lang/en.json @@ -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": { diff --git a/src/modelTypes/IApiToken.ts b/src/modelTypes/IApiToken.ts new file mode 100644 index 000000000..842e242ad --- /dev/null +++ b/src/modelTypes/IApiToken.ts @@ -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 +} \ No newline at end of file diff --git a/src/models/apiTokenModel.ts b/src/models/apiTokenModel.ts new file mode 100644 index 000000000..dc3d69f17 --- /dev/null +++ b/src/models/apiTokenModel.ts @@ -0,0 +1,20 @@ +import AbstractModel from '@/models/abstractModel' +import type {IApiToken} from '@/modelTypes/IApiToken' + +export default class ApiTokenModel extends AbstractModel { + id = 0 + token = '' + permissions = null + expiresAt: Date = null + created: Date = null + + constructor(data: Partial) { + super() + + this.assignData(data) + + this.expiresAt = new Date(this.expiresAt) + this.created = new Date(this.created) + this.updated = new Date(this.updated) + } +} \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts index 3aaa786e9..d00b6d4c4 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -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, + }, ], }, { diff --git a/src/services/apiToken.ts b/src/services/apiToken.ts new file mode 100644 index 000000000..5c29370e3 --- /dev/null +++ b/src/services/apiToken.ts @@ -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 { + 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) { + return new ApiTokenModel(data) + } +} \ No newline at end of file diff --git a/src/views/user/Settings.vue b/src/views/user/Settings.vue index 0af37c141..4403e3650 100644 --- a/src/views/user/Settings.vue +++ b/src/views/user/Settings.vue @@ -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', diff --git a/src/views/user/settings/ApiTokens.vue b/src/views/user/settings/ApiTokens.vue new file mode 100644 index 000000000..2ae763f0c --- /dev/null +++ b/src/views/user/settings/ApiTokens.vue @@ -0,0 +1,69 @@ + + + + + \ No newline at end of file