mirror of
https://github.com/directus/directus.git
synced 2026-01-23 13:37:58 -05:00
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:
@@ -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
|
||||
|
||||
13
app/src/interfaces/_system/system-token/index.ts
Normal file
13
app/src/interfaces/_system/system-token/index.ts
Normal 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: [],
|
||||
});
|
||||
126
app/src/interfaces/_system/system-token/system-token.vue
Normal file
126
app/src/interfaces/_system/system-token/system-token.vue
Normal 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>
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user