@drax/identity-vue 0.0.31 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/composables/useUserApiKey.ts +81 -0
- package/src/cruds/tenant-crud/TenantList.vue +6 -0
- package/src/cruds/user-api-key-crud/UserApiKeyCrud.vue +203 -0
- package/src/cruds/user-api-key-crud/UserApiKeyList.vue +113 -0
- package/src/forms/UserApiKeyForm.vue +79 -0
- package/src/index.ts +16 -0
- package/src/pages/UserApiKeyCrudPage.vue +12 -0
- package/src/routes/IdentityRoutes.ts +10 -1
- package/src/views/UserApiKeyView.vue +24 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.1.5",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./src/index.ts",
|
|
9
9
|
"module": "./src/index.ts",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"format": "prettier --write src/"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@drax/common-front": "^0.0
|
|
28
|
-
"@drax/common-share": "^0.0
|
|
29
|
-
"@drax/common-vue": "^0.0
|
|
30
|
-
"@drax/identity-share": "^0.0
|
|
27
|
+
"@drax/common-front": "^0.1.0",
|
|
28
|
+
"@drax/common-share": "^0.1.0",
|
|
29
|
+
"@drax/common-vue": "^0.1.0",
|
|
30
|
+
"@drax/identity-share": "^0.1.0",
|
|
31
31
|
"vue-i18n": "^9.13.1"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"vue-tsc": "^2.0.11",
|
|
65
65
|
"vuetify": "^3.6.4"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "b9efcbc5785e3a02dbb6ca0255527ee2be1d16c0"
|
|
68
68
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {inject, ref} from "vue";
|
|
2
|
+
import type {IUserApiKey, IUserApiKeyBase} from "@drax/identity-share";
|
|
3
|
+
import type { UserApiKeySystem} from "@drax/identity-front";
|
|
4
|
+
import {ClientError} from "@drax/common-front";
|
|
5
|
+
import type { IClientInputError} from "@drax/common-front";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export function useUserApiKey() {
|
|
9
|
+
|
|
10
|
+
const userApiKeySystem = inject('UserApiKeySystem') as UserApiKeySystem
|
|
11
|
+
|
|
12
|
+
let userApiKeyError = ref<string>('')
|
|
13
|
+
let inputErrors = ref<IClientInputError>()
|
|
14
|
+
let loading = ref(false);
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async function paginateUserApiKey({page= 1, limit= 5, orderBy="", orderDesc=false, search = ""}) {
|
|
18
|
+
loading.value = true
|
|
19
|
+
let paginateduserApiKey = userApiKeySystem.paginate({page, limit, orderBy, orderDesc, search})
|
|
20
|
+
loading.value = false
|
|
21
|
+
return paginateduserApiKey
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function createUserApiKey(userApiKeyData: IUserApiKeyBase) {
|
|
25
|
+
try {
|
|
26
|
+
loading.value = true
|
|
27
|
+
let userApiKey: IUserApiKey = await userApiKeySystem.create(userApiKeyData)
|
|
28
|
+
return userApiKey
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err instanceof ClientError) {
|
|
31
|
+
inputErrors.value = err.inputErrors
|
|
32
|
+
}if(err instanceof Error) {
|
|
33
|
+
userApiKeyError.value = err.message
|
|
34
|
+
}
|
|
35
|
+
throw err
|
|
36
|
+
}finally {
|
|
37
|
+
loading.value = false
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function editUserApiKey(id: string, userApiKeyData: IUserApiKeyBase) {
|
|
43
|
+
try {
|
|
44
|
+
loading.value = true
|
|
45
|
+
let userApiKey: IUserApiKey = await userApiKeySystem.update(id, userApiKeyData)
|
|
46
|
+
return userApiKey
|
|
47
|
+
} catch (err) {
|
|
48
|
+
|
|
49
|
+
if (err instanceof ClientError) {
|
|
50
|
+
inputErrors.value = err.inputErrors
|
|
51
|
+
}
|
|
52
|
+
if(err instanceof Error) {
|
|
53
|
+
userApiKeyError.value = err.message
|
|
54
|
+
}
|
|
55
|
+
throw err
|
|
56
|
+
}finally {
|
|
57
|
+
loading.value = false
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function deleteUserApiKey(id: string) {
|
|
62
|
+
try {
|
|
63
|
+
loading.value = true
|
|
64
|
+
await userApiKeySystem.delete(id)
|
|
65
|
+
} catch (err) {
|
|
66
|
+
console.log("composable delete error: ", err, )
|
|
67
|
+
if (err instanceof ClientError) {
|
|
68
|
+
inputErrors.value = err.inputErrors
|
|
69
|
+
}
|
|
70
|
+
if(err instanceof Error) {
|
|
71
|
+
userApiKeyError.value = err.message
|
|
72
|
+
}
|
|
73
|
+
throw err
|
|
74
|
+
}finally {
|
|
75
|
+
loading.value = false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {paginateUserApiKey, createUserApiKey, editUserApiKey, deleteUserApiKey, loading, userApiKeyError, inputErrors}
|
|
80
|
+
|
|
81
|
+
}
|
|
@@ -5,6 +5,7 @@ import {useTenant} from "../../composables/useTenant";
|
|
|
5
5
|
import {useAuth} from "../../composables/useAuth";
|
|
6
6
|
import {useI18n} from "vue-i18n";
|
|
7
7
|
import type {ITenant} from "@drax/identity-share";
|
|
8
|
+
import dayjs from "dayjs";
|
|
8
9
|
|
|
9
10
|
const {hasPermission} = useAuth()
|
|
10
11
|
const {paginateTenant} = useTenant()
|
|
@@ -25,6 +26,7 @@ const loading = ref(false)
|
|
|
25
26
|
const search = ref('')
|
|
26
27
|
const headers = ref<any>([
|
|
27
28
|
{ title: t('tenant.name') as string, key: 'name', align: 'start' },
|
|
29
|
+
{ title: t('tenant.createdAt') as string, key: 'createdAt', align: 'start' },
|
|
28
30
|
{ title: '', key: 'actions', align: 'end', minWidth: '150px' },
|
|
29
31
|
])
|
|
30
32
|
|
|
@@ -81,6 +83,10 @@ defineExpose({
|
|
|
81
83
|
</v-toolbar>
|
|
82
84
|
</template>
|
|
83
85
|
|
|
86
|
+
<template v-slot:item.createdAt="{ value }" >
|
|
87
|
+
{{value ? dayjs(value).format('YYYY-MM-DD') : '' }}
|
|
88
|
+
</template>
|
|
89
|
+
|
|
84
90
|
|
|
85
91
|
<template v-slot:item.actions="{item}" >
|
|
86
92
|
<v-btn v-if="hasPermission('tenant:update')" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {computed, ref} from 'vue'
|
|
3
|
+
import UserApiKeyList from "./UserApiKeyList.vue";
|
|
4
|
+
import {useUserApiKey} from "../../composables/useUserApiKey";
|
|
5
|
+
import type {IUserApiKey, IUserApiKeyBase} from "@drax/identity-share";
|
|
6
|
+
import {useCopy} from "@drax/common-vue";
|
|
7
|
+
import UserApiKeyForm from "../../forms/UserApiKeyForm.vue";
|
|
8
|
+
import UserApiKeyView from "../../views/UserApiKeyView.vue";
|
|
9
|
+
|
|
10
|
+
const {createUserApiKey, editUserApiKey, deleteUserApiKey, loading, userApiKeyError, inputErrors} = useUserApiKey()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const {copy} = useCopy()
|
|
14
|
+
|
|
15
|
+
interface UserApiKeyList {
|
|
16
|
+
loadItems: () => void;
|
|
17
|
+
items: IUserApiKey[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type DialogMode = 'create' | 'edit' | 'delete' | null;
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
let dialog = ref(false);
|
|
24
|
+
let success = ref(false);
|
|
25
|
+
let dialogMode = ref<DialogMode>(null);
|
|
26
|
+
let dialogTitle = ref('');
|
|
27
|
+
const userApiKeyList = ref<UserApiKeyList | null>(null);
|
|
28
|
+
let form = ref<IUserApiKeyBase>({name: "", ipv4: [], ipv6: []})
|
|
29
|
+
let target = ref<IUserApiKey>();
|
|
30
|
+
let targetId = ref<string>('');
|
|
31
|
+
let filterEnable = ref(false);
|
|
32
|
+
|
|
33
|
+
let userApiKeyCreated = ref<IUserApiKey>();
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
function cancel() {
|
|
37
|
+
dialog.value = false
|
|
38
|
+
userApiKeyCreated.value = undefined
|
|
39
|
+
success.value = false
|
|
40
|
+
inputErrors.value = {}
|
|
41
|
+
userApiKeyError.value = '';
|
|
42
|
+
dialogMode.value = null
|
|
43
|
+
dialogTitle.value = ''
|
|
44
|
+
targetId.value = ''
|
|
45
|
+
target.value = undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function save() {
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
if (dialogMode.value === 'create') {
|
|
52
|
+
userApiKeyCreated.value = await createUserApiKey(form.value)
|
|
53
|
+
|
|
54
|
+
} else if (dialogMode.value === 'edit') {
|
|
55
|
+
await editUserApiKey(targetId.value, form.value)
|
|
56
|
+
} else if (dialogMode.value === 'delete') {
|
|
57
|
+
await deleteUserApiKey(targetId.value)
|
|
58
|
+
}
|
|
59
|
+
success.value = true
|
|
60
|
+
inputErrors.value = {}
|
|
61
|
+
userApiKeyError.value = '';
|
|
62
|
+
if (userApiKeyList.value !== null) {
|
|
63
|
+
userApiKeyList.value.loadItems()
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.error(e)
|
|
67
|
+
if (e instanceof Error) {
|
|
68
|
+
userApiKeyError.value = e.message
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let buttonText = computed(() => {
|
|
74
|
+
switch (dialogMode.value) {
|
|
75
|
+
case 'create':
|
|
76
|
+
return 'action.create'
|
|
77
|
+
case 'edit':
|
|
78
|
+
return 'action.update'
|
|
79
|
+
case 'delete':
|
|
80
|
+
return 'action.delete'
|
|
81
|
+
default:
|
|
82
|
+
return 'action.sent'
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
function toCreate() {
|
|
87
|
+
dialogMode.value = 'create';
|
|
88
|
+
dialogTitle.value = 'userApiKey.creating';
|
|
89
|
+
form.value = {name: "", ipv4: [], ipv6: []}
|
|
90
|
+
dialog.value = true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function toEdit(item: IUserApiKey) {
|
|
94
|
+
console.log('toEdit', item)
|
|
95
|
+
dialogMode.value = 'edit';
|
|
96
|
+
dialogTitle.value = 'userApiKey.updating';
|
|
97
|
+
targetId.value = item.id;
|
|
98
|
+
form.value = {
|
|
99
|
+
name: item.name,
|
|
100
|
+
ipv4: item.ipv4 ? item.ipv4 : [],
|
|
101
|
+
ipv6: item.ipv6 ? item.ipv6 : [],
|
|
102
|
+
}
|
|
103
|
+
dialog.value = true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function toDelete(item: IUserApiKey) {
|
|
107
|
+
console.log('toDelete', item)
|
|
108
|
+
dialogMode.value = 'delete';
|
|
109
|
+
dialogTitle.value = 'userApiKey.deleting';
|
|
110
|
+
target.value = item
|
|
111
|
+
const {id} = item;
|
|
112
|
+
targetId.value = id;
|
|
113
|
+
dialog.value = true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<template>
|
|
119
|
+
<v-container fluid>
|
|
120
|
+
|
|
121
|
+
<v-card border rounded>
|
|
122
|
+
<v-toolbar class="bg-toolbar">
|
|
123
|
+
<v-toolbar-title>{{ $t('userApiKey.managing') }}</v-toolbar-title>
|
|
124
|
+
<v-spacer></v-spacer>
|
|
125
|
+
<v-btn icon @click="filterEnable = !filterEnable">
|
|
126
|
+
<v-icon>{{ filterEnable ? 'mdi-filter' : 'mdi-filter-off' }}</v-icon>
|
|
127
|
+
</v-btn>
|
|
128
|
+
<v-btn class="font-weight-bold" color="primary" @click="toCreate">
|
|
129
|
+
{{ $t('action.create') }}
|
|
130
|
+
</v-btn>
|
|
131
|
+
</v-toolbar>
|
|
132
|
+
<v-theme-provider with-background class="pa-2 rounded-b">
|
|
133
|
+
<UserApiKeyList
|
|
134
|
+
ref="userApiKeyList"
|
|
135
|
+
@toEdit="toEdit"
|
|
136
|
+
@toDelete="toDelete"
|
|
137
|
+
:filterEnable="filterEnable"
|
|
138
|
+
/>
|
|
139
|
+
</v-theme-provider>
|
|
140
|
+
</v-card>
|
|
141
|
+
|
|
142
|
+
<v-dialog v-model="dialog" max-width="800">
|
|
143
|
+
<v-sheet border>
|
|
144
|
+
<v-toolbar>
|
|
145
|
+
<v-toolbar-title>{{ dialogTitle ? $t(dialogTitle) : '-' }}</v-toolbar-title>
|
|
146
|
+
</v-toolbar>
|
|
147
|
+
<v-card class="pa-10">
|
|
148
|
+
<v-card-text v-if="userApiKeyError">
|
|
149
|
+
<v-alert type="error">{{ userApiKeyError }}</v-alert>
|
|
150
|
+
</v-card-text>
|
|
151
|
+
<v-card-text v-if="success">
|
|
152
|
+
<v-alert type="success">{{ $t('action.success') }}</v-alert>
|
|
153
|
+
</v-card-text>
|
|
154
|
+
<v-card-text>
|
|
155
|
+
<UserApiKeyForm v-if="dialogMode === 'create' || dialogMode === 'edit'"
|
|
156
|
+
v-model="form"
|
|
157
|
+
:inputErrors="inputErrors"
|
|
158
|
+
@formSubmit="save"
|
|
159
|
+
/>
|
|
160
|
+
<v-text-field
|
|
161
|
+
v-if="userApiKeyCreated"
|
|
162
|
+
label="API KEY"
|
|
163
|
+
v-model="userApiKeyCreated.secret"
|
|
164
|
+
color="success"
|
|
165
|
+
base-color="success"
|
|
166
|
+
variant="outlined"
|
|
167
|
+
@click:append="copy(userApiKeyCreated.secret)"
|
|
168
|
+
:hint="$t('userApiKey.secretWarning')"
|
|
169
|
+
persistent-hint
|
|
170
|
+
>
|
|
171
|
+
<template v-slot:append>
|
|
172
|
+
<v-btn icon class="text-success" @click="copy(userApiKeyCreated.secret)"><v-icon>mdi mdi-content-copy</v-icon></v-btn>
|
|
173
|
+
</template>
|
|
174
|
+
|
|
175
|
+
</v-text-field>
|
|
176
|
+
<UserApiKeyView v-if="dialogMode === 'delete' && target" :userApiKey="target"></UserApiKeyView>
|
|
177
|
+
</v-card-text>
|
|
178
|
+
<v-card-actions>
|
|
179
|
+
<v-spacer></v-spacer>
|
|
180
|
+
<v-btn variant="text" @click="cancel" :loading="loading">
|
|
181
|
+
{{success ? $t('action.close') : $t('action.cancel')}}
|
|
182
|
+
</v-btn>
|
|
183
|
+
<v-btn
|
|
184
|
+
v-if="!success"
|
|
185
|
+
variant="flat"
|
|
186
|
+
:color="dialogMode==='delete' ? 'red' : 'primary'"
|
|
187
|
+
@click="save"
|
|
188
|
+
:loading="loading"
|
|
189
|
+
>
|
|
190
|
+
{{ $t(buttonText) }}
|
|
191
|
+
</v-btn>
|
|
192
|
+
</v-card-actions>
|
|
193
|
+
|
|
194
|
+
</v-card>
|
|
195
|
+
</v-sheet>
|
|
196
|
+
</v-dialog>
|
|
197
|
+
|
|
198
|
+
</v-container>
|
|
199
|
+
</template>
|
|
200
|
+
|
|
201
|
+
<style scoped>
|
|
202
|
+
|
|
203
|
+
</style>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
import {defineProps, type Ref, ref} from "vue";
|
|
4
|
+
import {useUserApiKey} from "../../composables/useUserApiKey";
|
|
5
|
+
import {useAuth} from "../../composables/useAuth";
|
|
6
|
+
import {useI18n} from "vue-i18n";
|
|
7
|
+
import type {IUserApiKey} from "@drax/identity-share";
|
|
8
|
+
import dayjs from "dayjs";
|
|
9
|
+
|
|
10
|
+
const {hasPermission} = useAuth()
|
|
11
|
+
const {paginateUserApiKey} = useUserApiKey()
|
|
12
|
+
const {t} = useI18n()
|
|
13
|
+
|
|
14
|
+
defineProps({
|
|
15
|
+
filterEnable: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
default: false,
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const itemsPerPage = ref(5)
|
|
22
|
+
const page = ref(1)
|
|
23
|
+
const serverItems: Ref<IUserApiKey[]> = ref([]);
|
|
24
|
+
const totalItems = ref(0)
|
|
25
|
+
const loading = ref(false)
|
|
26
|
+
const search = ref('')
|
|
27
|
+
|
|
28
|
+
const headers = ref<any>([
|
|
29
|
+
...( hasPermission('userApiKey:view') ? [{ title: t('userApiKey.user') as string, key: 'user.username', align: 'start' }] : []),
|
|
30
|
+
{ title: t('userApiKey.name') as string, key: 'name', align: 'start' },
|
|
31
|
+
{ title: t('userApiKey.ipv4') as string, key: 'ipv4', align: 'start' },
|
|
32
|
+
{ title: t('userApiKey.ipv6') as string, key: 'ipv6', align: 'start' },
|
|
33
|
+
{ title: t('userApiKey.createdAt') as string, key: 'createdAt', align: 'start' },
|
|
34
|
+
{ title: '', key: 'actions', align: 'end', minWidth: '150px' },
|
|
35
|
+
])
|
|
36
|
+
|
|
37
|
+
async function loadItems(){
|
|
38
|
+
try{
|
|
39
|
+
loading.value = true
|
|
40
|
+
const r = await paginateUserApiKey({
|
|
41
|
+
page: page.value,
|
|
42
|
+
limit: itemsPerPage.value,
|
|
43
|
+
search: search.value})
|
|
44
|
+
serverItems.value = r.items
|
|
45
|
+
totalItems.value = r.total
|
|
46
|
+
}catch (e){
|
|
47
|
+
console.error(e)
|
|
48
|
+
}finally {
|
|
49
|
+
loading.value = false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
defineExpose({
|
|
56
|
+
loadItems
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<template>
|
|
62
|
+
<v-data-table-server
|
|
63
|
+
class="border"
|
|
64
|
+
v-if="hasPermission('userApiKey:manage')"
|
|
65
|
+
v-model:items-per-page="itemsPerPage"
|
|
66
|
+
v-model:page="page"
|
|
67
|
+
:headers="headers"
|
|
68
|
+
:items="serverItems"
|
|
69
|
+
:items-length="totalItems"
|
|
70
|
+
:loading="loading"
|
|
71
|
+
:search="search"
|
|
72
|
+
item-value="name"
|
|
73
|
+
@update:options="loadItems"
|
|
74
|
+
>
|
|
75
|
+
<template v-slot:top>
|
|
76
|
+
<v-toolbar density="compact" v-if="filterEnable">
|
|
77
|
+
<v-toolbar-title>{{ $t('action.filters') }}</v-toolbar-title>
|
|
78
|
+
<v-spacer></v-spacer>
|
|
79
|
+
<v-text-field v-model="search" hide-details
|
|
80
|
+
density="compact" class="mr-2"
|
|
81
|
+
variant="outlined"
|
|
82
|
+
append-inner-icon="mdi-magnify"
|
|
83
|
+
:label="$t('action.search')"
|
|
84
|
+
single-line clearable @click:clear="() => search = ''"
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
</v-toolbar>
|
|
88
|
+
</template>
|
|
89
|
+
|
|
90
|
+
<template v-slot:item.ipv4="{ value }" >
|
|
91
|
+
<v-chip v-for="(ip,index) in value" :key="index">{{ip}}</v-chip>
|
|
92
|
+
</template>
|
|
93
|
+
|
|
94
|
+
<template v-slot:item.ipv6="{ value }" >
|
|
95
|
+
<v-chip v-for="(ip,index) in value" :key="index">{{ip}}</v-chip>
|
|
96
|
+
</template>
|
|
97
|
+
|
|
98
|
+
<template v-slot:item.createdAt="{ value }" >
|
|
99
|
+
{{value ? dayjs(value).format('YYYY-MM-DD') : '' }}
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
<template v-slot:item.actions="{item}" >
|
|
104
|
+
<v-btn v-if="hasPermission('userApiKey:update')" icon="mdi-pencil" variant="text" color="primary" @click="$emit('toEdit', item)"></v-btn>
|
|
105
|
+
<v-btn v-if="hasPermission('userApiKey:delete')" icon="mdi-delete" class="mr-1" variant="text" color="red" @click="$emit('toDelete', item)"></v-btn>
|
|
106
|
+
</template>
|
|
107
|
+
|
|
108
|
+
</v-data-table-server>
|
|
109
|
+
</template>
|
|
110
|
+
|
|
111
|
+
<style scoped>
|
|
112
|
+
|
|
113
|
+
</style>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {defineModel, type PropType} from "vue";
|
|
3
|
+
import type {IUserApiKeyBase} from "@drax/identity-share";
|
|
4
|
+
import {useI18nValidation} from "@drax/common-vue";
|
|
5
|
+
import type {IClientInputError} from "@drax/common-front";
|
|
6
|
+
import {useI18n} from "vue-i18n";
|
|
7
|
+
|
|
8
|
+
const {$ta} = useI18nValidation()
|
|
9
|
+
const {t} = useI18n()
|
|
10
|
+
|
|
11
|
+
defineProps({
|
|
12
|
+
inputErrors: {
|
|
13
|
+
type: Object as PropType<IClientInputError>,
|
|
14
|
+
default: () => ({name: "", ipv4: "", ipv6: ""})
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const form = defineModel<IUserApiKeyBase>({
|
|
19
|
+
type: Object,
|
|
20
|
+
default: () => ({name: "", ipv4: [], ipv6: []})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// Define emits
|
|
24
|
+
const emits = defineEmits(['formSubmit'])
|
|
25
|
+
|
|
26
|
+
// Function to call when form is attempted to be submitted
|
|
27
|
+
const onSubmit = () => {
|
|
28
|
+
emits('formSubmit', form); // Emitting an event with the form data
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ipv6Regex = /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/
|
|
32
|
+
const ipv4Regex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/
|
|
33
|
+
|
|
34
|
+
const ipv4Address = [
|
|
35
|
+
(v: string[]) => !v.some(ip => !ipv4Regex.test(ip)) || t('validation.invalidIpv4'),
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const ipv6Address = [
|
|
39
|
+
(v: string[]) => !v.some(ip => !ipv6Regex.test(ip)) || t('validation.invalidIpv6'),
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<v-form @submit.prevent="onSubmit" validate-on="blur">
|
|
46
|
+
<v-text-field
|
|
47
|
+
variant="outlined"
|
|
48
|
+
id="name-input"
|
|
49
|
+
:label="$t('userApiKey.name')"
|
|
50
|
+
v-model="form.name"
|
|
51
|
+
prepend-inner-icon="mdi-card-account-details"
|
|
52
|
+
required
|
|
53
|
+
:error-messages="$ta(inputErrors.name)"
|
|
54
|
+
></v-text-field>
|
|
55
|
+
|
|
56
|
+
<v-combobox
|
|
57
|
+
v-model="form.ipv4 as string[]"
|
|
58
|
+
:label="$t('userApiKey.ipv4')"
|
|
59
|
+
variant="outlined"
|
|
60
|
+
multiple chips
|
|
61
|
+
validate-on="blur"
|
|
62
|
+
:rules="ipv4Address"
|
|
63
|
+
></v-combobox>
|
|
64
|
+
|
|
65
|
+
<v-combobox
|
|
66
|
+
v-model="form.ipv6 as string[]"
|
|
67
|
+
:label="$t('userApiKey.ipv6')"
|
|
68
|
+
variant="outlined"
|
|
69
|
+
multiple chips
|
|
70
|
+
validate-on="blur"
|
|
71
|
+
:rules="ipv6Address"
|
|
72
|
+
></v-combobox>
|
|
73
|
+
|
|
74
|
+
</v-form>
|
|
75
|
+
</template>
|
|
76
|
+
|
|
77
|
+
<style scoped>
|
|
78
|
+
|
|
79
|
+
</style>
|
package/src/index.ts
CHANGED
|
@@ -27,10 +27,18 @@ import TenantCrud from "./cruds/tenant-crud/TenantCrud.vue";
|
|
|
27
27
|
import TenantList from "./cruds/tenant-crud/TenantList.vue";
|
|
28
28
|
import TenantCrudPage from "./pages/TenantCrudPage.vue";
|
|
29
29
|
|
|
30
|
+
|
|
31
|
+
import UserApiKeyForm from "./forms/UserApiKeyForm.vue";
|
|
32
|
+
import UserApiKeyView from "./views/UserApiKeyView.vue";
|
|
33
|
+
import UserApiKeyCrud from "./cruds/user-api-key-crud/UserApiKeyCrud.vue";
|
|
34
|
+
import UserApiKeyList from "./cruds/user-api-key-crud/UserApiKeyList.vue";
|
|
35
|
+
import UserApiKeyCrudPage from "./pages/UserApiKeyCrudPage.vue";
|
|
36
|
+
|
|
30
37
|
import {useAuth} from "./composables/useAuth.js";
|
|
31
38
|
import {useUser} from "./composables/useUser.js";
|
|
32
39
|
import {useRole} from "./composables/useRole.js";
|
|
33
40
|
import {useTenant} from "./composables/useTenant.js";
|
|
41
|
+
import {useUserApiKey} from "./composables/useUserApiKey.js";
|
|
34
42
|
|
|
35
43
|
|
|
36
44
|
import {useAuthStore} from "./stores/auth/AuthStore.js";
|
|
@@ -77,6 +85,14 @@ export {
|
|
|
77
85
|
useTenant,
|
|
78
86
|
|
|
79
87
|
|
|
88
|
+
//UserApiKey
|
|
89
|
+
UserApiKeyView,
|
|
90
|
+
UserApiKeyForm,
|
|
91
|
+
UserApiKeyCrud,
|
|
92
|
+
UserApiKeyList,
|
|
93
|
+
UserApiKeyCrudPage,
|
|
94
|
+
useUserApiKey,
|
|
95
|
+
|
|
80
96
|
//Stores
|
|
81
97
|
useAuthStore,
|
|
82
98
|
|
|
@@ -4,6 +4,7 @@ import PasswordPage from '../pages/PasswordPage.vue'
|
|
|
4
4
|
import UserCrudPage from '../pages/UserCrudPage.vue'
|
|
5
5
|
import RoleCrudPage from '../pages/RoleCrudPage.vue'
|
|
6
6
|
import TenantCrudPage from '../pages/TenantCrudPage.vue'
|
|
7
|
+
import UserApiKeyCrudPage from '../pages/UserApiKeyCrudPage.vue'
|
|
7
8
|
|
|
8
9
|
const routes = [
|
|
9
10
|
{
|
|
@@ -57,7 +58,15 @@ const routes = [
|
|
|
57
58
|
permission: 'user:manage'
|
|
58
59
|
}
|
|
59
60
|
},
|
|
60
|
-
|
|
61
|
+
{
|
|
62
|
+
name: 'CrudUserApiKey',
|
|
63
|
+
path: '/crud/user-api-key',
|
|
64
|
+
component: UserApiKeyCrudPage,
|
|
65
|
+
meta: {
|
|
66
|
+
auth: true,
|
|
67
|
+
permission: 'userApiKey:manage'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
61
70
|
]
|
|
62
71
|
|
|
63
72
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type {PropType} from "vue";
|
|
3
|
+
import type {IUserApiKey} from "@drax/identity-share";
|
|
4
|
+
|
|
5
|
+
defineProps({
|
|
6
|
+
userApiKey:{
|
|
7
|
+
type: Object as PropType<IUserApiKey>,
|
|
8
|
+
required: true,
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<v-sheet border class="position-relative d-flex justify-center align-center" height="150">
|
|
15
|
+
<v-sheet class="text-center">
|
|
16
|
+
<h6 class="text-h6">{{$t ? $t('userApiKey.name') : 'Name'}} : {{ userApiKey.name }}</h6>
|
|
17
|
+
</v-sheet>
|
|
18
|
+
</v-sheet>
|
|
19
|
+
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<style scoped>
|
|
23
|
+
|
|
24
|
+
</style>
|