Add System token interface (#13549)

* Add system token interface

* use system token interface in users token field

* Update placeholder

* move notice below input

* fix clear value interaction

* update placeholder and notice text

* remove unused translation key

* rename class to match current naming

* fix bugs in disabled state and it's UX

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
Co-authored-by: jaycammarano <jay.cammarano@gmail.com>
This commit is contained in:
Azri Kahar
2022-05-27 06:01:19 +08:00
committed by GitHub
parent 374e0f47f5
commit 4e6c361a0e
4 changed files with 150 additions and 5 deletions

View File

@@ -135,10 +135,8 @@ fields:
template: '{{ name }}'
- field: token
interface: token
options:
iconRight: vpn_key
placeholder: $t:fields.directus_users.token_placeholder
interface: system-token
special: conceal
width: full
- field: id

View File

@@ -0,0 +1,13 @@
import { defineInterface } from '@directus/shared/utils';
import InterfaceSystemToken from './system-token.vue';
export default defineInterface({
id: 'system-token',
name: '$t:interfaces.system-token.system-token',
description: '$t:interfaces.system-token.description',
icon: 'vpn_key',
component: InterfaceSystemToken,
system: true,
types: ['hash'],
options: [],
});

View File

@@ -0,0 +1,126 @@
<template>
<div class="system-token">
<v-input
:model-value="localValue"
:type="!isNewTokenGenerated ? 'password' : 'text'"
:placeholder="placeholder"
:disabled="disabled"
readonly
:class="{ saved: value && !localValue }"
@update:model-value="emitValue"
>
<template #append>
<v-icon
v-if="!disabled"
v-tooltip="value ? t('interfaces.system-token.regenerate') : t('interfaces.system-token.generate')"
:name="value ? 'refresh' : 'add'"
class="regenerate-icon"
clickable
:disabled="disabled || loading"
@click="generateToken"
/>
<v-icon
v-tooltip="!disabled && value && t('interfaces.system-token.remove_token')"
:name="!disabled && value ? 'clear' : 'vpn_key'"
:class="{ 'clear-icon': !disabled && !!value, 'default-icon': disabled && value }"
:clickable="!disabled && !!value"
:disabled="loading || !value"
@click="emitValue(null)"
/>
</template>
</v-input>
<v-notice v-if="isNewTokenGenerated && value" type="info">
{{ t('interfaces.system-token.generate_success_copy') }}
</v-notice>
</div>
</template>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import api from '@/api';
import { unexpectedError } from '@/utils/unexpected-error';
interface Props {
value?: string | null;
disabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), { value: () => null, disabled: false });
const emit = defineEmits(['input']);
const { t } = useI18n();
const placeholder = computed(() => {
if (props.disabled && !props.value) return null;
return props.value ? t('interfaces.system-token.value_securely_saved') : t('interfaces.system-token.placeholder');
});
const localValue = ref<string | null>(null);
const loading = ref(false);
const isNewTokenGenerated = ref(false);
const regexp = new RegExp('^[*]+$');
watch(
() => props.value,
(newValue) => {
if (!newValue) {
localValue.value = null;
return;
}
if (newValue && regexp.test(newValue)) {
localValue.value = null;
isNewTokenGenerated.value = false;
}
},
{ immediate: true }
);
async function generateToken() {
loading.value = true;
try {
const response = await api.get('/utils/random/string');
emitValue(response.data.data);
isNewTokenGenerated.value = true;
} catch (err: any) {
unexpectedError(err);
} finally {
loading.value = false;
}
}
function emitValue(newValue: string | null) {
emit('input', newValue);
localValue.value = newValue;
}
</script>
<style lang="scss" scoped>
.v-input {
--v-input-font-family: var(--family-monospace);
}
.saved {
--v-input-placeholder-color: var(--primary);
}
.v-notice {
margin-top: 12px;
}
.regenerate-icon {
margin-right: 4px;
}
.clear-icon {
--v-icon-color-hover: var(--danger);
}
.default-icon {
--v-icon-color: var(--primary);
}
</style>

View File

@@ -1042,7 +1042,6 @@ fields:
status_archived: Archived
role: Role
token: Token
token_placeholder: Enter a secure access token...
provider: Provider
external_identifier: External Identifier
last_page: Last Page
@@ -1644,6 +1643,15 @@ interfaces:
header_color: Header Color
start_open: Start Open
start_closed: Start Closed
system-token:
system-token: Token
description: Manage static access token
placeholder: Click "Generate Token" to create a new static access token
value_securely_saved: Value Securely Saved
generate: Generate Token
regenerate: Regenerate Token
generate_success_copy: Make sure to backup and copy the token above. For security reasons, you will not be able to view the token again after saving and navigate off this page.
remove_token: Remove Token
displays:
translations:
translations: Translations