@abraca/nuxt 2.4.0 → 2.5.1
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/README.md +25 -6
- package/dist/module.d.mts +4 -2
- package/dist/module.json +1 -1
- package/dist/runtime/components/ACodeEditor.d.vue.ts +1 -1
- package/dist/runtime/components/ACodeEditor.vue.d.ts +1 -1
- package/dist/runtime/components/AConnectServerModal.d.vue.ts +2 -2
- package/dist/runtime/components/AConnectServerModal.vue +1 -1
- package/dist/runtime/components/AConnectServerModal.vue.d.ts +2 -2
- package/dist/runtime/components/ADocumentTree.d.vue.ts +8 -1
- package/dist/runtime/components/ADocumentTree.vue +6 -3
- package/dist/runtime/components/ADocumentTree.vue.d.ts +8 -1
- package/dist/runtime/components/AEditor.d.vue.ts +7 -7
- package/dist/runtime/components/AEditor.vue.d.ts +7 -7
- package/dist/runtime/components/AEmailVerifyConfirmModal.d.vue.ts +2 -2
- package/dist/runtime/components/AEmailVerifyConfirmModal.vue.d.ts +2 -2
- package/dist/runtime/components/AIdentityModal.d.vue.ts +2 -2
- package/dist/runtime/components/AIdentityModal.vue.d.ts +2 -2
- package/dist/runtime/components/AInviteRedeemModal.vue +15 -11
- package/dist/runtime/components/AMnemonicLoginModal.d.vue.ts +2 -2
- package/dist/runtime/components/AMnemonicLoginModal.vue.d.ts +2 -2
- package/dist/runtime/components/APasswordChangeModal.d.vue.ts +3 -3
- package/dist/runtime/components/APasswordChangeModal.vue.d.ts +3 -3
- package/dist/runtime/components/APasswordLoginModal.d.vue.ts +4 -4
- package/dist/runtime/components/APasswordLoginModal.vue.d.ts +4 -4
- package/dist/runtime/components/APasswordRegisterModal.d.vue.ts +3 -3
- package/dist/runtime/components/APasswordRegisterModal.vue.d.ts +3 -3
- package/dist/runtime/components/APasswordResetConfirmModal.d.vue.ts +3 -3
- package/dist/runtime/components/APasswordResetConfirmModal.vue.d.ts +3 -3
- package/dist/runtime/components/APasswordResetRequestModal.d.vue.ts +2 -2
- package/dist/runtime/components/APasswordResetRequestModal.vue.d.ts +2 -2
- package/dist/runtime/components/ASetPasswordCard.d.vue.ts +1 -1
- package/dist/runtime/components/ASetPasswordCard.vue.d.ts +1 -1
- package/dist/runtime/components/aware/AArea.d.vue.ts +1 -1
- package/dist/runtime/components/aware/AArea.vue.d.ts +1 -1
- package/dist/runtime/components/aware/ACalendar.d.vue.ts +1 -1
- package/dist/runtime/components/aware/ACalendar.vue.d.ts +1 -1
- package/dist/runtime/components/aware/AFileUpload.d.vue.ts +1 -1
- package/dist/runtime/components/aware/AFileUpload.vue.d.ts +1 -1
- package/dist/runtime/components/aware/AInput.d.vue.ts +1 -1
- package/dist/runtime/components/aware/AInput.vue.d.ts +1 -1
- package/dist/runtime/components/aware/AMedia.d.vue.ts +1 -1
- package/dist/runtime/components/aware/AMedia.vue.d.ts +1 -1
- package/dist/runtime/components/aware/AScroll.d.vue.ts +1 -1
- package/dist/runtime/components/aware/AScroll.vue.d.ts +1 -1
- package/dist/runtime/components/aware/ASlider.d.vue.ts +1 -1
- package/dist/runtime/components/aware/ASlider.vue.d.ts +1 -1
- package/dist/runtime/components/aware/ATable.d.vue.ts +1 -1
- package/dist/runtime/components/aware/ATable.vue.d.ts +1 -1
- package/dist/runtime/components/aware/ATabs.d.vue.ts +1 -1
- package/dist/runtime/components/aware/ATabs.vue.d.ts +1 -1
- package/dist/runtime/components/aware/ATextarea.d.vue.ts +1 -1
- package/dist/runtime/components/aware/ATextarea.vue.d.ts +1 -1
- package/dist/runtime/components/aware/ATree.d.vue.ts +1 -1
- package/dist/runtime/components/aware/ATree.vue.d.ts +1 -1
- package/dist/runtime/components/docs/ADocsNavigation.d.vue.ts +1 -1
- package/dist/runtime/components/docs/ADocsNavigation.vue.d.ts +1 -1
- package/dist/runtime/components/docs/ADocsSearch.d.vue.ts +4 -4
- package/dist/runtime/components/docs/ADocsSearch.vue.d.ts +4 -4
- package/dist/runtime/components/docs/ADocsSearchButton.d.vue.ts +2 -2
- package/dist/runtime/components/docs/ADocsSearchButton.vue.d.ts +2 -2
- package/dist/runtime/components/docs/ADocsToc.d.vue.ts +1 -1
- package/dist/runtime/components/docs/ADocsToc.vue.d.ts +1 -1
- package/dist/runtime/components/editor/AColorPalettePopover.d.vue.ts +2 -2
- package/dist/runtime/components/editor/AColorPalettePopover.vue.d.ts +2 -2
- package/dist/runtime/components/editor/AIconPickerPopover.d.vue.ts +2 -2
- package/dist/runtime/components/editor/AIconPickerPopover.vue.d.ts +2 -2
- package/dist/runtime/components/editor/ALocationPickerPopover.d.vue.ts +2 -2
- package/dist/runtime/components/editor/ALocationPickerPopover.vue.d.ts +2 -2
- package/dist/runtime/components/registry/APluginCapabilityDialog.d.vue.ts +4 -4
- package/dist/runtime/components/registry/APluginCapabilityDialog.vue +1 -1
- package/dist/runtime/components/registry/APluginCapabilityDialog.vue.d.ts +4 -4
- package/dist/runtime/components/renderers/AProseRenderer.d.vue.ts +4 -4
- package/dist/runtime/components/renderers/AProseRenderer.vue.d.ts +4 -4
- package/dist/runtime/components/renderers/sheets/ASheetsCell.d.vue.ts +4 -4
- package/dist/runtime/components/renderers/sheets/ASheetsCell.vue.d.ts +4 -4
- package/dist/runtime/components/renderers/sheets/ASheetsGrid.d.vue.ts +2 -2
- package/dist/runtime/components/renderers/sheets/ASheetsGrid.vue.d.ts +2 -2
- package/dist/runtime/components/renderers/sheets/ASheetsMobileActionBar.d.vue.ts +6 -6
- package/dist/runtime/components/renderers/sheets/ASheetsMobileActionBar.vue.d.ts +6 -6
- package/dist/runtime/components/renderers/sheets/ASheetsToolbar.d.vue.ts +4 -4
- package/dist/runtime/components/renderers/sheets/ASheetsToolbar.vue.d.ts +4 -4
- package/dist/runtime/components/shell/ADocPanelSettings.d.vue.ts +1 -1
- package/dist/runtime/components/shell/ADocPanelSettings.vue.d.ts +1 -1
- package/dist/runtime/components/shell/AUserProfilePopover.d.vue.ts +1 -1
- package/dist/runtime/components/shell/AUserProfilePopover.vue.d.ts +1 -1
- package/dist/runtime/components/shell/AWelcomeScreen.d.vue.ts +4 -4
- package/dist/runtime/components/shell/AWelcomeScreen.vue +77 -58
- package/dist/runtime/components/shell/AWelcomeScreen.vue.d.ts +4 -4
- package/dist/runtime/composables/useAbraAdmin.d.ts +131 -4
- package/dist/runtime/composables/useAbraAdmin.js +136 -36
- package/dist/runtime/composables/useInvites.d.ts +7 -0
- package/dist/runtime/plugin-abracadabra.client.js +44 -3
- package/dist/runtime/server/api/_abracadabra/spaces.get.d.ts +1 -1
- package/dist/runtime/server/plugins/abracadabra-service.js +1 -0
- package/dist/runtime/types.d.ts +2 -0
- package/dist/types.d.mts +6 -2
- package/package.json +20 -20
|
@@ -4,7 +4,25 @@ const open = defineModel("open", { type: Boolean, ...{ default: true } });
|
|
|
4
4
|
const props = defineProps({
|
|
5
5
|
userName: { type: String, required: false, default: "" },
|
|
6
6
|
avatarStyle: { type: String, required: false, default: "" },
|
|
7
|
-
colors: { type: Array, required: false, default: () => [
|
|
7
|
+
colors: { type: Array, required: false, default: () => [
|
|
8
|
+
"red",
|
|
9
|
+
"orange",
|
|
10
|
+
"amber",
|
|
11
|
+
"yellow",
|
|
12
|
+
"lime",
|
|
13
|
+
"green",
|
|
14
|
+
"emerald",
|
|
15
|
+
"teal",
|
|
16
|
+
"cyan",
|
|
17
|
+
"sky",
|
|
18
|
+
"blue",
|
|
19
|
+
"indigo",
|
|
20
|
+
"violet",
|
|
21
|
+
"purple",
|
|
22
|
+
"fuchsia",
|
|
23
|
+
"pink",
|
|
24
|
+
"rose"
|
|
25
|
+
] },
|
|
8
26
|
neutrals: { type: Array, required: false, default: () => ["slate", "gray", "zinc", "neutral", "stone"] },
|
|
9
27
|
currentColor: { type: String, required: false, default: "blue" },
|
|
10
28
|
currentNeutral: { type: String, required: false, default: "zinc" },
|
|
@@ -49,11 +67,14 @@ function connectToServer() {
|
|
|
49
67
|
if (!raw) return;
|
|
50
68
|
emit("connect-server", raw);
|
|
51
69
|
}
|
|
52
|
-
watch(
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
watch(
|
|
71
|
+
() => props.connecting,
|
|
72
|
+
(val, prev) => {
|
|
73
|
+
if (prev && !val && !props.connectionError && step.value === "server") {
|
|
74
|
+
step.value = "done";
|
|
75
|
+
}
|
|
55
76
|
}
|
|
56
|
-
|
|
77
|
+
);
|
|
57
78
|
</script>
|
|
58
79
|
|
|
59
80
|
<template>
|
|
@@ -74,10 +95,7 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
74
95
|
>
|
|
75
96
|
<template #body>
|
|
76
97
|
<div class="flex flex-col gap-6">
|
|
77
|
-
<Transition
|
|
78
|
-
name="step-fade"
|
|
79
|
-
mode="out-in"
|
|
80
|
-
>
|
|
98
|
+
<Transition name="step-fade" mode="out-in">
|
|
81
99
|
<!-- ── Step 1: Identity ────────────────────────────── -->
|
|
82
100
|
<div
|
|
83
101
|
v-if="step === 'identity'"
|
|
@@ -93,10 +111,10 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
93
111
|
/>
|
|
94
112
|
</slot>
|
|
95
113
|
<div class="flex flex-col items-center gap-1">
|
|
96
|
-
<h2
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
</slot>
|
|
114
|
+
<h2
|
|
115
|
+
class="text-xl font-bold text-(--ui-text-highlighted) tracking-tight"
|
|
116
|
+
>
|
|
117
|
+
<slot name="title"> Welcome </slot>
|
|
100
118
|
</h2>
|
|
101
119
|
<p class="text-sm text-(--ui-text-muted)">
|
|
102
120
|
<slot name="subtitle">
|
|
@@ -126,18 +144,18 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
126
144
|
|
|
127
145
|
<!-- Color picker -->
|
|
128
146
|
<div class="flex flex-col items-center gap-2">
|
|
129
|
-
<p
|
|
147
|
+
<p
|
|
148
|
+
class="text-xs font-medium text-(--ui-text-muted) uppercase tracking-wide"
|
|
149
|
+
>
|
|
130
150
|
Choose your color
|
|
131
151
|
</p>
|
|
132
152
|
<div class="flex flex-wrap justify-center gap-1.5 max-w-78">
|
|
133
|
-
<UTooltip
|
|
134
|
-
v-for="color in colors"
|
|
135
|
-
:key="color"
|
|
136
|
-
:text="color"
|
|
137
|
-
>
|
|
153
|
+
<UTooltip v-for="color in colors" :key="color" :text="color">
|
|
138
154
|
<button
|
|
139
155
|
class="w-5 h-5 rounded-full ring-2 ring-offset-1 ring-offset-(--ui-bg) transition-all duration-150 hover:scale-110 focus:outline-none"
|
|
140
|
-
:class="
|
|
156
|
+
:class="
|
|
157
|
+
currentColor === color ? 'ring-current scale-110' : 'ring-transparent'
|
|
158
|
+
"
|
|
141
159
|
:style="{ background: `var(--color-${color}-400)` }"
|
|
142
160
|
@click="emit('set-color', color)"
|
|
143
161
|
/>
|
|
@@ -147,18 +165,18 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
147
165
|
|
|
148
166
|
<!-- Neutral color picker -->
|
|
149
167
|
<div class="flex flex-col items-center gap-2">
|
|
150
|
-
<p
|
|
168
|
+
<p
|
|
169
|
+
class="text-xs font-medium text-(--ui-text-muted) uppercase tracking-wide"
|
|
170
|
+
>
|
|
151
171
|
Choose your neutral
|
|
152
172
|
</p>
|
|
153
173
|
<div class="flex flex-wrap justify-center gap-1.5 max-w-78">
|
|
154
|
-
<UTooltip
|
|
155
|
-
v-for="color in neutrals"
|
|
156
|
-
:key="color"
|
|
157
|
-
:text="color"
|
|
158
|
-
>
|
|
174
|
+
<UTooltip v-for="color in neutrals" :key="color" :text="color">
|
|
159
175
|
<button
|
|
160
176
|
class="w-5 h-5 rounded-full ring-2 ring-offset-1 ring-offset-(--ui-bg) transition-all duration-150 hover:scale-110 focus:outline-none"
|
|
161
|
-
:class="
|
|
177
|
+
:class="
|
|
178
|
+
currentNeutral === color ? 'ring-current scale-110' : 'ring-transparent'
|
|
179
|
+
"
|
|
162
180
|
:style="{ background: `var(--color-${color}-400)` }"
|
|
163
181
|
@click="emit('set-neutral', color)"
|
|
164
182
|
/>
|
|
@@ -188,13 +206,11 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
188
206
|
class="flex flex-col gap-6"
|
|
189
207
|
>
|
|
190
208
|
<div class="flex flex-col items-center text-center gap-4 pt-4">
|
|
191
|
-
<UAvatar
|
|
192
|
-
:alt="displayName"
|
|
193
|
-
size="lg"
|
|
194
|
-
:style="avatarStyle"
|
|
195
|
-
/>
|
|
209
|
+
<UAvatar :alt="displayName" size="lg" :style="avatarStyle" />
|
|
196
210
|
<div class="flex flex-col items-center gap-1">
|
|
197
|
-
<h2
|
|
211
|
+
<h2
|
|
212
|
+
class="text-xl font-bold text-(--ui-text-highlighted) tracking-tight"
|
|
213
|
+
>
|
|
198
214
|
How would you like to get started?
|
|
199
215
|
</h2>
|
|
200
216
|
<p class="text-sm text-(--ui-text-muted)">
|
|
@@ -213,8 +229,13 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
213
229
|
class="size-5 text-(--ui-text-muted) group-hover:text-(--color-primary-500) transition-colors"
|
|
214
230
|
/>
|
|
215
231
|
<div class="flex flex-col gap-0.5">
|
|
216
|
-
<span
|
|
217
|
-
|
|
232
|
+
<span
|
|
233
|
+
class="text-xs font-semibold text-(--ui-text-highlighted) leading-tight"
|
|
234
|
+
>Start locally</span
|
|
235
|
+
>
|
|
236
|
+
<span class="text-[11px] text-(--ui-text-muted) leading-snug"
|
|
237
|
+
>No server needed</span
|
|
238
|
+
>
|
|
218
239
|
</div>
|
|
219
240
|
</button>
|
|
220
241
|
|
|
@@ -227,12 +248,17 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
227
248
|
@click="step = 'server'"
|
|
228
249
|
>
|
|
229
250
|
<UIcon
|
|
230
|
-
name="i-lucide-
|
|
251
|
+
name="i-lucide-hexagon"
|
|
231
252
|
class="size-5 text-(--ui-text-muted) group-hover:text-(--color-primary-500) transition-colors"
|
|
232
253
|
/>
|
|
233
254
|
<div class="flex flex-col gap-0.5">
|
|
234
|
-
<span
|
|
235
|
-
|
|
255
|
+
<span
|
|
256
|
+
class="text-xs font-semibold text-(--ui-text-highlighted) leading-tight"
|
|
257
|
+
>Self-hosted</span
|
|
258
|
+
>
|
|
259
|
+
<span class="text-[11px] text-(--ui-text-muted) leading-snug"
|
|
260
|
+
>Your infrastructure</span
|
|
261
|
+
>
|
|
236
262
|
</div>
|
|
237
263
|
</button>
|
|
238
264
|
</div>
|
|
@@ -254,7 +280,9 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
254
280
|
/>
|
|
255
281
|
</slot>
|
|
256
282
|
<div class="flex flex-col items-center gap-1">
|
|
257
|
-
<h2
|
|
283
|
+
<h2
|
|
284
|
+
class="text-xl font-bold text-(--ui-text-highlighted) tracking-tight"
|
|
285
|
+
>
|
|
258
286
|
Connect to server
|
|
259
287
|
</h2>
|
|
260
288
|
<p class="text-sm text-(--ui-text-muted)">
|
|
@@ -307,12 +335,10 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
307
335
|
<!-- Already claimed or just claimed -->
|
|
308
336
|
<template v-if="isClaimed || claimSuccess">
|
|
309
337
|
<div class="relative">
|
|
310
|
-
<UAvatar
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
/>
|
|
315
|
-
<div class="absolute -bottom-1 -right-1 rounded-full p-0.5 bg-(--ui-color-success-500)">
|
|
338
|
+
<UAvatar :alt="displayName" size="2xl" :style="avatarStyle" />
|
|
339
|
+
<div
|
|
340
|
+
class="absolute -bottom-1 -right-1 rounded-full p-0.5 bg-(--ui-color-success-500)"
|
|
341
|
+
>
|
|
316
342
|
<UIcon
|
|
317
343
|
name="i-lucide-shield-check"
|
|
318
344
|
class="text-white size-4"
|
|
@@ -328,9 +354,7 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
328
354
|
<template v-if="claimSuccess">
|
|
329
355
|
Your identity is now hardware-bound, {{ displayName }}
|
|
330
356
|
</template>
|
|
331
|
-
<template v-else>
|
|
332
|
-
Welcome back, {{ displayName }}
|
|
333
|
-
</template>
|
|
357
|
+
<template v-else> Welcome back, {{ displayName }} </template>
|
|
334
358
|
</p>
|
|
335
359
|
</div>
|
|
336
360
|
|
|
@@ -347,16 +371,11 @@ watch(() => props.connecting, (val, prev) => {
|
|
|
347
371
|
<!-- Default: offer passkey -->
|
|
348
372
|
<template v-else>
|
|
349
373
|
<div class="relative">
|
|
350
|
-
<UAvatar
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
<div class="absolute -bottom-1 -right-1 rounded-full p-0.5 bg-(--ui-color-success-500)">
|
|
356
|
-
<UIcon
|
|
357
|
-
name="i-lucide-check"
|
|
358
|
-
class="text-white size-4"
|
|
359
|
-
/>
|
|
374
|
+
<UAvatar :alt="displayName" size="2xl" :style="avatarStyle" />
|
|
375
|
+
<div
|
|
376
|
+
class="absolute -bottom-1 -right-1 rounded-full p-0.5 bg-(--ui-color-success-500)"
|
|
377
|
+
>
|
|
378
|
+
<UIcon name="i-lucide-check" class="text-white size-4" />
|
|
360
379
|
</div>
|
|
361
380
|
</div>
|
|
362
381
|
|
|
@@ -35,7 +35,7 @@ type __VLS_ModelProps = {
|
|
|
35
35
|
* Ported from cou-sh/app/components/WelcomeScreen.vue — decoupled from
|
|
36
36
|
* useAbracadabra, useSpaceRegistry, and all app-specific modals.
|
|
37
37
|
*/
|
|
38
|
-
|
|
38
|
+
"open"?: boolean;
|
|
39
39
|
};
|
|
40
40
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
41
41
|
declare var __VLS_15: {}, __VLS_22: {}, __VLS_24: {}, __VLS_57: {}, __VLS_69: {}, __VLS_76: {}, __VLS_78: {};
|
|
@@ -55,8 +55,8 @@ type __VLS_Slots = {} & {
|
|
|
55
55
|
logo?: (props: typeof __VLS_78) => any;
|
|
56
56
|
};
|
|
57
57
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
58
|
-
done: () => any;
|
|
59
58
|
"update:open": (value: boolean) => any;
|
|
59
|
+
done: () => any;
|
|
60
60
|
"set-name": (name: string) => any;
|
|
61
61
|
"set-color": (color: string) => any;
|
|
62
62
|
"set-neutral": (color: string) => any;
|
|
@@ -64,8 +64,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
64
64
|
"connect-server": (url: string) => any;
|
|
65
65
|
"claim-passkey": () => any;
|
|
66
66
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
67
|
-
onDone?: (() => any) | undefined;
|
|
68
67
|
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
68
|
+
onDone?: (() => any) | undefined;
|
|
69
69
|
"onSet-name"?: ((name: string) => any) | undefined;
|
|
70
70
|
"onSet-color"?: ((color: string) => any) | undefined;
|
|
71
71
|
"onSet-neutral"?: ((color: string) => any) | undefined;
|
|
@@ -74,9 +74,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
74
74
|
"onClaim-passkey"?: (() => any) | undefined;
|
|
75
75
|
}>, {
|
|
76
76
|
connecting: boolean;
|
|
77
|
+
isClaimed: boolean;
|
|
77
78
|
userName: string;
|
|
78
79
|
currentColor: string;
|
|
79
|
-
isClaimed: boolean;
|
|
80
80
|
avatarStyle: string;
|
|
81
81
|
connectionError: string;
|
|
82
82
|
colors: string[];
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useAbraAdmin
|
|
3
3
|
*
|
|
4
|
-
* Admin-only composable.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Admin-only composable. Typed facade over the full `/admin/*` REST
|
|
5
|
+
* surface (users, audit, storage, uploads, runtime config, backup).
|
|
6
|
+
* Every method rejects with the server error when the caller lacks an
|
|
7
|
+
* elevated role — UI should still gate route entry on `isAdmin`.
|
|
8
|
+
*
|
|
9
|
+
* This is the single typed entry point for admin tooling (e.g.
|
|
10
|
+
* abracadabra-arcana). Consumers must never reach for
|
|
11
|
+
* `client as any` — if a method is missing here, add it here.
|
|
7
12
|
*
|
|
8
13
|
* Usage:
|
|
9
14
|
* const admin = useAbraAdmin()
|
|
@@ -11,11 +16,95 @@
|
|
|
11
16
|
* const rows = await admin.auditList({ event_type: 'auth.login_failed' })
|
|
12
17
|
* }
|
|
13
18
|
*/
|
|
14
|
-
import type { AuditLogEntry, AuditQueryOpts, AuditVerifyResult } from '@abraca/dabra';
|
|
19
|
+
import type { AdminConfigField, AuditLogEntry, AuditQueryOpts, AuditVerifyResult, EnvSnapshotResponse, UserProfile } from '@abraca/dabra';
|
|
20
|
+
/** One row from `GET /admin/users` — a profile plus admin-only fields. */
|
|
21
|
+
export type AdminUserRow = UserProfile & {
|
|
22
|
+
revoked: boolean;
|
|
23
|
+
deviceKeys: string[];
|
|
24
|
+
};
|
|
25
|
+
/** One row from `GET /admin/uploads` (uploads ⨝ documents ⨝ file_blobs). */
|
|
26
|
+
export interface AdminUploadRow {
|
|
27
|
+
id: string;
|
|
28
|
+
doc_id: string;
|
|
29
|
+
doc_label: string | null;
|
|
30
|
+
filename: string;
|
|
31
|
+
mime_type: string | null;
|
|
32
|
+
size: number | null;
|
|
33
|
+
content_hash: string | null;
|
|
34
|
+
/** Number of uploads pointing at the same content-addressed blob. */
|
|
35
|
+
ref_count: number | null;
|
|
36
|
+
owner_id: string;
|
|
37
|
+
created_at: number;
|
|
38
|
+
}
|
|
39
|
+
/** `GET /admin/uploads` response. */
|
|
40
|
+
export interface AdminUploadList {
|
|
41
|
+
uploads: AdminUploadRow[];
|
|
42
|
+
total: number;
|
|
43
|
+
}
|
|
44
|
+
/** One document a user owns or has an explicit grant on
|
|
45
|
+
* (`GET /admin/users/:id/docs`). Label is best-effort (CRDT-resident). */
|
|
46
|
+
export interface AdminUserDocRow {
|
|
47
|
+
id: string;
|
|
48
|
+
label: string | null;
|
|
49
|
+
kind: string | null;
|
|
50
|
+
doc_type: string | null;
|
|
51
|
+
parent_id: string | null;
|
|
52
|
+
source: 'owner' | 'grant';
|
|
53
|
+
role: string | null;
|
|
54
|
+
}
|
|
55
|
+
/** `GET /admin/storage/stats` — aggregate storage figures. */
|
|
56
|
+
export interface AdminStorageStats {
|
|
57
|
+
uploadCount: number;
|
|
58
|
+
blobCount: number;
|
|
59
|
+
/** Sum of upload sizes as users see them. */
|
|
60
|
+
logicalBytes: number;
|
|
61
|
+
/** On-disk after content-addressed dedup. */
|
|
62
|
+
physicalBytes: number;
|
|
63
|
+
/** `logicalBytes - physicalBytes`. */
|
|
64
|
+
dedupSaved: number;
|
|
65
|
+
}
|
|
15
66
|
declare function auditList(opts?: AuditQueryOpts): Promise<AuditLogEntry[]>;
|
|
16
67
|
declare function auditExport(opts?: Omit<AuditQueryOpts, 'limit' | 'offset'>): Promise<string>;
|
|
17
68
|
declare function auditVerify(): Promise<AuditVerifyResult>;
|
|
69
|
+
declare function listUsers(): Promise<AdminUserRow[]>;
|
|
70
|
+
declare function promoteUser(userId: string): Promise<void>;
|
|
71
|
+
declare function demoteUser(userId: string): Promise<void>;
|
|
18
72
|
declare function unlockUser(userId: string): Promise<void>;
|
|
73
|
+
declare function userDocs(userId: string, opts?: {
|
|
74
|
+
limit?: number;
|
|
75
|
+
}): Promise<AdminUserDocRow[]>;
|
|
76
|
+
declare function listUploads(opts?: {
|
|
77
|
+
q?: string;
|
|
78
|
+
docId?: string;
|
|
79
|
+
limit?: number;
|
|
80
|
+
offset?: number;
|
|
81
|
+
}): Promise<AdminUploadList>;
|
|
82
|
+
declare function storageStats(): Promise<AdminStorageStats>;
|
|
83
|
+
declare function storageSweep(): Promise<{
|
|
84
|
+
blobsDeleted: number;
|
|
85
|
+
}>;
|
|
86
|
+
declare function storageRepair(): Promise<{
|
|
87
|
+
refCountsRepaired: number;
|
|
88
|
+
blobsSwept: number;
|
|
89
|
+
}>;
|
|
90
|
+
declare function snapshotsBackfillRefs(): Promise<{
|
|
91
|
+
snapshotsScanned: number;
|
|
92
|
+
refsWritten: number;
|
|
93
|
+
}>;
|
|
94
|
+
declare function snapshotsBackfillBlobs(): Promise<{
|
|
95
|
+
snapshotsMigrated: number;
|
|
96
|
+
blobsAfter: number;
|
|
97
|
+
totalBytesAfter: number;
|
|
98
|
+
}>;
|
|
99
|
+
declare function configList(): Promise<AdminConfigField[]>;
|
|
100
|
+
declare function configGet(path: string): Promise<AdminConfigField>;
|
|
101
|
+
declare function configSet(path: string, value: unknown): Promise<AdminConfigField>;
|
|
102
|
+
declare function configUnset(path: string): Promise<boolean>;
|
|
103
|
+
declare function configEnvSnapshot(): Promise<EnvSnapshotResponse>;
|
|
104
|
+
declare function configListRoutes(): Promise<string[]>;
|
|
105
|
+
declare function configGetRoute(route: string, path: string): Promise<AdminConfigField>;
|
|
106
|
+
declare function configSetRoute(route: string, path: string, value: unknown): Promise<AdminConfigField>;
|
|
107
|
+
declare function configUnsetRoute(route: string, path: string): Promise<boolean>;
|
|
19
108
|
declare function backupDump(opts?: {
|
|
20
109
|
includeAudit?: boolean;
|
|
21
110
|
}): Promise<Blob>;
|
|
@@ -44,8 +133,46 @@ export declare function useAbraAdmin(): {
|
|
|
44
133
|
auditExport: typeof auditExport;
|
|
45
134
|
/** GET /admin/audit/verify — returns the result whether ok or broken. */
|
|
46
135
|
auditVerify: typeof auditVerify;
|
|
136
|
+
/** GET /admin/users — profiles + revoked + device keys + effective role. */
|
|
137
|
+
listUsers: typeof listUsers;
|
|
138
|
+
/** POST /admin/users/:id/admin (service identity only). */
|
|
139
|
+
promoteUser: typeof promoteUser;
|
|
140
|
+
/** DELETE /admin/users/:id/admin (service identity only). */
|
|
141
|
+
demoteUser: typeof demoteUser;
|
|
47
142
|
/** POST /admin/users/:id/unlock. */
|
|
48
143
|
unlockUser: typeof unlockUser;
|
|
144
|
+
/** GET /admin/users/:id/docs — owned + explicitly-granted docs. */
|
|
145
|
+
userDocs: typeof userDocs;
|
|
146
|
+
/** GET /admin/uploads — paginated/filterable, with dedup ref counts. */
|
|
147
|
+
listUploads: typeof listUploads;
|
|
148
|
+
/** GET /admin/storage/stats. */
|
|
149
|
+
storageStats: typeof storageStats;
|
|
150
|
+
/** POST /admin/storage/sweep — GC orphaned blobs. */
|
|
151
|
+
storageSweep: typeof storageSweep;
|
|
152
|
+
/** POST /admin/storage/repair — repair ref-counts + sweep. */
|
|
153
|
+
storageRepair: typeof storageRepair;
|
|
154
|
+
/** POST /admin/snapshots/backfill-refs — populate snapshot_files. */
|
|
155
|
+
snapshotsBackfillRefs: typeof snapshotsBackfillRefs;
|
|
156
|
+
/** POST /admin/snapshots/backfill-blobs — migrate to dedup store. */
|
|
157
|
+
snapshotsBackfillBlobs: typeof snapshotsBackfillBlobs;
|
|
158
|
+
/** GET /admin/config. */
|
|
159
|
+
configList: typeof configList;
|
|
160
|
+
/** GET /admin/config/fields/:path. */
|
|
161
|
+
configGet: typeof configGet;
|
|
162
|
+
/** PUT /admin/config/fields/:path. */
|
|
163
|
+
configSet: typeof configSet;
|
|
164
|
+
/** DELETE /admin/config/fields/:path — revert to base. */
|
|
165
|
+
configUnset: typeof configUnset;
|
|
166
|
+
/** GET /admin/config/env-snapshot. */
|
|
167
|
+
configEnvSnapshot: typeof configEnvSnapshot;
|
|
168
|
+
/** GET /admin/config/routes — routes with ≥1 override. */
|
|
169
|
+
configListRoutes: typeof configListRoutes;
|
|
170
|
+
/** GET /admin/config/routes/:route/fields/:path. */
|
|
171
|
+
configGetRoute: typeof configGetRoute;
|
|
172
|
+
/** PUT /admin/config/routes/:route/fields/:path. */
|
|
173
|
+
configSetRoute: typeof configSetRoute;
|
|
174
|
+
/** DELETE /admin/config/routes/:route/fields/:path. */
|
|
175
|
+
configUnsetRoute: typeof configUnsetRoute;
|
|
49
176
|
/** GET /admin/backup/dump — returns the tar Blob. */
|
|
50
177
|
backupDump: typeof backupDump;
|
|
51
178
|
/** Convenience: backupDump + browser download. */
|
|
@@ -13,62 +13,119 @@ function _client() {
|
|
|
13
13
|
function _isAdminRole(role) {
|
|
14
14
|
return role === "admin" || role === "service" || role === "owner";
|
|
15
15
|
}
|
|
16
|
-
async function
|
|
17
|
-
const { client } = _client();
|
|
16
|
+
async function _run(label, fn) {
|
|
18
17
|
isLoading.value = true;
|
|
19
18
|
error.value = null;
|
|
20
19
|
try {
|
|
21
|
-
|
|
22
|
-
auditEntries.value = rows;
|
|
23
|
-
return rows;
|
|
20
|
+
return await fn();
|
|
24
21
|
} catch (e) {
|
|
25
|
-
error.value = e instanceof Error ? e.message :
|
|
22
|
+
error.value = e instanceof Error ? e.message : label;
|
|
26
23
|
throw e;
|
|
27
24
|
} finally {
|
|
28
25
|
isLoading.value = false;
|
|
29
26
|
}
|
|
30
27
|
}
|
|
28
|
+
async function auditList(opts) {
|
|
29
|
+
const { client } = _client();
|
|
30
|
+
const rows = await _run("Failed to load audit log", () => client.adminAuditList(opts));
|
|
31
|
+
auditEntries.value = rows;
|
|
32
|
+
return rows;
|
|
33
|
+
}
|
|
31
34
|
async function auditExport(opts) {
|
|
32
35
|
const { client } = _client();
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
return await client.adminAuditExport(opts);
|
|
36
|
-
} catch (e) {
|
|
37
|
-
error.value = e instanceof Error ? e.message : "Failed to export audit log";
|
|
38
|
-
throw e;
|
|
39
|
-
}
|
|
36
|
+
return _run("Failed to export audit log", () => client.adminAuditExport(opts));
|
|
40
37
|
}
|
|
41
38
|
async function auditVerify() {
|
|
42
39
|
const { client } = _client();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
const result = await _run("Audit verification failed", () => client.adminAuditVerify());
|
|
41
|
+
auditVerifyResult.value = result;
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
async function listUsers() {
|
|
45
|
+
const { client } = _client();
|
|
46
|
+
const res = await _run("Failed to load users", () => client.adminListUsers());
|
|
47
|
+
return res?.users ?? [];
|
|
48
|
+
}
|
|
49
|
+
async function promoteUser(userId) {
|
|
50
|
+
const { client } = _client();
|
|
51
|
+
await _run("Failed to promote user", () => client.adminPromote(userId));
|
|
52
|
+
}
|
|
53
|
+
async function demoteUser(userId) {
|
|
54
|
+
const { client } = _client();
|
|
55
|
+
await _run("Failed to demote user", () => client.adminDemote(userId));
|
|
52
56
|
}
|
|
53
57
|
async function unlockUser(userId) {
|
|
54
58
|
const { client } = _client();
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
await _run("Failed to unlock user", () => client.adminUnlockUser(userId));
|
|
60
|
+
}
|
|
61
|
+
async function userDocs(userId, opts = {}) {
|
|
62
|
+
const { client } = _client();
|
|
63
|
+
const res = await _run("Failed to load user documents", () => client.adminUserDocs(userId, opts));
|
|
64
|
+
return res?.docs ?? [];
|
|
65
|
+
}
|
|
66
|
+
async function listUploads(opts = {}) {
|
|
67
|
+
const { client } = _client();
|
|
68
|
+
return _run("Failed to load uploads", () => client.adminListUploads(opts));
|
|
69
|
+
}
|
|
70
|
+
async function storageStats() {
|
|
71
|
+
const { client } = _client();
|
|
72
|
+
return _run("Failed to load storage stats", () => client.adminStorageStats());
|
|
73
|
+
}
|
|
74
|
+
async function storageSweep() {
|
|
75
|
+
const { client } = _client();
|
|
76
|
+
return _run("Storage sweep failed", () => client.adminStorageSweep());
|
|
77
|
+
}
|
|
78
|
+
async function storageRepair() {
|
|
79
|
+
const { client } = _client();
|
|
80
|
+
return _run("Storage repair failed", () => client.adminStorageRepair());
|
|
81
|
+
}
|
|
82
|
+
async function snapshotsBackfillRefs() {
|
|
83
|
+
const { client } = _client();
|
|
84
|
+
return _run("Snapshot ref backfill failed", () => client.adminSnapshotsBackfillRefs());
|
|
85
|
+
}
|
|
86
|
+
async function snapshotsBackfillBlobs() {
|
|
87
|
+
const { client } = _client();
|
|
88
|
+
return _run("Snapshot blob backfill failed", () => client.adminSnapshotsBackfillBlobs());
|
|
89
|
+
}
|
|
90
|
+
async function configList() {
|
|
91
|
+
const { client } = _client();
|
|
92
|
+
return _run("Failed to load config", () => client.adminConfigList());
|
|
93
|
+
}
|
|
94
|
+
async function configGet(path) {
|
|
95
|
+
const { client } = _client();
|
|
96
|
+
return _run(`Failed to read ${path}`, () => client.adminConfigGet(path));
|
|
97
|
+
}
|
|
98
|
+
async function configSet(path, value) {
|
|
99
|
+
const { client } = _client();
|
|
100
|
+
return _run(`Failed to set ${path}`, () => client.adminConfigSet(path, value));
|
|
101
|
+
}
|
|
102
|
+
async function configUnset(path) {
|
|
103
|
+
const { client } = _client();
|
|
104
|
+
return _run(`Failed to reset ${path}`, () => client.adminConfigUnset(path));
|
|
105
|
+
}
|
|
106
|
+
async function configEnvSnapshot() {
|
|
107
|
+
const { client } = _client();
|
|
108
|
+
return _run("Failed to load env snapshot", () => client.adminConfigEnvSnapshot());
|
|
109
|
+
}
|
|
110
|
+
async function configListRoutes() {
|
|
111
|
+
const { client } = _client();
|
|
112
|
+
return _run("Failed to load route overrides", () => client.adminConfigListRoutes());
|
|
113
|
+
}
|
|
114
|
+
async function configGetRoute(route, path) {
|
|
115
|
+
const { client } = _client();
|
|
116
|
+
return _run(`Failed to read ${path} for ${route}`, () => client.adminConfigGetRoute(route, path));
|
|
117
|
+
}
|
|
118
|
+
async function configSetRoute(route, path, value) {
|
|
119
|
+
const { client } = _client();
|
|
120
|
+
return _run(`Failed to set ${path} for ${route}`, () => client.adminConfigSetRoute(route, path, value));
|
|
121
|
+
}
|
|
122
|
+
async function configUnsetRoute(route, path) {
|
|
123
|
+
const { client } = _client();
|
|
124
|
+
return _run(`Failed to clear ${path} for ${route}`, () => client.adminConfigUnsetRoute(route, path));
|
|
62
125
|
}
|
|
63
126
|
async function backupDump(opts) {
|
|
64
127
|
const { client } = _client();
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
return await client.adminBackupDump(opts);
|
|
68
|
-
} catch (e) {
|
|
69
|
-
error.value = e instanceof Error ? e.message : "Backup dump failed";
|
|
70
|
-
throw e;
|
|
71
|
-
}
|
|
128
|
+
return _run("Backup dump failed", () => client.adminBackupDump(opts));
|
|
72
129
|
}
|
|
73
130
|
async function downloadBackup(opts) {
|
|
74
131
|
const { abra } = _client();
|
|
@@ -99,14 +156,57 @@ export function useAbraAdmin() {
|
|
|
99
156
|
isLoading,
|
|
100
157
|
/** Last error message, or null. */
|
|
101
158
|
error,
|
|
159
|
+
/* Audit */
|
|
102
160
|
/** GET /admin/audit. */
|
|
103
161
|
auditList,
|
|
104
162
|
/** GET /admin/audit/export — returns NDJSON as a string. */
|
|
105
163
|
auditExport,
|
|
106
164
|
/** GET /admin/audit/verify — returns the result whether ok or broken. */
|
|
107
165
|
auditVerify,
|
|
166
|
+
/* Users */
|
|
167
|
+
/** GET /admin/users — profiles + revoked + device keys + effective role. */
|
|
168
|
+
listUsers,
|
|
169
|
+
/** POST /admin/users/:id/admin (service identity only). */
|
|
170
|
+
promoteUser,
|
|
171
|
+
/** DELETE /admin/users/:id/admin (service identity only). */
|
|
172
|
+
demoteUser,
|
|
108
173
|
/** POST /admin/users/:id/unlock. */
|
|
109
174
|
unlockUser,
|
|
175
|
+
/** GET /admin/users/:id/docs — owned + explicitly-granted docs. */
|
|
176
|
+
userDocs,
|
|
177
|
+
/* Storage & uploads */
|
|
178
|
+
/** GET /admin/uploads — paginated/filterable, with dedup ref counts. */
|
|
179
|
+
listUploads,
|
|
180
|
+
/** GET /admin/storage/stats. */
|
|
181
|
+
storageStats,
|
|
182
|
+
/** POST /admin/storage/sweep — GC orphaned blobs. */
|
|
183
|
+
storageSweep,
|
|
184
|
+
/** POST /admin/storage/repair — repair ref-counts + sweep. */
|
|
185
|
+
storageRepair,
|
|
186
|
+
/** POST /admin/snapshots/backfill-refs — populate snapshot_files. */
|
|
187
|
+
snapshotsBackfillRefs,
|
|
188
|
+
/** POST /admin/snapshots/backfill-blobs — migrate to dedup store. */
|
|
189
|
+
snapshotsBackfillBlobs,
|
|
190
|
+
/* Runtime config */
|
|
191
|
+
/** GET /admin/config. */
|
|
192
|
+
configList,
|
|
193
|
+
/** GET /admin/config/fields/:path. */
|
|
194
|
+
configGet,
|
|
195
|
+
/** PUT /admin/config/fields/:path. */
|
|
196
|
+
configSet,
|
|
197
|
+
/** DELETE /admin/config/fields/:path — revert to base. */
|
|
198
|
+
configUnset,
|
|
199
|
+
/** GET /admin/config/env-snapshot. */
|
|
200
|
+
configEnvSnapshot,
|
|
201
|
+
/** GET /admin/config/routes — routes with ≥1 override. */
|
|
202
|
+
configListRoutes,
|
|
203
|
+
/** GET /admin/config/routes/:route/fields/:path. */
|
|
204
|
+
configGetRoute,
|
|
205
|
+
/** PUT /admin/config/routes/:route/fields/:path. */
|
|
206
|
+
configSetRoute,
|
|
207
|
+
/** DELETE /admin/config/routes/:route/fields/:path. */
|
|
208
|
+
configUnsetRoute,
|
|
209
|
+
/* Backup */
|
|
110
210
|
/** GET /admin/backup/dump — returns the tar Blob. */
|
|
111
211
|
backupDump,
|
|
112
212
|
/** Convenience: backupDump + browser download. */
|