@sfxcode/nuxt-ui-mongocamp 1.1.0
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/LICENSE +21 -0
- package/README.md +308 -0
- package/dist/module.d.mts +8 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +35 -0
- package/dist/runtime/components/MongocampAccount.d.vue.ts +3 -0
- package/dist/runtime/components/MongocampAccount.vue +197 -0
- package/dist/runtime/components/MongocampAccount.vue.d.ts +3 -0
- package/dist/runtime/components/MongocampCollectionData.d.vue.ts +6 -0
- package/dist/runtime/components/MongocampCollectionData.vue +511 -0
- package/dist/runtime/components/MongocampCollectionData.vue.d.ts +6 -0
- package/dist/runtime/components/MongocampCollectionInfos.d.vue.ts +9 -0
- package/dist/runtime/components/MongocampCollectionInfos.vue +393 -0
- package/dist/runtime/components/MongocampCollectionInfos.vue.d.ts +9 -0
- package/dist/runtime/components/MongocampCollections.d.vue.ts +7 -0
- package/dist/runtime/components/MongocampCollections.vue +231 -0
- package/dist/runtime/components/MongocampCollections.vue.d.ts +7 -0
- package/dist/runtime/components/MongocampDatabases.d.vue.ts +3 -0
- package/dist/runtime/components/MongocampDatabases.vue +112 -0
- package/dist/runtime/components/MongocampDatabases.vue.d.ts +3 -0
- package/dist/runtime/components/MongocampJobs.d.vue.ts +3 -0
- package/dist/runtime/components/MongocampJobs.vue +354 -0
- package/dist/runtime/components/MongocampJobs.vue.d.ts +3 -0
- package/dist/runtime/components/MongocampLogin.d.vue.ts +3 -0
- package/dist/runtime/components/MongocampLogin.vue +63 -0
- package/dist/runtime/components/MongocampLogin.vue.d.ts +3 -0
- package/dist/runtime/components/MongocampRoleGrants.d.vue.ts +6 -0
- package/dist/runtime/components/MongocampRoleGrants.vue +276 -0
- package/dist/runtime/components/MongocampRoleGrants.vue.d.ts +6 -0
- package/dist/runtime/components/MongocampRoles.d.vue.ts +6 -0
- package/dist/runtime/components/MongocampRoles.vue +272 -0
- package/dist/runtime/components/MongocampRoles.vue.d.ts +6 -0
- package/dist/runtime/components/MongocampUsers.d.vue.ts +3 -0
- package/dist/runtime/components/MongocampUsers.vue +320 -0
- package/dist/runtime/components/MongocampUsers.vue.d.ts +3 -0
- package/dist/runtime/components/MongocampVersion.d.vue.ts +3 -0
- package/dist/runtime/components/MongocampVersion.vue +25 -0
- package/dist/runtime/components/MongocampVersion.vue.d.ts +3 -0
- package/dist/runtime/composables/useMongocampAccount.d.ts +7 -0
- package/dist/runtime/composables/useMongocampAccount.js +28 -0
- package/dist/runtime/composables/useMongocampAdmin.d.ts +14 -0
- package/dist/runtime/composables/useMongocampAdmin.js +48 -0
- package/dist/runtime/composables/useMongocampBucket.d.ts +15 -0
- package/dist/runtime/composables/useMongocampBucket.js +109 -0
- package/dist/runtime/composables/useMongocampCollection.d.ts +17 -0
- package/dist/runtime/composables/useMongocampCollection.js +12 -0
- package/dist/runtime/composables/useMongocampDocument.d.ts +13 -0
- package/dist/runtime/composables/useMongocampDocument.js +18 -0
- package/dist/runtime/composables/useMongocampDynamicForm.d.ts +14 -0
- package/dist/runtime/composables/useMongocampDynamicForm.js +231 -0
- package/dist/runtime/composables/useMongocampIndex.d.ts +9 -0
- package/dist/runtime/composables/useMongocampIndex.js +23 -0
- package/dist/runtime/composables/useMongocampJobs.d.ts +9 -0
- package/dist/runtime/composables/useMongocampJobs.js +25 -0
- package/dist/runtime/composables/useMongocampQuery.d.ts +99 -0
- package/dist/runtime/composables/useMongocampQuery.js +108 -0
- package/dist/runtime/composables/useMongocampQueryBuilder.d.ts +30 -0
- package/dist/runtime/composables/useMongocampQueryBuilder.js +112 -0
- package/dist/runtime/composables/useMongocampSchema.d.ts +14 -0
- package/dist/runtime/composables/useMongocampSchema.js +142 -0
- package/dist/runtime/composables/useMongocampSystem.d.ts +12 -0
- package/dist/runtime/composables/useMongocampSystem.js +21 -0
- package/dist/runtime/plugin.d.ts +6 -0
- package/dist/runtime/plugin.js +23 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/types.d.mts +3 -0
- package/package.json +99 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { h, reactive, ref, resolveComponent } from "vue";
|
|
3
|
+
import useMongocampAdmin from "../composables/useMongocampAdmin";
|
|
4
|
+
const { listUsers, addUser, deleteUser, updateUserRoles, updateUserPassword, listRoles } = useMongocampAdmin();
|
|
5
|
+
const users = ref([]);
|
|
6
|
+
const loading = ref(false);
|
|
7
|
+
const filterText = ref("");
|
|
8
|
+
const sorting = ref([]);
|
|
9
|
+
let filterTimer;
|
|
10
|
+
const isAddModalOpen = ref(false);
|
|
11
|
+
const isEditModalOpen = ref(false);
|
|
12
|
+
const isDeleteModalOpen = ref(false);
|
|
13
|
+
const userToDelete = ref("");
|
|
14
|
+
const errorMessage = ref("");
|
|
15
|
+
const newUser = ref({
|
|
16
|
+
userId: "",
|
|
17
|
+
password: "",
|
|
18
|
+
roles: []
|
|
19
|
+
});
|
|
20
|
+
const editUser = ref({
|
|
21
|
+
userId: "",
|
|
22
|
+
password: "",
|
|
23
|
+
roles: []
|
|
24
|
+
});
|
|
25
|
+
const addUserSchema = reactive([
|
|
26
|
+
{
|
|
27
|
+
$formkit: "nuxtUIInput",
|
|
28
|
+
name: "userId",
|
|
29
|
+
label: "Email",
|
|
30
|
+
validation: "required|email"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
$formkit: "nuxtUIInput",
|
|
34
|
+
name: "password",
|
|
35
|
+
label: "Password",
|
|
36
|
+
inputType: "password",
|
|
37
|
+
validation: "required|length:3"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
$formkit: "nuxtUIListbox",
|
|
41
|
+
name: "roles",
|
|
42
|
+
label: "Roles",
|
|
43
|
+
displayMode: "transfer",
|
|
44
|
+
options: [],
|
|
45
|
+
filter: true,
|
|
46
|
+
class: "h-60",
|
|
47
|
+
transferHeaderClass: "text-sm text-gray-500 font-medium",
|
|
48
|
+
transferAll: true,
|
|
49
|
+
transferLeftHeaderText: "Available",
|
|
50
|
+
transferRightHeaderText: "Assigned"
|
|
51
|
+
}
|
|
52
|
+
]);
|
|
53
|
+
const editUserSchema = reactive([
|
|
54
|
+
{
|
|
55
|
+
$formkit: "nuxtUIInput",
|
|
56
|
+
name: "password",
|
|
57
|
+
label: "New Password",
|
|
58
|
+
inputType: "password",
|
|
59
|
+
validation: "length:3,100",
|
|
60
|
+
help: "Leave empty to keep current password"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
$formkit: "nuxtUIListbox",
|
|
64
|
+
name: "roles",
|
|
65
|
+
label: "Roles",
|
|
66
|
+
displayMode: "transfer",
|
|
67
|
+
filter: true,
|
|
68
|
+
class: "h-60",
|
|
69
|
+
transferHeaderClass: "text-sm text-gray-500 font-medium",
|
|
70
|
+
options: [],
|
|
71
|
+
transferAll: true,
|
|
72
|
+
transferLeftHeaderText: "Available",
|
|
73
|
+
transferRightHeaderText: "Assigned"
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
async function fetchUsers() {
|
|
77
|
+
loading.value = true;
|
|
78
|
+
try {
|
|
79
|
+
users.value = await listUsers(filterText.value);
|
|
80
|
+
} finally {
|
|
81
|
+
loading.value = false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function onFilterInput() {
|
|
85
|
+
clearTimeout(filterTimer);
|
|
86
|
+
filterTimer = setTimeout(() => fetchUsers(), 300);
|
|
87
|
+
}
|
|
88
|
+
async function fetchRoles() {
|
|
89
|
+
const roles = await listRoles();
|
|
90
|
+
const items = roles.map((r) => ({ value: r.name, label: r.name }));
|
|
91
|
+
addUserSchema[2].options = items;
|
|
92
|
+
editUserSchema[1].options = items;
|
|
93
|
+
}
|
|
94
|
+
async function handleAddUser() {
|
|
95
|
+
errorMessage.value = "";
|
|
96
|
+
try {
|
|
97
|
+
const roles = newUser.value.roles.map((r) => r.value);
|
|
98
|
+
await addUser(newUser.value.userId, newUser.value.password, "", roles);
|
|
99
|
+
isAddModalOpen.value = false;
|
|
100
|
+
newUser.value = { userId: "", password: "", roles: [] };
|
|
101
|
+
await fetchUsers();
|
|
102
|
+
} catch (e) {
|
|
103
|
+
errorMessage.value = e instanceof Error ? e.message : "Failed to add user";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function openEdit(user) {
|
|
107
|
+
editUser.value = {
|
|
108
|
+
userId: user.user,
|
|
109
|
+
password: "",
|
|
110
|
+
roles: (user.roles ?? []).map((r) => ({ value: r, label: r }))
|
|
111
|
+
};
|
|
112
|
+
isEditModalOpen.value = true;
|
|
113
|
+
}
|
|
114
|
+
async function handleEditUser() {
|
|
115
|
+
errorMessage.value = "";
|
|
116
|
+
try {
|
|
117
|
+
const roles = editUser.value.roles.map((r) => r.value);
|
|
118
|
+
await updateUserRoles(editUser.value.userId, roles);
|
|
119
|
+
if (editUser.value.password) {
|
|
120
|
+
await updateUserPassword(editUser.value.userId, editUser.value.password);
|
|
121
|
+
}
|
|
122
|
+
isEditModalOpen.value = false;
|
|
123
|
+
await fetchUsers();
|
|
124
|
+
} catch (e) {
|
|
125
|
+
errorMessage.value = e instanceof Error ? e.message : "Failed to update user";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function confirmDelete(userId) {
|
|
129
|
+
userToDelete.value = userId;
|
|
130
|
+
isDeleteModalOpen.value = true;
|
|
131
|
+
}
|
|
132
|
+
async function handleDeleteUser() {
|
|
133
|
+
await deleteUser(userToDelete.value);
|
|
134
|
+
isDeleteModalOpen.value = false;
|
|
135
|
+
await fetchUsers();
|
|
136
|
+
}
|
|
137
|
+
const UBadge = resolveComponent("UBadge");
|
|
138
|
+
const UButton = resolveComponent("UButton");
|
|
139
|
+
const UTooltip = resolveComponent("UTooltip");
|
|
140
|
+
function withTooltip(text, node) {
|
|
141
|
+
return h(UTooltip, { text }, { default: () => node });
|
|
142
|
+
}
|
|
143
|
+
function sortHeader(column, label) {
|
|
144
|
+
const isSorted = column.getIsSorted();
|
|
145
|
+
return h(UButton, {
|
|
146
|
+
color: "neutral",
|
|
147
|
+
variant: "ghost",
|
|
148
|
+
label,
|
|
149
|
+
icon: isSorted === "asc" ? "i-lucide-arrow-up-narrow-wide" : isSorted === "desc" ? "i-lucide-arrow-down-wide-narrow" : "i-lucide-arrow-up-down",
|
|
150
|
+
class: "-mx-2.5",
|
|
151
|
+
onClick: () => column.toggleSorting(isSorted === "asc")
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const columns = [
|
|
155
|
+
{
|
|
156
|
+
accessorKey: "user",
|
|
157
|
+
header: ({ column }) => sortHeader(column, "User")
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
accessorKey: "isAdmin",
|
|
161
|
+
header: ({ column }) => sortHeader(column, "Admin"),
|
|
162
|
+
cell: ({ row }) => h(UBadge, {
|
|
163
|
+
label: row.original.isAdmin ? "Admin" : "User",
|
|
164
|
+
color: row.original.isAdmin ? "error" : "neutral",
|
|
165
|
+
variant: "subtle"
|
|
166
|
+
})
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
accessorKey: "roles",
|
|
170
|
+
header: "Roles",
|
|
171
|
+
cell: ({ row }) => {
|
|
172
|
+
const roles = row.original.roles ?? [];
|
|
173
|
+
if (!roles.length)
|
|
174
|
+
return h("span", { class: "text-dimmed" }, "\u2014");
|
|
175
|
+
return h(
|
|
176
|
+
"div",
|
|
177
|
+
{ class: "flex flex-wrap gap-1" },
|
|
178
|
+
roles.map((role) => h(UBadge, { label: role, color: "primary", variant: "subtle" }))
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "actions",
|
|
184
|
+
header: "",
|
|
185
|
+
cell: ({ row }) => h("div", { class: "flex gap-1 justify-end" }, [
|
|
186
|
+
withTooltip("Edit user", h(UButton, {
|
|
187
|
+
"icon": "i-lucide-pencil",
|
|
188
|
+
"color": "neutral",
|
|
189
|
+
"variant": "ghost",
|
|
190
|
+
"size": "sm",
|
|
191
|
+
"aria-label": "Edit user",
|
|
192
|
+
"onClick": () => openEdit(row.original)
|
|
193
|
+
})),
|
|
194
|
+
withTooltip("Delete user", h(UButton, {
|
|
195
|
+
"icon": "i-lucide-trash-2",
|
|
196
|
+
"color": "error",
|
|
197
|
+
"variant": "ghost",
|
|
198
|
+
"size": "sm",
|
|
199
|
+
"aria-label": "Delete user",
|
|
200
|
+
"onClick": () => confirmDelete(row.original.user)
|
|
201
|
+
}))
|
|
202
|
+
])
|
|
203
|
+
}
|
|
204
|
+
];
|
|
205
|
+
fetchUsers();
|
|
206
|
+
fetchRoles();
|
|
207
|
+
</script>
|
|
208
|
+
|
|
209
|
+
<template>
|
|
210
|
+
<div class="flex flex-col gap-4">
|
|
211
|
+
<div class="flex items-center justify-between gap-4">
|
|
212
|
+
<h2 class="text-xl font-semibold">
|
|
213
|
+
Users
|
|
214
|
+
</h2>
|
|
215
|
+
<div class="flex flex-1 items-center gap-2">
|
|
216
|
+
<UInput
|
|
217
|
+
v-model="filterText"
|
|
218
|
+
icon="i-lucide-search"
|
|
219
|
+
placeholder="Search users..."
|
|
220
|
+
size="sm"
|
|
221
|
+
class="flex-1 max-w-xs"
|
|
222
|
+
@input="onFilterInput"
|
|
223
|
+
/>
|
|
224
|
+
<UTooltip text="Refresh">
|
|
225
|
+
<UButton
|
|
226
|
+
icon="i-lucide-refresh-cw"
|
|
227
|
+
color="neutral"
|
|
228
|
+
variant="ghost"
|
|
229
|
+
:loading="loading"
|
|
230
|
+
aria-label="Refresh"
|
|
231
|
+
@click="fetchUsers"
|
|
232
|
+
/>
|
|
233
|
+
</UTooltip>
|
|
234
|
+
<UButton
|
|
235
|
+
icon="i-lucide-plus"
|
|
236
|
+
label="Add User"
|
|
237
|
+
@click="isAddModalOpen = true"
|
|
238
|
+
/>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<UTable
|
|
243
|
+
v-model:sorting="sorting"
|
|
244
|
+
:data="users"
|
|
245
|
+
:columns="columns"
|
|
246
|
+
:loading="loading"
|
|
247
|
+
/>
|
|
248
|
+
|
|
249
|
+
<!-- Add User Modal -->
|
|
250
|
+
<UModal
|
|
251
|
+
v-model:open="isAddModalOpen"
|
|
252
|
+
title="Add User"
|
|
253
|
+
>
|
|
254
|
+
<template #body>
|
|
255
|
+
<FUDataEdit
|
|
256
|
+
:data="newUser"
|
|
257
|
+
:schema="addUserSchema"
|
|
258
|
+
submit-label="Add User"
|
|
259
|
+
submit-icon="i-lucide-plus"
|
|
260
|
+
@data-saved="handleAddUser"
|
|
261
|
+
/>
|
|
262
|
+
<p
|
|
263
|
+
v-if="errorMessage"
|
|
264
|
+
class="mt-2 text-sm text-error-500"
|
|
265
|
+
>
|
|
266
|
+
{{ errorMessage }}
|
|
267
|
+
</p>
|
|
268
|
+
</template>
|
|
269
|
+
</UModal>
|
|
270
|
+
|
|
271
|
+
<!-- Edit User Modal -->
|
|
272
|
+
<UModal
|
|
273
|
+
v-model:open="isEditModalOpen"
|
|
274
|
+
:title="`Edit User: ${editUser.userId}`"
|
|
275
|
+
>
|
|
276
|
+
<template #body>
|
|
277
|
+
<FUDataEdit
|
|
278
|
+
:data="editUser"
|
|
279
|
+
:schema="editUserSchema"
|
|
280
|
+
submit-label="Save Changes"
|
|
281
|
+
submit-icon="i-lucide-save"
|
|
282
|
+
@data-saved="handleEditUser"
|
|
283
|
+
/>
|
|
284
|
+
<p
|
|
285
|
+
v-if="errorMessage"
|
|
286
|
+
class="mt-2 text-sm text-error-500"
|
|
287
|
+
>
|
|
288
|
+
{{ errorMessage }}
|
|
289
|
+
</p>
|
|
290
|
+
</template>
|
|
291
|
+
</UModal>
|
|
292
|
+
|
|
293
|
+
<!-- Delete Confirmation Modal -->
|
|
294
|
+
<UModal
|
|
295
|
+
v-model:open="isDeleteModalOpen"
|
|
296
|
+
title="Delete User"
|
|
297
|
+
:ui="{ footer: 'justify-end' }"
|
|
298
|
+
>
|
|
299
|
+
<template #body>
|
|
300
|
+
<p>
|
|
301
|
+
Are you sure you want to delete user <strong>{{ userToDelete }}</strong>?
|
|
302
|
+
This action cannot be undone.
|
|
303
|
+
</p>
|
|
304
|
+
</template>
|
|
305
|
+
<template #footer>
|
|
306
|
+
<UButton
|
|
307
|
+
label="Cancel"
|
|
308
|
+
color="neutral"
|
|
309
|
+
variant="ghost"
|
|
310
|
+
@click="isDeleteModalOpen = false"
|
|
311
|
+
/>
|
|
312
|
+
<UButton
|
|
313
|
+
label="Delete"
|
|
314
|
+
color="error"
|
|
315
|
+
@click="handleDeleteUser"
|
|
316
|
+
/>
|
|
317
|
+
</template>
|
|
318
|
+
</UModal>
|
|
319
|
+
</div>
|
|
320
|
+
</template>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useNuxtApp } from "#app";
|
|
4
|
+
const { $mongocampVersion } = useNuxtApp();
|
|
5
|
+
const version = computed(() => $mongocampVersion.value);
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<UBadge
|
|
10
|
+
v-if="version"
|
|
11
|
+
color="primary"
|
|
12
|
+
variant="subtle"
|
|
13
|
+
icon="i-lucide-database"
|
|
14
|
+
>
|
|
15
|
+
{{ version.name }} v{{ version.version }}
|
|
16
|
+
</UBadge>
|
|
17
|
+
<UBadge
|
|
18
|
+
v-else
|
|
19
|
+
color="neutral"
|
|
20
|
+
variant="subtle"
|
|
21
|
+
icon="i-lucide-database-zap"
|
|
22
|
+
>
|
|
23
|
+
MongoCamp unavailable
|
|
24
|
+
</UBadge>
|
|
25
|
+
</template>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UserProfile } from '@sfxcode/nuxt-mongocamp-server';
|
|
2
|
+
export declare function useMongocampAccount(): {
|
|
3
|
+
changingPassword: import("vue").Ref<boolean, boolean>;
|
|
4
|
+
fetchProfile: () => Promise<UserProfile>;
|
|
5
|
+
changePassword: (password: string) => Promise<boolean>;
|
|
6
|
+
regenerateApiKey: () => Promise<string>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
import { useMongocampApi, useToast } from "#imports";
|
|
3
|
+
export function useMongocampAccount() {
|
|
4
|
+
const { authApi } = useMongocampApi();
|
|
5
|
+
const toast = useToast();
|
|
6
|
+
const changingPassword = ref(false);
|
|
7
|
+
async function fetchProfile() {
|
|
8
|
+
return authApi.userProfile();
|
|
9
|
+
}
|
|
10
|
+
async function changePassword(password) {
|
|
11
|
+
changingPassword.value = true;
|
|
12
|
+
try {
|
|
13
|
+
const result = await authApi.updatePassword({ passwordUpdateRequest: { password } });
|
|
14
|
+
toast.add({ title: "Password changed", description: "Your password was updated.", color: "success" });
|
|
15
|
+
return result.value;
|
|
16
|
+
} catch {
|
|
17
|
+
toast.add({ title: "Change failed", description: "Could not change your password.", color: "error" });
|
|
18
|
+
return false;
|
|
19
|
+
} finally {
|
|
20
|
+
changingPassword.value = false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function regenerateApiKey() {
|
|
24
|
+
const result = await authApi.generateNewApiKey();
|
|
25
|
+
return result.value;
|
|
26
|
+
}
|
|
27
|
+
return { changingPassword, fetchProfile, changePassword, regenerateApiKey };
|
|
28
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Grant, Role, UserProfile } from '@sfxcode/nuxt-mongocamp-server';
|
|
2
|
+
declare const _default: () => {
|
|
3
|
+
listUsers: (filter?: string) => Promise<UserProfile[]>;
|
|
4
|
+
addUser: (userId: string, password: string, apiKey?: string, roles?: string[]) => Promise<UserProfile>;
|
|
5
|
+
deleteUser: (userId: string) => Promise<import("@sfxcode/nuxt-mongocamp-server").JsonValueBoolean>;
|
|
6
|
+
updateUserRoles: (userId: string, roles: string[]) => Promise<UserProfile>;
|
|
7
|
+
updateUserPassword: (userId: string, password: string) => Promise<import("@sfxcode/nuxt-mongocamp-server").JsonValueBoolean>;
|
|
8
|
+
listRoles: (filter?: string) => Promise<Role[]>;
|
|
9
|
+
addRole: (name: string, isAdmin?: boolean, collectionGrants?: Grant[]) => Promise<Role>;
|
|
10
|
+
updateRole: (roleName: string, isAdmin: boolean, collectionGrants?: Grant[]) => Promise<Role>;
|
|
11
|
+
deleteRole: (roleName: string) => Promise<import("@sfxcode/nuxt-mongocamp-server").JsonValueBoolean>;
|
|
12
|
+
listCollections: () => Promise<string[]>;
|
|
13
|
+
};
|
|
14
|
+
export default _default;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useMongocampApi } from "#imports";
|
|
2
|
+
export default () => {
|
|
3
|
+
const { adminApi, collectionApi } = useMongocampApi();
|
|
4
|
+
async function listUsers(filter = "") {
|
|
5
|
+
return adminApi.listUsers({ filter });
|
|
6
|
+
}
|
|
7
|
+
async function addUser(userId, password, apiKey = "", roles = []) {
|
|
8
|
+
return adminApi.addUser({
|
|
9
|
+
userInformation: {
|
|
10
|
+
userId,
|
|
11
|
+
password,
|
|
12
|
+
apiKey,
|
|
13
|
+
roles
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async function deleteUser(userId) {
|
|
18
|
+
return adminApi.deleteUser({ userId });
|
|
19
|
+
}
|
|
20
|
+
async function updateUserRoles(userId, roles) {
|
|
21
|
+
return adminApi.updateRolesForUser({ userId, requestBody: roles });
|
|
22
|
+
}
|
|
23
|
+
async function updateUserPassword(userId, password) {
|
|
24
|
+
return adminApi.updatePasswordForUser({ userId, passwordUpdateRequest: { password } });
|
|
25
|
+
}
|
|
26
|
+
async function listRoles(filter = "") {
|
|
27
|
+
return adminApi.listRoles({ filter });
|
|
28
|
+
}
|
|
29
|
+
async function listCollections() {
|
|
30
|
+
return collectionApi.listCollections();
|
|
31
|
+
}
|
|
32
|
+
async function addRole(name, isAdmin = false, collectionGrants = []) {
|
|
33
|
+
return adminApi.addRole({
|
|
34
|
+
role: {
|
|
35
|
+
name,
|
|
36
|
+
isAdmin,
|
|
37
|
+
collectionGrants
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function updateRole(roleName, isAdmin, collectionGrants = []) {
|
|
42
|
+
return adminApi.updateRole({ roleName, updateRoleRequest: { isAdmin, collectionGrants } });
|
|
43
|
+
}
|
|
44
|
+
async function deleteRole(roleName) {
|
|
45
|
+
return adminApi.deleteRole({ roleName });
|
|
46
|
+
}
|
|
47
|
+
return { listUsers, addUser, deleteUser, updateUserRoles, updateUserPassword, listRoles, addRole, updateRole, deleteRole, listCollections };
|
|
48
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BucketInformation } from '@sfxcode/nuxt-mongocamp-server';
|
|
2
|
+
export declare function useMongocampBucket(): {
|
|
3
|
+
isBucketCollection: (collectionName: string) => boolean;
|
|
4
|
+
bucketNameFor: (collectionName: string) => string;
|
|
5
|
+
fileIdForRow: (collectionName: string, row: Record<string, unknown>) => string | undefined;
|
|
6
|
+
downloadingFileIds: import("vue").Ref<Set<string> & Omit<Set<string>, keyof Set<any>>, Set<string> | (Set<string> & Omit<Set<string>, keyof Set<any>>)>;
|
|
7
|
+
uploading: import("vue").Ref<boolean, boolean>;
|
|
8
|
+
downloadFile: (collectionName: string, fileId: string) => Promise<void>;
|
|
9
|
+
uploadFile: (collectionName: string, file: File) => Promise<boolean>;
|
|
10
|
+
bucketActionInFlight: import("vue").Ref<Set<string> & Omit<Set<string>, keyof Set<any>>, Set<string> | (Set<string> & Omit<Set<string>, keyof Set<any>>)>;
|
|
11
|
+
listBuckets: () => Promise<string[]>;
|
|
12
|
+
getBucketInfo: (bucketName: string) => Promise<BucketInformation>;
|
|
13
|
+
clearBucket: (bucketName: string) => Promise<boolean>;
|
|
14
|
+
deleteBucket: (bucketName: string) => Promise<boolean>;
|
|
15
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
import { useMongocampApi, useToast } from "#imports";
|
|
3
|
+
const BUCKET_COLLECTION_SUFFIX = /\.(?:files|chunks)$/;
|
|
4
|
+
function unwrapId(value) {
|
|
5
|
+
if (typeof value === "string") return value;
|
|
6
|
+
if (typeof value === "object" && value !== null && "$oid" in value) {
|
|
7
|
+
return String(value.$oid);
|
|
8
|
+
}
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
function triggerBrowserDownload(blob, filename) {
|
|
12
|
+
const url = URL.createObjectURL(blob);
|
|
13
|
+
const link = document.createElement("a");
|
|
14
|
+
link.href = url;
|
|
15
|
+
link.download = filename;
|
|
16
|
+
document.body.appendChild(link);
|
|
17
|
+
link.click();
|
|
18
|
+
document.body.removeChild(link);
|
|
19
|
+
URL.revokeObjectURL(url);
|
|
20
|
+
}
|
|
21
|
+
export function useMongocampBucket() {
|
|
22
|
+
const { fileApi, bucketApi } = useMongocampApi();
|
|
23
|
+
const toast = useToast();
|
|
24
|
+
const downloadingFileIds = ref(/* @__PURE__ */ new Set());
|
|
25
|
+
const uploading = ref(false);
|
|
26
|
+
const bucketActionInFlight = ref(/* @__PURE__ */ new Set());
|
|
27
|
+
function isBucketCollection(collectionName) {
|
|
28
|
+
return BUCKET_COLLECTION_SUFFIX.test(collectionName);
|
|
29
|
+
}
|
|
30
|
+
function bucketNameFor(collectionName) {
|
|
31
|
+
return collectionName.replace(BUCKET_COLLECTION_SUFFIX, "");
|
|
32
|
+
}
|
|
33
|
+
function fileIdForRow(collectionName, row) {
|
|
34
|
+
return collectionName.endsWith(".chunks") ? unwrapId(row.files_id) : unwrapId(row._id);
|
|
35
|
+
}
|
|
36
|
+
async function downloadFile(collectionName, fileId) {
|
|
37
|
+
const bucketName = bucketNameFor(collectionName);
|
|
38
|
+
downloadingFileIds.value.add(fileId);
|
|
39
|
+
try {
|
|
40
|
+
const info = await fileApi.getFileInformation({ bucketName, fileId });
|
|
41
|
+
const blob = await fileApi.getFile({ bucketName, fileId });
|
|
42
|
+
triggerBrowserDownload(blob, info.filename || fileId);
|
|
43
|
+
} catch {
|
|
44
|
+
toast.add({ title: "Download failed", description: "Could not download the file.", color: "error" });
|
|
45
|
+
} finally {
|
|
46
|
+
downloadingFileIds.value.delete(fileId);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function uploadFile(collectionName, file) {
|
|
50
|
+
const bucketName = bucketNameFor(collectionName);
|
|
51
|
+
uploading.value = true;
|
|
52
|
+
try {
|
|
53
|
+
await fileApi.insertFile({ bucketName, file, fileName: file.name, metaData: "{}" });
|
|
54
|
+
toast.add({ title: "File uploaded", description: `"${file.name}" was uploaded.`, color: "success" });
|
|
55
|
+
return true;
|
|
56
|
+
} catch {
|
|
57
|
+
toast.add({ title: "Upload failed", description: "Could not upload the file.", color: "error" });
|
|
58
|
+
return false;
|
|
59
|
+
} finally {
|
|
60
|
+
uploading.value = false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function listBuckets() {
|
|
64
|
+
return bucketApi.listBuckets();
|
|
65
|
+
}
|
|
66
|
+
async function getBucketInfo(bucketName) {
|
|
67
|
+
return bucketApi.getBucket({ bucketName });
|
|
68
|
+
}
|
|
69
|
+
async function clearBucket(bucketName) {
|
|
70
|
+
bucketActionInFlight.value.add(bucketName);
|
|
71
|
+
try {
|
|
72
|
+
const result = await bucketApi.clearBucket({ bucketName });
|
|
73
|
+
toast.add({ title: "Bucket cleared", description: `All files in "${bucketName}" were deleted.`, color: "success" });
|
|
74
|
+
return result.value;
|
|
75
|
+
} catch {
|
|
76
|
+
toast.add({ title: "Clear failed", description: `Could not clear bucket "${bucketName}".`, color: "error" });
|
|
77
|
+
return false;
|
|
78
|
+
} finally {
|
|
79
|
+
bucketActionInFlight.value.delete(bucketName);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function deleteBucket(bucketName) {
|
|
83
|
+
bucketActionInFlight.value.add(bucketName);
|
|
84
|
+
try {
|
|
85
|
+
const result = await bucketApi.deleteBucket({ bucketName });
|
|
86
|
+
toast.add({ title: "Bucket deleted", description: `Bucket "${bucketName}" was deleted.`, color: "success" });
|
|
87
|
+
return result.value;
|
|
88
|
+
} catch {
|
|
89
|
+
toast.add({ title: "Delete failed", description: `Could not delete bucket "${bucketName}".`, color: "error" });
|
|
90
|
+
return false;
|
|
91
|
+
} finally {
|
|
92
|
+
bucketActionInFlight.value.delete(bucketName);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
isBucketCollection,
|
|
97
|
+
bucketNameFor,
|
|
98
|
+
fileIdForRow,
|
|
99
|
+
downloadingFileIds,
|
|
100
|
+
uploading,
|
|
101
|
+
downloadFile,
|
|
102
|
+
uploadFile,
|
|
103
|
+
bucketActionInFlight,
|
|
104
|
+
listBuckets,
|
|
105
|
+
getBucketInfo,
|
|
106
|
+
clearBucket,
|
|
107
|
+
deleteBucket
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const _default: () => {
|
|
2
|
+
filter: import("vue").Ref<string | undefined, string | undefined>;
|
|
3
|
+
sort: import("vue").Ref<string[] | undefined, string[] | undefined>;
|
|
4
|
+
projection: import("vue").Ref<string[] | undefined, string[] | undefined>;
|
|
5
|
+
pagination: import("vue").Ref<{
|
|
6
|
+
pageIndex: number;
|
|
7
|
+
pageSize: number;
|
|
8
|
+
}, {
|
|
9
|
+
pageIndex: number;
|
|
10
|
+
pageSize: number;
|
|
11
|
+
} | {
|
|
12
|
+
pageIndex: number;
|
|
13
|
+
pageSize: number;
|
|
14
|
+
}>;
|
|
15
|
+
total: import("vue").Ref<number, number>;
|
|
16
|
+
};
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
export default () => {
|
|
3
|
+
const filter = ref();
|
|
4
|
+
const sort = ref();
|
|
5
|
+
const projection = ref();
|
|
6
|
+
const pagination = ref({
|
|
7
|
+
pageIndex: 1,
|
|
8
|
+
pageSize: 20
|
|
9
|
+
});
|
|
10
|
+
const total = ref(0);
|
|
11
|
+
return { filter, sort, projection, pagination, total };
|
|
12
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface MetaData {
|
|
2
|
+
createdBy: string;
|
|
3
|
+
updatedBy: string;
|
|
4
|
+
created: string | Date;
|
|
5
|
+
updated: string | Date;
|
|
6
|
+
}
|
|
7
|
+
declare const _default: () => {
|
|
8
|
+
ensureMetaData: <T extends {
|
|
9
|
+
metaData?: Partial<MetaData>;
|
|
10
|
+
}>(data: T) => T;
|
|
11
|
+
updateFromPartial: <T>(obj: T, updates: Partial<T>) => T;
|
|
12
|
+
};
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useMongocampStorage } from "#imports";
|
|
2
|
+
export default () => {
|
|
3
|
+
function ensureMetaData(data) {
|
|
4
|
+
const storage = useMongocampStorage();
|
|
5
|
+
const userId = storage.value?.profile.user ?? "";
|
|
6
|
+
const now = /* @__PURE__ */ new Date();
|
|
7
|
+
if (!data.metaData || !data.metaData.createdBy) {
|
|
8
|
+
data.metaData = { createdBy: userId, updatedBy: userId, created: now, updated: now };
|
|
9
|
+
} else {
|
|
10
|
+
data.metaData = { createdBy: data.metaData.createdBy, updatedBy: userId, created: data.metaData.created ?? now, updated: now };
|
|
11
|
+
}
|
|
12
|
+
return data;
|
|
13
|
+
}
|
|
14
|
+
function updateFromPartial(obj, updates) {
|
|
15
|
+
return { ...obj, ...updates };
|
|
16
|
+
}
|
|
17
|
+
return { ensureMetaData, updateFromPartial };
|
|
18
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ColumnDefinition } from './useMongocampSchema.js';
|
|
2
|
+
export interface FormKitSchemaNode {
|
|
3
|
+
$formkit?: string;
|
|
4
|
+
$el?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
validation?: string;
|
|
8
|
+
attrs?: Record<string, unknown>;
|
|
9
|
+
children?: FormKitSchemaNode[];
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export declare function columnsToFormKitSchema(columns: ColumnDefinition[]): FormKitSchemaNode[];
|
|
13
|
+
export declare function documentToFormData(doc: Record<string, unknown>, columns: ColumnDefinition[]): Record<string, unknown>;
|
|
14
|
+
export declare function formDataToDocument(formData: Record<string, unknown>, columns: ColumnDefinition[], originalDoc?: Record<string, unknown>): Record<string, unknown>;
|