@revenexx/studio-integrations 0.1.3 → 0.2.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/dist/module.json +1 -1
- package/dist/runtime/components/Integrations/CredentialCreateDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +279 -0
- package/dist/runtime/components/Integrations/CredentialCreateDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.d.vue.ts +20 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +342 -0
- package/dist/runtime/components/Integrations/CredentialDetailDialog.vue.d.ts +20 -0
- package/dist/runtime/components/Integrations/CredentialLogo.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/CredentialLogo.vue +37 -0
- package/dist/runtime/components/Integrations/CredentialLogo.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +364 -0
- package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.d.vue.ts +15 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue +71 -0
- package/dist/runtime/components/Integrations/CredentialTypePicker.vue.d.ts +15 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.d.vue.ts +5 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue +151 -0
- package/dist/runtime/components/Integrations/DeadLettersPanel.vue.d.ts +5 -0
- package/dist/runtime/components/Integrations/EditorTip.d.vue.ts +17 -0
- package/dist/runtime/components/Integrations/EditorTip.vue +46 -0
- package/dist/runtime/components/Integrations/EditorTip.vue.d.ts +17 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue +64 -0
- package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +19 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue +236 -0
- package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +19 -0
- package/dist/runtime/components/Integrations/GroupFrameView.d.vue.ts +28 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue +222 -0
- package/dist/runtime/components/Integrations/GroupFrameView.vue.d.ts +28 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +184 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.d.vue.ts +16 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +102 -0
- package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue.d.ts +16 -0
- package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +23 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue +587 -0
- package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +23 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue +80 -0
- package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +22 -3
- package/dist/runtime/components/Integrations/NodeInspector.vue +1074 -294
- package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +22 -3
- package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue +38 -0
- package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/NodePalette.d.vue.ts +22 -1
- package/dist/runtime/components/Integrations/NodePalette.vue +411 -90
- package/dist/runtime/components/Integrations/NodePalette.vue.d.ts +22 -1
- package/dist/runtime/components/Integrations/QueryBuilder.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/QueryBuilder.vue +239 -0
- package/dist/runtime/components/Integrations/QueryBuilder.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/{RunDetailDialog.d.vue.ts → RunActionsMenu.d.vue.ts} +3 -4
- package/dist/runtime/components/Integrations/RunActionsMenu.vue +86 -0
- package/dist/runtime/components/Integrations/{RunDetailDialog.vue.d.ts → RunActionsMenu.vue.d.ts} +3 -4
- package/dist/runtime/components/Integrations/RunDetailPanel.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue +777 -0
- package/dist/runtime/components/Integrations/RunDetailPanel.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.vue +175 -0
- package/dist/runtime/components/Integrations/RunPayloadInput.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/RunResponseModal.d.vue.ts +27 -0
- package/dist/runtime/components/Integrations/RunResponseModal.vue +84 -0
- package/dist/runtime/components/Integrations/RunResponseModal.vue.d.ts +27 -0
- package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +21 -0
- package/dist/runtime/components/Integrations/RunsTable.vue +114 -0
- package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +21 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.d.vue.ts +11 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +91 -0
- package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue.d.ts +11 -0
- package/dist/runtime/components/Integrations/Sidebar.vue +1 -0
- package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +28 -0
- package/dist/runtime/components/Integrations/StickyNoteView.vue +208 -0
- package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +28 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +29 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +201 -0
- package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +29 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.d.vue.ts +32 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +138 -0
- package/dist/runtime/components/Integrations/WorkflowContextMenu.vue.d.ts +32 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.d.vue.ts +9 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +322 -0
- package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue.d.ts +9 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.d.vue.ts +18 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +63 -0
- package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue.d.ts +18 -0
- package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +12 -1
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +68 -6
- package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +12 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +8 -1
- package/dist/runtime/components/Integrations/WorkflowEditor.vue +1424 -144
- package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +8 -1
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +417 -0
- package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +89 -0
- package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.d.vue.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.vue +35 -0
- package/dist/runtime/components/Integrations/WorkflowIconView.vue.d.ts +10 -0
- package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +5 -1
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue +114 -84
- package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +5 -1
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.d.vue.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +654 -0
- package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue.d.ts +12 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +36 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +136 -0
- package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +36 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +14 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +63 -0
- package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +14 -0
- package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +35 -15
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.d.vue.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +265 -0
- package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue.d.ts +13 -0
- package/dist/runtime/components/Integrations/WorkflowsTable.d.vue.ts +4 -1
- package/dist/runtime/components/Integrations/WorkflowsTable.vue +122 -39
- package/dist/runtime/components/Integrations/WorkflowsTable.vue.d.ts +4 -1
- package/dist/runtime/components/Integrations/workflowLayout.d.ts +5 -0
- package/dist/runtime/components/Integrations/workflowLayout.js +5 -3
- package/dist/runtime/composables/integrationsApi.d.ts +6 -2
- package/dist/runtime/composables/integrationsApi.js +5 -4
- package/dist/runtime/composables/useCanvasShortcuts.d.ts +37 -0
- package/dist/runtime/composables/useCanvasShortcuts.js +62 -0
- package/dist/runtime/composables/useIntegrationsDeadLetters.d.ts +22 -0
- package/dist/runtime/composables/useIntegrationsDeadLetters.js +50 -0
- package/dist/runtime/composables/useIntegrationsNodes.d.ts +4 -0
- package/dist/runtime/composables/useIntegrationsNodes.js +6 -3
- package/dist/runtime/composables/useIntegrationsRevisions.d.ts +40 -0
- package/dist/runtime/composables/useIntegrationsRevisions.js +51 -0
- package/dist/runtime/composables/useIntegrationsRuns.d.ts +21 -1
- package/dist/runtime/composables/useIntegrationsRuns.js +92 -4
- package/dist/runtime/composables/useIntegrationsTemplates.d.ts +1 -0
- package/dist/runtime/composables/useIntegrationsTemplates.js +32 -4
- package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +1 -0
- package/dist/runtime/composables/useIntegrationsWorkflows.js +57 -1
- package/dist/runtime/composables/useMiniMapLayout.d.ts +34 -0
- package/dist/runtime/composables/useMiniMapLayout.js +119 -0
- package/dist/runtime/composables/useNodeConfigResolve.d.ts +43 -0
- package/dist/runtime/composables/useNodeConfigResolve.js +123 -0
- package/dist/runtime/composables/useNodeDrag.d.ts +22 -0
- package/dist/runtime/composables/useNodeDrag.js +10 -0
- package/dist/runtime/composables/usePaletteLayout.d.ts +41 -0
- package/dist/runtime/composables/usePaletteLayout.js +113 -0
- package/dist/runtime/data/expressionEditor.d.ts +22 -0
- package/dist/runtime/data/expressionEditor.js +40 -0
- package/dist/runtime/data/integrationNodeCatalog.js +8 -8
- package/dist/runtime/data/nodeInputData.d.ts +37 -0
- package/dist/runtime/data/nodeInputData.js +62 -0
- package/dist/runtime/data/nodeRunData.d.ts +56 -0
- package/dist/runtime/data/nodeRunData.js +91 -0
- package/dist/runtime/data/payloadForm.d.ts +44 -0
- package/dist/runtime/data/payloadForm.js +126 -0
- package/dist/runtime/data/workflowIconCatalog.d.ts +34 -0
- package/dist/runtime/data/workflowIconCatalog.js +116 -0
- package/dist/runtime/pages/integrations/audit.vue +25 -35
- package/dist/runtime/pages/integrations/credentials.vue +117 -122
- package/dist/runtime/pages/integrations/index.vue +133 -36
- package/dist/runtime/pages/integrations/runs.vue +125 -75
- package/dist/runtime/pages/integrations/schedules.vue +5 -16
- package/dist/runtime/pages/integrations/secrets.vue +1 -3
- package/dist/runtime/pages/integrations/workflows/[id].vue +599 -194
- package/dist/runtime/pages/integrations/workflows/index.vue +10 -8
- package/dist/runtime/types/integrations.d.ts +190 -4
- package/dist/runtime/utils/credentialState.d.ts +28 -0
- package/dist/runtime/utils/credentialState.js +29 -0
- package/dist/runtime/utils/localize.d.ts +3 -1
- package/dist/runtime/utils/localize.js +1 -2
- package/dist/runtime/utils/statusStyle.d.ts +6 -0
- package/dist/runtime/utils/statusStyle.js +15 -0
- package/package.json +12 -2
- package/dist/runtime/components/Integrations/RunDetailDialog.vue +0 -169
- package/dist/runtime/pages/integrations/workflows/new.d.vue.ts +0 -2
- package/dist/runtime/pages/integrations/workflows/new.vue +0 -153
- package/dist/runtime/pages/integrations/workflows/new.vue.d.ts +0 -2
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import {
|
|
3
3
|
ShieldCheck,
|
|
4
|
-
Magnifier as Search,
|
|
5
4
|
CloseCircle as Close,
|
|
6
5
|
Copy as CopyIcon,
|
|
7
6
|
ShieldKeyhole as Credential,
|
|
@@ -10,23 +9,15 @@ import {
|
|
|
10
9
|
Bolt as Trigger,
|
|
11
10
|
Play as Run
|
|
12
11
|
} from "#solar-icons";
|
|
13
|
-
import { Badge, Button
|
|
12
|
+
import { Badge, Button } from "@revenexx/studio";
|
|
14
13
|
import { toast } from "vue-sonner";
|
|
15
14
|
useHead({ title: "Audit Log \xB7 Integration Studio" });
|
|
16
15
|
const { entries, loading, error, hasMore, load, loadMore } = useIntegrationsAudit();
|
|
17
16
|
const resourceTypes = ["credential", "secret", "workflow", "trigger", "workflow_run"];
|
|
18
17
|
const actions = ["create", "update", "delete", "run.started", "run.finished", "run.failed"];
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function filters() {
|
|
23
|
-
return { resource_type: resourceType.value ?? void 0, action: action.value ?? void 0, sub: actor.value.trim() || void 0 };
|
|
24
|
-
}
|
|
25
|
-
function reload() {
|
|
26
|
-
void load(filters());
|
|
27
|
-
}
|
|
28
|
-
onMounted(reload);
|
|
29
|
-
watch([resourceType, action], reload);
|
|
18
|
+
onMounted(() => {
|
|
19
|
+
void load();
|
|
20
|
+
});
|
|
30
21
|
const resourceIcon = { credential: Credential, secret: Secret, workflow: Workflow, trigger: Trigger, workflow_run: Run };
|
|
31
22
|
function actionTone(a) {
|
|
32
23
|
if (a === "create" || a === "run.finished") return "bg-emerald-500/10 text-emerald-700 border-emerald-500/20";
|
|
@@ -46,6 +37,20 @@ function resourceName(e) {
|
|
|
46
37
|
function fmtDate(iso) {
|
|
47
38
|
return iso ? new Date(iso).toLocaleString() : "\u2014";
|
|
48
39
|
}
|
|
40
|
+
const filterTabs = [
|
|
41
|
+
{
|
|
42
|
+
key: "resource",
|
|
43
|
+
label: "Resource",
|
|
44
|
+
getter: (e) => e.resource_type,
|
|
45
|
+
options: resourceTypes.map((t) => ({ value: t, label: prettyType(t) }))
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
key: "action",
|
|
49
|
+
label: "Action",
|
|
50
|
+
getter: (e) => e.action,
|
|
51
|
+
options: actions.map((a) => ({ value: a, label: prettyAction(a) }))
|
|
52
|
+
}
|
|
53
|
+
];
|
|
49
54
|
const columns = [
|
|
50
55
|
{ key: "action", label: "Action", width: "130px", sortable: true, sortGetter: (e) => e.action },
|
|
51
56
|
{ key: "resource", label: "Resource", sortable: true, sortGetter: (e) => `${e.resource_type} ${resourceName(e)}`, searchGetter: (e) => `${e.resource_type} ${resourceName(e)} ${e.action} ${e.actor_sub ?? ""}` },
|
|
@@ -66,23 +71,6 @@ function copy(t) {
|
|
|
66
71
|
<PageHeader title="Audit Log" description="Who changed what, and when. Append-only, per-tenant — credentials, secrets, workflows, triggers and runs." />
|
|
67
72
|
|
|
68
73
|
<div class="p-4 space-y-3">
|
|
69
|
-
<!-- Filters -->
|
|
70
|
-
<div class="flex flex-wrap items-center gap-2">
|
|
71
|
-
<div class="flex flex-wrap gap-1.5">
|
|
72
|
-
<button class="rounded-full border px-3 py-1 text-xs transition-colors" :class="!resourceType ? 'border-primary/40 bg-primary/10 text-primary' : 'border-black/[0.06] text-muted-foreground hover:bg-black/[0.03]'" @click="resourceType = null">All</button>
|
|
73
|
-
<button v-for="t in resourceTypes" :key="t" class="rounded-full border px-3 py-1 text-xs capitalize transition-colors" :class="resourceType === t ? 'border-primary/40 bg-primary/10 text-primary' : 'border-black/[0.06] text-muted-foreground hover:bg-black/[0.03]'" @click="resourceType = t">{{ prettyType(t) }}</button>
|
|
74
|
-
</div>
|
|
75
|
-
<div class="flex-1" />
|
|
76
|
-
<select v-model="action" class="h-9 rounded-md border border-black/[0.1] bg-white px-2 text-sm">
|
|
77
|
-
<option :value="null">All actions</option>
|
|
78
|
-
<option v-for="a in actions" :key="a" :value="a">{{ prettyAction(a) }}</option>
|
|
79
|
-
</select>
|
|
80
|
-
<div class="relative">
|
|
81
|
-
<Search :size="14" class="absolute left-2.5 top-2.5 text-muted-foreground" />
|
|
82
|
-
<Input v-model="actor" class="h-9 w-44 pl-8 text-sm" placeholder="Actor (sub)…" @keydown.enter="reload" />
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
|
|
86
74
|
<p v-if="error" class="text-sm text-rose-700">{{ error }}</p>
|
|
87
75
|
|
|
88
76
|
<DataTable
|
|
@@ -90,12 +78,14 @@ function copy(t) {
|
|
|
90
78
|
:columns="columns"
|
|
91
79
|
:row-key="(e) => e.id"
|
|
92
80
|
:default-sort="{ column: 'when', direction: 'desc' }"
|
|
93
|
-
:search="{ placeholder: 'Search
|
|
94
|
-
:
|
|
81
|
+
:search="{ placeholder: 'Search action, resource, actor\u2026' }"
|
|
82
|
+
:filter-tabs="filterTabs"
|
|
83
|
+
:loading="loading"
|
|
84
|
+
empty-text="No audit entries match."
|
|
95
85
|
@row-click="(e) => selected = e"
|
|
96
86
|
>
|
|
97
87
|
<template #cell-action="{ row }">
|
|
98
|
-
<Badge variant="outline" :class="['text-xs
|
|
88
|
+
<Badge variant="outline" :class="['border text-xs font-medium uppercase', actionTone(row.action)]">{{ prettyAction(row.action) }}</Badge>
|
|
99
89
|
</template>
|
|
100
90
|
<template #cell-resource="{ row }">
|
|
101
91
|
<div class="flex items-center gap-2 min-w-0">
|
|
@@ -115,7 +105,7 @@ function copy(t) {
|
|
|
115
105
|
</DataTable>
|
|
116
106
|
|
|
117
107
|
<div v-if="hasMore" class="flex justify-center pt-1">
|
|
118
|
-
<Button variant="outline" size="sm" :disabled="loading" @click="loadMore(
|
|
108
|
+
<Button variant="outline" size="sm" :disabled="loading" @click="loadMore()">{{ loading ? "Loading\u2026" : "Load more" }}</Button>
|
|
119
109
|
</div>
|
|
120
110
|
</div>
|
|
121
111
|
|
|
@@ -125,7 +115,7 @@ function copy(t) {
|
|
|
125
115
|
<div class="flex-1 bg-black/20 backdrop-blur-[1px]" @click="selected = null" />
|
|
126
116
|
<aside class="w-full max-w-md overflow-y-auto border-l border-black/[0.06] bg-white shadow-xl">
|
|
127
117
|
<div class="sticky top-0 z-10 flex items-center gap-2 border-b border-black/[0.06] bg-white/90 backdrop-blur px-4 py-3">
|
|
128
|
-
<Badge variant="outline" :class="['text-xs
|
|
118
|
+
<Badge variant="outline" :class="['border text-xs font-medium uppercase', actionTone(selected.action)]">{{ prettyAction(selected.action) }}</Badge>
|
|
129
119
|
<span class="text-sm font-semibold text-[#0B0522] capitalize">{{ prettyType(selected.resource_type) }}</span>
|
|
130
120
|
<Button variant="ghost" size="sm" class="ml-auto h-7 w-7 p-0" @click="selected = null"><Close :size="15" /></Button>
|
|
131
121
|
</div>
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, onMounted, reactive, ref } from "vue";
|
|
3
3
|
import {
|
|
4
|
-
CheckCircle as CheckCircle2,
|
|
5
4
|
RefreshCircle as Loader2,
|
|
6
|
-
Magnifier as Search,
|
|
7
5
|
AddCircle as Plus,
|
|
8
6
|
TrashBinTrash as Trash2,
|
|
9
|
-
TestTube as TestIcon
|
|
10
|
-
ShieldKeyhole as KeyIcon
|
|
7
|
+
TestTube as TestIcon
|
|
11
8
|
} from "#solar-icons";
|
|
12
9
|
import {
|
|
13
10
|
Badge,
|
|
@@ -28,19 +25,13 @@ import {
|
|
|
28
25
|
SelectValue
|
|
29
26
|
} from "@revenexx/studio";
|
|
30
27
|
import { toast } from "vue-sonner";
|
|
28
|
+
import { credentialState } from "../../utils/credentialState";
|
|
31
29
|
useHead({ title: "Credentials \xB7 Revenexx Cockpit" });
|
|
32
30
|
const { credentials, loading, error, load, create, update, remove, testInline, test, oauthAuthorizeUrl } = useIntegrationsCredentials();
|
|
33
31
|
const { types, load: loadTypes, bySlug } = useCredentialTypes();
|
|
34
32
|
onMounted(async () => {
|
|
35
33
|
await Promise.all([load(), loadTypes()]);
|
|
36
34
|
});
|
|
37
|
-
const search = ref("");
|
|
38
|
-
const filtered = computed(() => {
|
|
39
|
-
const q = search.value.trim().toLowerCase();
|
|
40
|
-
const list = [...credentials.value].sort((a, b) => a.name.localeCompare(b.name));
|
|
41
|
-
if (!q) return list;
|
|
42
|
-
return list.filter((c) => c.name.toLowerCase().includes(q) || c.credential_type_slug.toLowerCase().includes(q));
|
|
43
|
-
});
|
|
44
35
|
function typeFor(slug) {
|
|
45
36
|
return bySlug(slug);
|
|
46
37
|
}
|
|
@@ -64,6 +55,15 @@ function formatDateTime(iso) {
|
|
|
64
55
|
return iso;
|
|
65
56
|
}
|
|
66
57
|
}
|
|
58
|
+
const columns = [
|
|
59
|
+
{ key: "system", label: "System", width: "minmax(160px,220px)", sortable: true, sortGetter: (c) => typeName(c.credential_type_slug).toLowerCase(), searchGetter: (c) => `${typeName(c.credential_type_slug)} ${c.credential_type_slug}` },
|
|
60
|
+
{ key: "name", label: "Name", sortable: true, sortGetter: (c) => c.name.toLowerCase(), searchGetter: (c) => c.name },
|
|
61
|
+
{ key: "status", label: "Status", width: "150px", sortable: true, sortGetter: (c) => credentialState(c).key },
|
|
62
|
+
{ key: "connected", label: "Connected", width: "150px", sortable: true, sortGetter: (c) => c.last_test_ok ? c.last_test_at ?? "" : "" },
|
|
63
|
+
{ key: "created", label: "Created", width: "150px", sortable: true, sortGetter: (c) => c.created_at ?? "" },
|
|
64
|
+
{ key: "updated", label: "Updated", width: "150px", sortable: true, sortGetter: (c) => c.updated_at ?? "" },
|
|
65
|
+
{ key: "actions", label: "", width: "80px" }
|
|
66
|
+
];
|
|
67
67
|
const dialogOpen = ref(false);
|
|
68
68
|
const editing = ref(null);
|
|
69
69
|
const pickedType = ref(null);
|
|
@@ -71,6 +71,7 @@ const form = reactive({ name: "", config: {} });
|
|
|
71
71
|
const saving = ref(false);
|
|
72
72
|
const testing = ref(false);
|
|
73
73
|
const testResult = ref(null);
|
|
74
|
+
const sourceActive = ref(false);
|
|
74
75
|
const isEdit = computed(() => editing.value !== null);
|
|
75
76
|
function fieldsOf(t) {
|
|
76
77
|
return t?.fields ?? [];
|
|
@@ -81,15 +82,24 @@ function openCreate(t) {
|
|
|
81
82
|
form.name = "";
|
|
82
83
|
form.config = {};
|
|
83
84
|
testResult.value = null;
|
|
85
|
+
sourceActive.value = false;
|
|
84
86
|
dialogOpen.value = true;
|
|
85
87
|
}
|
|
88
|
+
const detailOpen = ref(false);
|
|
89
|
+
const detailCredential = ref(null);
|
|
90
|
+
const detailType = ref(null);
|
|
86
91
|
function openEdit(c) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
detailCredential.value = c;
|
|
93
|
+
detailType.value = typeFor(c.credential_type_slug);
|
|
94
|
+
detailOpen.value = true;
|
|
95
|
+
}
|
|
96
|
+
async function onDetailChanged() {
|
|
97
|
+
const id = detailCredential.value?.id;
|
|
98
|
+
await load(true);
|
|
99
|
+
if (id) {
|
|
100
|
+
const fresh = credentials.value.find((c) => c.id === id);
|
|
101
|
+
if (fresh) detailCredential.value = fresh;
|
|
102
|
+
}
|
|
93
103
|
}
|
|
94
104
|
function buildConfigPayload() {
|
|
95
105
|
const out = {};
|
|
@@ -164,17 +174,6 @@ async function connect(c) {
|
|
|
164
174
|
connecting.value = null;
|
|
165
175
|
}
|
|
166
176
|
}
|
|
167
|
-
const cardTesting = ref(null);
|
|
168
|
-
async function testCard(c) {
|
|
169
|
-
cardTesting.value = c.id;
|
|
170
|
-
try {
|
|
171
|
-
const res = await test(c.id);
|
|
172
|
-
if (res.ok) toast.success(`${c.name}: connection ok`);
|
|
173
|
-
else toast.error(`${c.name}: ${res.error ?? "connection failed"}`);
|
|
174
|
-
} finally {
|
|
175
|
-
cardTesting.value = null;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
177
|
async function destroy(c) {
|
|
179
178
|
if (!confirm(`Delete credential "${c.name}"? Workflows referencing it will fail until re-bound.`)) return;
|
|
180
179
|
try {
|
|
@@ -200,84 +199,76 @@ async function destroy(c) {
|
|
|
200
199
|
</template>
|
|
201
200
|
</PageHeader>
|
|
202
201
|
|
|
203
|
-
<div class="flex items-center justify-between gap-3 border-b border-black/[0.06] px-4 py-2">
|
|
204
|
-
<div class="text-sm text-muted-foreground">
|
|
205
|
-
{{ filtered.length }} credential{{ filtered.length === 1 ? "" : "s" }}
|
|
206
|
-
</div>
|
|
207
|
-
<div class="relative w-64">
|
|
208
|
-
<Search :size="12" class="absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground" />
|
|
209
|
-
<Input v-model="search" placeholder="Search…" class="h-7 pl-7 text-sm bg-white" />
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
212
|
-
|
|
213
202
|
<div v-if="error" class="m-4 rounded-md border border-rose-500/20 bg-rose-500/5 px-3 py-2 text-sm text-rose-700">
|
|
214
203
|
{{ error }}
|
|
215
204
|
</div>
|
|
216
205
|
|
|
217
|
-
<div class="p-4
|
|
218
|
-
<
|
|
219
|
-
|
|
220
|
-
:
|
|
221
|
-
|
|
206
|
+
<div class="p-4">
|
|
207
|
+
<DataTable
|
|
208
|
+
:data="credentials"
|
|
209
|
+
:columns="columns"
|
|
210
|
+
:row-key="(c2) => c2.id"
|
|
211
|
+
:default-sort="{ column: 'name', direction: 'asc' }"
|
|
212
|
+
:search="{ placeholder: 'Search credentials\u2026' }"
|
|
213
|
+
:filter="true"
|
|
214
|
+
:loading="loading"
|
|
215
|
+
empty-text="No credentials yet — hit “New credential” to connect your first system."
|
|
216
|
+
@row-click="(c2) => openEdit(c2)"
|
|
222
217
|
>
|
|
223
|
-
<
|
|
224
|
-
<
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
<div class="flex-1 min-w-0">
|
|
228
|
-
<div class="text-sm font-semibold truncate">
|
|
229
|
-
{{ c.name }}
|
|
230
|
-
</div>
|
|
231
|
-
<div class="text-sm text-muted-foreground truncate">
|
|
232
|
-
{{ typeName(c.credential_type_slug) }}
|
|
233
|
-
</div>
|
|
218
|
+
<template #cell-system="{ row: c }">
|
|
219
|
+
<div class="flex items-center gap-2 min-w-0">
|
|
220
|
+
<IntegrationsCredentialLogo :type="typeFor(c.credential_type_slug)" :size="16" />
|
|
221
|
+
<span class="font-medium text-sm truncate">{{ typeName(c.credential_type_slug) }}</span>
|
|
234
222
|
</div>
|
|
223
|
+
</template>
|
|
224
|
+
|
|
225
|
+
<template #cell-name="{ row: c }">
|
|
226
|
+
<span class="text-sm truncate">{{ c.name }}</span>
|
|
227
|
+
</template>
|
|
228
|
+
|
|
229
|
+
<template #cell-status="{ row: c }">
|
|
235
230
|
<Badge
|
|
236
|
-
v-if="c.status === 'active'"
|
|
237
|
-
variant="outline"
|
|
238
|
-
class="text-sm border bg-emerald-500/10 border-emerald-500/20 text-emerald-700 gap-1"
|
|
239
|
-
>
|
|
240
|
-
<CheckCircle2 :size="12" />
|
|
241
|
-
Active
|
|
242
|
-
</Badge>
|
|
243
|
-
<Badge
|
|
244
|
-
v-else
|
|
245
231
|
variant="outline"
|
|
246
|
-
class="
|
|
232
|
+
class="border text-xs font-medium uppercase"
|
|
233
|
+
:class="credentialState(c).toneClass"
|
|
234
|
+
:title="credentialState(c).message ?? void 0"
|
|
247
235
|
>
|
|
248
|
-
|
|
236
|
+
{{ credentialState(c).label }}
|
|
249
237
|
</Badge>
|
|
250
|
-
</
|
|
238
|
+
</template>
|
|
251
239
|
|
|
252
|
-
<
|
|
253
|
-
<span
|
|
254
|
-
|
|
255
|
-
|
|
240
|
+
<template #cell-connected="{ row: c }">
|
|
241
|
+
<span class="text-sm text-muted-foreground tabular-nums">
|
|
242
|
+
{{ c.last_test_ok ? formatDateTime(c.last_test_at) : "\u2014" }}
|
|
243
|
+
</span>
|
|
244
|
+
</template>
|
|
245
|
+
|
|
246
|
+
<template #cell-created="{ row: c }">
|
|
247
|
+
<span class="text-sm text-muted-foreground tabular-nums">{{ formatDateTime(c.created_at) }}</span>
|
|
248
|
+
</template>
|
|
249
|
+
|
|
250
|
+
<template #cell-updated="{ row: c }">
|
|
251
|
+
<span class="text-sm text-muted-foreground tabular-nums">{{ formatDateTime(c.updated_at) }}</span>
|
|
252
|
+
</template>
|
|
253
|
+
|
|
254
|
+
<template #cell-actions="{ row: c }">
|
|
255
|
+
<div class="flex items-center justify-end gap-2 text-sm" @click.stop>
|
|
256
|
+
<Button
|
|
256
257
|
v-if="c.status === 'needs_reauth' && isOAuthAuthcode(typeFor(c.credential_type_slug))"
|
|
257
|
-
|
|
258
|
-
|
|
258
|
+
variant="outline"
|
|
259
|
+
size="sm"
|
|
260
|
+
class="h-7 text-amber-600"
|
|
259
261
|
:disabled="connecting === c.id"
|
|
260
262
|
@click="connect(c)"
|
|
261
263
|
>
|
|
262
264
|
Reconnect
|
|
263
|
-
</
|
|
264
|
-
<button type="button" class="text-
|
|
265
|
-
<Loader2 v-if="cardTesting === c.id" :size="12" class="inline animate-spin" />
|
|
266
|
-
<span v-else>Test</span>
|
|
267
|
-
</button>
|
|
268
|
-
<button type="button" class="text-foreground/50 hover:text-foreground" @click="openEdit(c)">
|
|
269
|
-
Edit
|
|
270
|
-
</button>
|
|
271
|
-
<button type="button" class="text-foreground/40 hover:text-rose-600" @click="destroy(c)">
|
|
265
|
+
</Button>
|
|
266
|
+
<button type="button" class="flex size-7 items-center justify-center rounded text-foreground/40 hover:text-rose-600 hover:bg-rose-500/10" title="Delete" @click="destroy(c)">
|
|
272
267
|
<Trash2 :size="13" />
|
|
273
268
|
</button>
|
|
274
269
|
</div>
|
|
275
|
-
</
|
|
276
|
-
</
|
|
277
|
-
|
|
278
|
-
<div v-if="!loading && !filtered.length" class="col-span-full px-3 py-12 text-center text-sm text-muted-foreground">
|
|
279
|
-
No credentials yet. Hit "New credential" to connect your first system.
|
|
280
|
-
</div>
|
|
270
|
+
</template>
|
|
271
|
+
</DataTable>
|
|
281
272
|
</div>
|
|
282
273
|
|
|
283
274
|
<!-- Create / edit dialog -->
|
|
@@ -290,29 +281,15 @@ async function destroy(c) {
|
|
|
290
281
|
</DialogDescription>
|
|
291
282
|
</DialogHeader>
|
|
292
283
|
|
|
293
|
-
<!-- Step 1: type picker (create only) -->
|
|
294
|
-
<
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
>
|
|
303
|
-
<span class="flex size-8 items-center justify-center rounded-md bg-slate-100 text-slate-500 shrink-0">
|
|
304
|
-
<KeyIcon :size="16" />
|
|
305
|
-
</span>
|
|
306
|
-
<div class="min-w-0">
|
|
307
|
-
<div class="text-sm font-semibold truncate">
|
|
308
|
-
{{ loc(t.name) }}
|
|
309
|
-
</div>
|
|
310
|
-
<div class="text-sm text-muted-foreground truncate">
|
|
311
|
-
{{ t.auth_kind }}
|
|
312
|
-
</div>
|
|
313
|
-
</div>
|
|
314
|
-
</button>
|
|
315
|
-
</div>
|
|
284
|
+
<!-- Step 1: searchable type picker (create only) -->
|
|
285
|
+
<IntegrationsCredentialTypePicker
|
|
286
|
+
v-if="!pickedType"
|
|
287
|
+
:types="types"
|
|
288
|
+
@pick="(t) => {
|
|
289
|
+
pickedType = t;
|
|
290
|
+
form.name = loc(t.name);
|
|
291
|
+
}"
|
|
292
|
+
/>
|
|
316
293
|
|
|
317
294
|
<!-- Step 2: dynamic form -->
|
|
318
295
|
<div v-else class="space-y-3 py-1 max-h-[28rem] overflow-y-auto pr-1">
|
|
@@ -321,7 +298,14 @@ async function destroy(c) {
|
|
|
321
298
|
<Input id="cred-name" v-model="form.name" class="bg-white" placeholder="e.g. Production SMTP" />
|
|
322
299
|
</div>
|
|
323
300
|
|
|
324
|
-
|
|
301
|
+
<!-- "Use revenexx …" source picker (SFTP / Mail / API key) -->
|
|
302
|
+
<IntegrationsCredentialRevenexxSource
|
|
303
|
+
:type="pickedType"
|
|
304
|
+
@update:active="(v) => sourceActive = v"
|
|
305
|
+
@apply="(c2) => Object.assign(form.config, c2)"
|
|
306
|
+
/>
|
|
307
|
+
|
|
308
|
+
<div v-for="f in fieldsOf(pickedType)" v-show="!sourceActive" :key="f.key" class="space-y-1.5">
|
|
325
309
|
<Label>{{ loc(f.label) }}{{ f.required ? " *" : "" }}</Label>
|
|
326
310
|
|
|
327
311
|
<!-- secret -->
|
|
@@ -383,21 +367,32 @@ async function destroy(c) {
|
|
|
383
367
|
</div>
|
|
384
368
|
</div>
|
|
385
369
|
|
|
386
|
-
<DialogFooter v-if="pickedType">
|
|
387
|
-
<Button v-if="!isEdit" variant="
|
|
370
|
+
<DialogFooter v-if="pickedType" class="sm:justify-between">
|
|
371
|
+
<Button v-if="!isEdit" variant="ghost" size="sm" :disabled="saving || testing" @click="pickedType = null">
|
|
388
372
|
Back
|
|
389
373
|
</Button>
|
|
390
|
-
<
|
|
391
|
-
|
|
392
|
-
<
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
374
|
+
<span v-else />
|
|
375
|
+
<div class="flex items-center gap-2">
|
|
376
|
+
<Button variant="outline" :disabled="saving || testing" @click="runTest">
|
|
377
|
+
<Loader2 v-if="testing" :size="14" class="mr-1 animate-spin" />
|
|
378
|
+
<TestIcon v-else :size="14" class="mr-1" />
|
|
379
|
+
Connect
|
|
380
|
+
</Button>
|
|
381
|
+
<Button :disabled="saving || !form.name.trim()" @click="submit">
|
|
382
|
+
<Loader2 v-if="saving" :size="14" class="mr-1 animate-spin" />
|
|
383
|
+
Save
|
|
384
|
+
</Button>
|
|
385
|
+
</div>
|
|
399
386
|
</DialogFooter>
|
|
400
387
|
</DialogContent>
|
|
401
388
|
</Dialog>
|
|
389
|
+
|
|
390
|
+
<!-- Detail / edit view (single-page: connection state + fields + metadata) -->
|
|
391
|
+
<IntegrationsCredentialDetailDialog
|
|
392
|
+
v-model:open="detailOpen"
|
|
393
|
+
:credential="detailCredential"
|
|
394
|
+
:type="detailType"
|
|
395
|
+
@changed="onDetailChanged"
|
|
396
|
+
/>
|
|
402
397
|
</IntegrationsLayout>
|
|
403
398
|
</template>
|