@nvent-addon/app 0.4.5 → 0.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/dist/module.d.mts +19 -1
- package/dist/module.mjs +20 -8
- package/dist/runtime/app/components/{nhealth/component-router.d.vue.ts → ComponentRouter.d.vue.ts} +1 -5
- package/dist/runtime/app/components/{nhealth/component-router.vue.d.ts → ComponentRouter.vue.d.ts} +1 -5
- package/dist/runtime/app/components/{nhealth/component-shell.d.vue.ts → ComponentShell.d.vue.ts} +4 -9
- package/dist/runtime/app/components/ComponentShell.vue +87 -0
- package/dist/runtime/app/components/{nhealth/component-shell.vue.d.ts → ComponentShell.vue.d.ts} +4 -9
- package/dist/runtime/app/components/ConfirmDialog.d.vue.ts +1 -6
- package/dist/runtime/app/components/ConfirmDialog.vue.d.ts +1 -6
- package/dist/runtime/app/components/ListItem.d.vue.ts +3 -6
- package/dist/runtime/app/components/ListItem.vue.d.ts +3 -6
- package/dist/runtime/app/components/LiveIndicator.d.vue.ts +7 -0
- package/dist/runtime/app/components/LiveIndicator.vue +30 -0
- package/dist/runtime/app/components/LiveIndicator.vue.d.ts +7 -0
- package/dist/runtime/app/components/{QueueConfigDetails.d.vue.ts → QueueConfiguration.d.vue.ts} +1 -10
- package/dist/runtime/app/components/QueueConfiguration.vue +387 -0
- package/dist/runtime/app/components/{QueueConfigDetails.vue.d.ts → QueueConfiguration.vue.d.ts} +1 -10
- package/dist/runtime/app/components/StatCard.d.vue.ts +9 -0
- package/dist/runtime/app/components/StatCard.vue +57 -0
- package/dist/runtime/app/components/StatCard.vue.d.ts +9 -0
- package/dist/runtime/app/components/TimelineList.vue +67 -0
- package/dist/runtime/app/components/flow/AwaitNode.d.vue.ts +18 -0
- package/dist/runtime/app/components/flow/AwaitNode.vue +91 -0
- package/dist/runtime/app/components/flow/AwaitNode.vue.d.ts +18 -0
- package/dist/runtime/app/components/{FlowDiagram.d.vue.ts → flow/Diagram.d.vue.ts} +12 -1
- package/dist/runtime/app/components/{FlowDiagram.vue → flow/Diagram.vue} +92 -11
- package/dist/runtime/app/components/{FlowDiagram.vue.d.ts → flow/Diagram.vue.d.ts} +12 -1
- package/dist/runtime/app/components/{FlowRunOverview.d.vue.ts → flow/RunOverview.d.vue.ts} +3 -0
- package/dist/runtime/app/components/{FlowRunOverview.vue → flow/RunOverview.vue} +94 -8
- package/dist/runtime/app/components/{FlowRunOverview.vue.d.ts → flow/RunOverview.vue.d.ts} +3 -0
- package/dist/runtime/app/components/{FlowRunStatusBadge.d.vue.ts → flow/RunStatusBadge.d.vue.ts} +2 -8
- package/dist/runtime/app/components/{FlowRunStatusBadge.vue → flow/RunStatusBadge.vue} +8 -1
- package/dist/runtime/app/components/{FlowRunStatusBadge.vue.d.ts → flow/RunStatusBadge.vue.d.ts} +2 -8
- package/dist/runtime/app/components/{FlowRunTimeline.vue → flow/RunTimeline.vue} +1 -1
- package/dist/runtime/app/components/{FlowStepSelector.d.vue.ts → flow/StepSelector.d.vue.ts} +1 -0
- package/dist/runtime/app/components/flow/StepSelector.vue +553 -0
- package/dist/runtime/app/components/{FlowStepSelector.vue.d.ts → flow/StepSelector.vue.d.ts} +1 -0
- package/dist/runtime/app/components/trigger/BasicInfoCard.d.vue.ts +33 -0
- package/dist/runtime/app/components/trigger/BasicInfoCard.vue +168 -0
- package/dist/runtime/app/components/trigger/BasicInfoCard.vue.d.ts +33 -0
- package/dist/runtime/app/components/{FlowSchedulesList.d.vue.ts → trigger/DangerZone.d.vue.ts} +4 -6
- package/dist/runtime/app/components/trigger/DangerZone.vue +46 -0
- package/dist/runtime/app/components/{FlowSchedulesList.vue.d.ts → trigger/DangerZone.vue.d.ts} +4 -6
- package/dist/runtime/app/components/trigger/EditHeader.d.vue.ts +15 -0
- package/dist/runtime/app/components/trigger/EditHeader.vue +55 -0
- package/dist/runtime/app/components/trigger/EditHeader.vue.d.ts +15 -0
- package/dist/runtime/app/components/trigger/EventConfig.d.vue.ts +24 -0
- package/dist/runtime/app/components/trigger/EventConfig.vue +68 -0
- package/dist/runtime/app/components/trigger/EventConfig.vue.d.ts +24 -0
- package/dist/runtime/app/components/trigger/FlowSubscriptions.d.vue.ts +14 -0
- package/dist/runtime/app/components/trigger/FlowSubscriptions.vue +128 -0
- package/dist/runtime/app/components/trigger/FlowSubscriptions.vue.d.ts +14 -0
- package/dist/runtime/app/components/trigger/ScheduleConfig.d.vue.ts +27 -0
- package/dist/runtime/app/components/trigger/ScheduleConfig.vue +375 -0
- package/dist/runtime/app/components/trigger/ScheduleConfig.vue.d.ts +27 -0
- package/dist/runtime/app/components/{FlowScheduleDialog.d.vue.ts → trigger/StatusConfig.d.vue.ts} +6 -6
- package/dist/runtime/app/components/trigger/StatusConfig.vue +78 -0
- package/dist/runtime/app/components/{FlowScheduleDialog.vue.d.ts → trigger/StatusConfig.vue.d.ts} +6 -6
- package/dist/runtime/app/components/trigger/WebhookConfig.d.vue.ts +30 -0
- package/dist/runtime/app/components/trigger/WebhookConfig.vue +97 -0
- package/dist/runtime/app/components/trigger/WebhookConfig.vue.d.ts +30 -0
- package/dist/runtime/app/composables/useAnalyzedFlows.d.ts +5 -0
- package/dist/runtime/app/composables/useAnalyzedFlows.js +15 -1
- package/dist/runtime/app/composables/useComponentRouter.d.ts +8 -0
- package/dist/runtime/app/composables/useComponentRouter.js +10 -2
- package/dist/runtime/app/composables/useFlowRunsInfinite.d.ts +1 -1
- package/dist/runtime/app/composables/useFlowState.js +65 -0
- package/dist/runtime/app/composables/useFlowWebSocket.d.ts +11 -2
- package/dist/runtime/app/composables/useFlowWebSocket.js +181 -65
- package/dist/runtime/app/composables/useQueueJobs.d.ts +12 -1
- package/dist/runtime/app/composables/useQueueJobs.js +13 -7
- package/dist/runtime/app/composables/useTrigger.d.ts +137 -0
- package/dist/runtime/app/composables/useTrigger.js +116 -0
- package/dist/runtime/app/composables/useTriggerWebSocket.d.ts +35 -0
- package/dist/runtime/app/composables/useTriggerWebSocket.js +333 -0
- package/dist/runtime/app/pages/dashboard.d.vue.ts +3 -0
- package/dist/runtime/app/pages/dashboard.vue +738 -0
- package/dist/runtime/app/pages/dashboard.vue.d.ts +3 -0
- package/dist/runtime/app/pages/flows/[name].d.vue.ts +3 -0
- package/dist/runtime/app/pages/flows/[name].vue +680 -0
- package/dist/runtime/app/pages/flows/[name].vue.d.ts +3 -0
- package/dist/runtime/app/pages/flows/index.vue +321 -620
- package/dist/runtime/app/pages/index.vue +39 -9
- package/dist/runtime/app/pages/queues/index.vue +202 -194
- package/dist/runtime/app/pages/queues/jobs.vue +534 -207
- package/dist/runtime/app/pages/settings/scheduler.d.vue.ts +3 -0
- package/dist/runtime/app/pages/settings/scheduler.vue +310 -0
- package/dist/runtime/app/pages/settings/scheduler.vue.d.ts +3 -0
- package/dist/runtime/app/pages/triggers/[name]/edit.d.vue.ts +3 -0
- package/dist/runtime/app/pages/triggers/[name]/edit.vue +429 -0
- package/dist/runtime/app/pages/triggers/[name]/edit.vue.d.ts +3 -0
- package/dist/runtime/app/pages/triggers/[name].d.vue.ts +3 -0
- package/dist/runtime/app/pages/triggers/[name].vue +898 -0
- package/dist/runtime/app/pages/triggers/[name].vue.d.ts +3 -0
- package/dist/runtime/app/pages/triggers/index.d.vue.ts +3 -0
- package/dist/runtime/app/pages/triggers/index.vue +528 -0
- package/dist/runtime/app/pages/triggers/index.vue.d.ts +3 -0
- package/dist/runtime/app/pages/triggers/new.d.vue.ts +3 -0
- package/dist/runtime/app/pages/triggers/new.vue +610 -0
- package/dist/runtime/app/pages/triggers/new.vue.d.ts +3 -0
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +10 -0
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +49 -0
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.d.ts +2 -0
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.js +21 -0
- package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +17 -0
- package/dist/runtime/server/api/_flows/[name]/runs.get.js +64 -0
- package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +2 -0
- package/dist/runtime/server/api/_flows/[name]/start.post.js +9 -0
- package/dist/runtime/server/api/_flows/index.get.d.ts +7 -0
- package/dist/runtime/server/api/_flows/index.get.js +5 -0
- package/dist/runtime/server/api/_flows/recent-runs.get.d.ts +15 -0
- package/dist/runtime/server/api/_flows/recent-runs.get.js +67 -0
- package/dist/runtime/server/api/_flows/ws.d.ts +80 -0
- package/dist/runtime/server/api/_flows/ws.js +309 -0
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.d.ts +2 -0
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.js +14 -0
- package/dist/runtime/server/api/_queues/[name]/job/index.get.d.ts +2 -0
- package/dist/runtime/server/api/_queues/[name]/job/index.get.js +39 -0
- package/dist/runtime/server/api/_queues/index.get.d.ts +2 -0
- package/dist/runtime/server/api/_queues/index.get.js +106 -0
- package/dist/runtime/server/api/_queues/ws.d.ts +48 -0
- package/dist/runtime/server/api/_queues/ws.js +215 -0
- package/dist/runtime/server/api/_scheduler/jobs.get.d.ts +19 -0
- package/dist/runtime/server/api/_scheduler/jobs.get.js +36 -0
- package/dist/runtime/server/api/_triggers/[name]/events.get.d.ts +6 -0
- package/dist/runtime/server/api/_triggers/[name]/events.get.js +43 -0
- package/dist/runtime/server/api/_triggers/[name]/index.get.d.ts +6 -0
- package/dist/runtime/server/api/_triggers/[name]/index.get.js +76 -0
- package/dist/runtime/server/api/_triggers/[name].delete.d.ts +7 -0
- package/dist/runtime/server/api/_triggers/[name].delete.js +37 -0
- package/dist/runtime/server/api/_triggers/[name].patch.d.ts +7 -0
- package/dist/runtime/server/api/_triggers/[name].patch.js +117 -0
- package/dist/runtime/server/api/_triggers/index.get.d.ts +6 -0
- package/dist/runtime/server/api/_triggers/index.get.js +44 -0
- package/dist/runtime/server/api/_triggers/index.post.d.ts +7 -0
- package/dist/runtime/server/api/_triggers/index.post.js +124 -0
- package/dist/runtime/server/api/_triggers/stats.get.d.ts +6 -0
- package/dist/runtime/server/api/_triggers/stats.get.js +41 -0
- package/dist/runtime/server/api/_triggers/ws.d.ts +74 -0
- package/dist/runtime/server/api/_triggers/ws.js +315 -0
- package/dist/runtime/server/tsconfig.json +7 -0
- package/package.json +8 -8
- package/dist/runtime/app/components/FlowScheduleDialog.vue +0 -226
- package/dist/runtime/app/components/FlowSchedulesList.vue +0 -99
- package/dist/runtime/app/components/FlowStepSelector.vue +0 -238
- package/dist/runtime/app/components/QueueConfigDetails.vue +0 -412
- package/dist/runtime/app/components/nhealth/component-shell.vue +0 -89
- /package/dist/runtime/app/components/{nhealth/component-router.vue → ComponentRouter.vue} +0 -0
- /package/dist/runtime/app/components/{FlowNodeCard.d.vue.ts → flow/NodeCard.d.vue.ts} +0 -0
- /package/dist/runtime/app/components/{FlowNodeCard.vue → flow/NodeCard.vue} +0 -0
- /package/dist/runtime/app/components/{FlowNodeCard.vue.d.ts → flow/NodeCard.vue.d.ts} +0 -0
- /package/dist/runtime/app/components/{FlowRunTimeline.d.vue.ts → flow/RunTimeline.d.vue.ts} +0 -0
- /package/dist/runtime/app/components/{FlowRunTimeline.vue.d.ts → flow/RunTimeline.vue.d.ts} +0 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineWebSocketHandler,
|
|
3
|
+
usePeerManager,
|
|
4
|
+
useNventLogger,
|
|
5
|
+
useStreamAdapter,
|
|
6
|
+
useStoreAdapter,
|
|
7
|
+
useStreamTopics,
|
|
8
|
+
useTrigger
|
|
9
|
+
} from "#imports";
|
|
10
|
+
const peerContexts = /* @__PURE__ */ new WeakMap();
|
|
11
|
+
function safeSend(peer, data) {
|
|
12
|
+
try {
|
|
13
|
+
peer.send(JSON.stringify(data));
|
|
14
|
+
return true;
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export default defineWebSocketHandler({
|
|
20
|
+
async open(peer) {
|
|
21
|
+
const logger = useNventLogger("api-triggers-ws");
|
|
22
|
+
logger.info("[ws] client connected:", { peerId: peer.id });
|
|
23
|
+
const { registerWsPeer } = usePeerManager();
|
|
24
|
+
registerWsPeer(peer);
|
|
25
|
+
peerContexts.set(peer, {
|
|
26
|
+
subscriptions: /* @__PURE__ */ new Map()
|
|
27
|
+
});
|
|
28
|
+
safeSend(peer, {
|
|
29
|
+
type: "connected",
|
|
30
|
+
timestamp: Date.now()
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
async message(peer, message) {
|
|
34
|
+
const logger = useNventLogger("api-triggers-ws");
|
|
35
|
+
const context = peerContexts.get(peer);
|
|
36
|
+
if (!context) {
|
|
37
|
+
logger.error("[ws] no context for peer:", { peerId: peer.id });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let data;
|
|
41
|
+
try {
|
|
42
|
+
data = JSON.parse(message.text());
|
|
43
|
+
} catch {
|
|
44
|
+
safeSend(peer, {
|
|
45
|
+
type: "error",
|
|
46
|
+
message: "Invalid JSON"
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const { type, triggerName } = data;
|
|
51
|
+
if (type === "subscribe") {
|
|
52
|
+
if (!triggerName) {
|
|
53
|
+
safeSend(peer, {
|
|
54
|
+
type: "error",
|
|
55
|
+
message: "Missing triggerName"
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
let stream;
|
|
60
|
+
let store;
|
|
61
|
+
try {
|
|
62
|
+
stream = useStreamAdapter();
|
|
63
|
+
store = useStoreAdapter();
|
|
64
|
+
} catch (err) {
|
|
65
|
+
logger.error("[ws] Adapters not initialized yet:", { error: err });
|
|
66
|
+
safeSend(peer, {
|
|
67
|
+
type: "error",
|
|
68
|
+
message: "Server initializing, please retry"
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const existingHandle = context.subscriptions.get(triggerName);
|
|
73
|
+
if (existingHandle) {
|
|
74
|
+
try {
|
|
75
|
+
if (typeof existingHandle === "function") {
|
|
76
|
+
await existingHandle();
|
|
77
|
+
} else {
|
|
78
|
+
await existingHandle.unsubscribe();
|
|
79
|
+
}
|
|
80
|
+
} catch (err) {
|
|
81
|
+
logger.error("[ws] error unsubscribing:", { error: err });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const { StoreSubjects, StreamTopics } = useStreamTopics();
|
|
85
|
+
const streamTopic = StreamTopics.triggerEvents(triggerName);
|
|
86
|
+
const storeSubject = StoreSubjects.triggerStream(triggerName);
|
|
87
|
+
const handle = await stream.subscribe(streamTopic, async (message2) => {
|
|
88
|
+
const event = message2.data?.event;
|
|
89
|
+
if (!event) {
|
|
90
|
+
logger.warn("[ws] Received message without event data:", message2);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
safeSend(peer, {
|
|
94
|
+
type: "event",
|
|
95
|
+
triggerName,
|
|
96
|
+
event
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
context.subscriptions.set(triggerName, handle);
|
|
100
|
+
try {
|
|
101
|
+
const historicalEvents = await store.stream.read(storeSubject, {
|
|
102
|
+
limit: 100,
|
|
103
|
+
order: "asc"
|
|
104
|
+
// Forward order to match flows
|
|
105
|
+
});
|
|
106
|
+
safeSend(peer, {
|
|
107
|
+
type: "history",
|
|
108
|
+
triggerName,
|
|
109
|
+
events: historicalEvents || []
|
|
110
|
+
});
|
|
111
|
+
} catch (err) {
|
|
112
|
+
logger.error("[ws] error sending history:", { error: err });
|
|
113
|
+
safeSend(peer, {
|
|
114
|
+
type: "error",
|
|
115
|
+
message: "Failed to load history"
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
safeSend(peer, {
|
|
119
|
+
type: "subscribed",
|
|
120
|
+
triggerName
|
|
121
|
+
});
|
|
122
|
+
} else if (type === "unsubscribe") {
|
|
123
|
+
if (!triggerName) {
|
|
124
|
+
safeSend(peer, {
|
|
125
|
+
type: "error",
|
|
126
|
+
message: "Missing triggerName"
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const handle = context.subscriptions.get(triggerName);
|
|
131
|
+
if (handle) {
|
|
132
|
+
try {
|
|
133
|
+
if (typeof handle === "function") {
|
|
134
|
+
await handle();
|
|
135
|
+
} else {
|
|
136
|
+
await handle.unsubscribe();
|
|
137
|
+
}
|
|
138
|
+
context.subscriptions.delete(triggerName);
|
|
139
|
+
safeSend(peer, {
|
|
140
|
+
type: "unsubscribed",
|
|
141
|
+
triggerName
|
|
142
|
+
});
|
|
143
|
+
} catch (err) {
|
|
144
|
+
logger.error("[ws] error unsubscribing:", { error: err });
|
|
145
|
+
safeSend(peer, {
|
|
146
|
+
type: "error",
|
|
147
|
+
message: "Failed to unsubscribe"
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} else if (type === "subscribe.stats") {
|
|
152
|
+
const statsKey = "stats";
|
|
153
|
+
const existingUnsub = context.subscriptions.get(statsKey);
|
|
154
|
+
if (existingUnsub) {
|
|
155
|
+
safeSend(peer, {
|
|
156
|
+
type: "error",
|
|
157
|
+
message: "Already subscribed to stats"
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const stream = useStreamAdapter();
|
|
163
|
+
const { StreamTopics } = useStreamTopics();
|
|
164
|
+
const topic = StreamTopics.triggerStats();
|
|
165
|
+
const handle = await stream.subscribe(topic, (message2) => {
|
|
166
|
+
safeSend(peer, {
|
|
167
|
+
type: "trigger.stats.update",
|
|
168
|
+
data: message2,
|
|
169
|
+
timestamp: Date.now()
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
const unsub = async () => {
|
|
173
|
+
try {
|
|
174
|
+
await handle.unsubscribe();
|
|
175
|
+
} catch (err) {
|
|
176
|
+
logger.error("[ws] error in stats unsub:", { error: err });
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
context.subscriptions.set(statsKey, unsub);
|
|
180
|
+
try {
|
|
181
|
+
const trigger = useTrigger();
|
|
182
|
+
const allTriggers = trigger.getAllTriggers();
|
|
183
|
+
const { getSubscribedFlows, getTriggerStats } = trigger;
|
|
184
|
+
if (allTriggers && allTriggers.length > 0) {
|
|
185
|
+
for (const triggerEntry of allTriggers) {
|
|
186
|
+
const subscribedFlows = getSubscribedFlows(triggerEntry.name);
|
|
187
|
+
const stats = await getTriggerStats(triggerEntry.name);
|
|
188
|
+
safeSend(peer, {
|
|
189
|
+
type: "trigger.stats.initial",
|
|
190
|
+
data: {
|
|
191
|
+
id: triggerEntry.name,
|
|
192
|
+
metadata: {
|
|
193
|
+
"name": triggerEntry.name,
|
|
194
|
+
"type": triggerEntry.type,
|
|
195
|
+
"scope": triggerEntry.scope,
|
|
196
|
+
"displayName": triggerEntry.displayName,
|
|
197
|
+
"description": triggerEntry.description,
|
|
198
|
+
"source": triggerEntry.source,
|
|
199
|
+
"status": triggerEntry.status || "active",
|
|
200
|
+
"registeredAt": triggerEntry.registeredAt,
|
|
201
|
+
"lastActivityAt": triggerEntry.lastActivityAt,
|
|
202
|
+
"webhook": triggerEntry.webhook,
|
|
203
|
+
"schedule": triggerEntry.schedule,
|
|
204
|
+
"config": triggerEntry.config,
|
|
205
|
+
"subscribedFlows": subscribedFlows,
|
|
206
|
+
"subscriptionCount": subscribedFlows.length,
|
|
207
|
+
"stats.totalFires": stats?.totalFires || 0,
|
|
208
|
+
"stats.totalFlowsStarted": stats?.totalFlowsStarted || 0,
|
|
209
|
+
"stats.activeSubscribers": stats?.activeSubscribers || subscribedFlows.length,
|
|
210
|
+
"stats.lastFiredAt": stats?.lastFiredAt
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
timestamp: Date.now()
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} catch (err) {
|
|
218
|
+
logger.error("[ws] error fetching initial trigger stats:", { error: err });
|
|
219
|
+
}
|
|
220
|
+
safeSend(peer, {
|
|
221
|
+
type: "stats.subscribed",
|
|
222
|
+
timestamp: Date.now()
|
|
223
|
+
});
|
|
224
|
+
} catch (err) {
|
|
225
|
+
logger.error("[ws] error subscribing to stats:", { error: err });
|
|
226
|
+
safeSend(peer, {
|
|
227
|
+
type: "error",
|
|
228
|
+
message: "Failed to subscribe to stats"
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
} else if (type === "unsubscribe.stats") {
|
|
232
|
+
const statsKey = "stats";
|
|
233
|
+
const unsub = context.subscriptions.get(statsKey);
|
|
234
|
+
if (unsub) {
|
|
235
|
+
try {
|
|
236
|
+
if (typeof unsub === "function") {
|
|
237
|
+
await unsub();
|
|
238
|
+
} else {
|
|
239
|
+
await unsub.unsubscribe();
|
|
240
|
+
}
|
|
241
|
+
context.subscriptions.delete(statsKey);
|
|
242
|
+
safeSend(peer, {
|
|
243
|
+
type: "stats.unsubscribed",
|
|
244
|
+
timestamp: Date.now()
|
|
245
|
+
});
|
|
246
|
+
} catch (err) {
|
|
247
|
+
logger.error("[ws] error unsubscribing from stats:", { error: err });
|
|
248
|
+
safeSend(peer, {
|
|
249
|
+
type: "error",
|
|
250
|
+
message: "Failed to unsubscribe from stats"
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} else if (type === "ping") {
|
|
255
|
+
safeSend(peer, {
|
|
256
|
+
type: "pong",
|
|
257
|
+
timestamp: Date.now()
|
|
258
|
+
});
|
|
259
|
+
} else {
|
|
260
|
+
safeSend(peer, {
|
|
261
|
+
type: "error",
|
|
262
|
+
message: `Unknown message type: ${type}`
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
async close(peer, event) {
|
|
267
|
+
const logger = useNventLogger("api-triggers-ws");
|
|
268
|
+
const isNormalClosure = event?.code === 1e3 || event?.code === 1001;
|
|
269
|
+
if (!isNormalClosure) {
|
|
270
|
+
logger.info("[ws] client disconnected:", { peerId: peer.id, code: event?.code, reason: event?.reason });
|
|
271
|
+
}
|
|
272
|
+
const { unregisterWsPeer } = usePeerManager();
|
|
273
|
+
unregisterWsPeer(peer);
|
|
274
|
+
const context = peerContexts.get(peer);
|
|
275
|
+
if (context) {
|
|
276
|
+
for (const handleOrUnsub of Array.from(context.subscriptions.values())) {
|
|
277
|
+
try {
|
|
278
|
+
if (typeof handleOrUnsub === "function") {
|
|
279
|
+
await handleOrUnsub();
|
|
280
|
+
} else {
|
|
281
|
+
await handleOrUnsub.unsubscribe();
|
|
282
|
+
}
|
|
283
|
+
} catch (err) {
|
|
284
|
+
if (!isNormalClosure) {
|
|
285
|
+
logger.error("[ws] error unsubscribing on close:", { error: err });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
context.subscriptions.clear();
|
|
290
|
+
peerContexts.delete(peer);
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
async error(peer, error) {
|
|
294
|
+
const logger = useNventLogger("api-triggers-ws");
|
|
295
|
+
logger.error("[ws] error for peer:", { peerId: peer.id, error });
|
|
296
|
+
const { unregisterWsPeer } = usePeerManager();
|
|
297
|
+
unregisterWsPeer(peer);
|
|
298
|
+
const context = peerContexts.get(peer);
|
|
299
|
+
if (context) {
|
|
300
|
+
for (const handleOrUnsub of Array.from(context.subscriptions.values())) {
|
|
301
|
+
try {
|
|
302
|
+
if (typeof handleOrUnsub === "function") {
|
|
303
|
+
await handleOrUnsub();
|
|
304
|
+
} else {
|
|
305
|
+
await handleOrUnsub.unsubscribe();
|
|
306
|
+
}
|
|
307
|
+
} catch (err) {
|
|
308
|
+
logger.error("[ws] error unsubscribing on error:", { error: err });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
context.subscriptions.clear();
|
|
312
|
+
peerContexts.delete(peer);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nvent-addon/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "nvent app module for Nuxt.js",
|
|
5
5
|
"repository": "DevJoghurt/nvent",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,29 +19,29 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "nuxt-module-build build",
|
|
21
21
|
"dev": "nuxt-module-build build --stub",
|
|
22
|
-
"prepack": "pnpm build"
|
|
23
|
-
"test": "vitest"
|
|
22
|
+
"prepack": "pnpm build"
|
|
24
23
|
},
|
|
25
24
|
"dependencies": {
|
|
26
|
-
"@iconify-json/devicon": "^1.2.
|
|
25
|
+
"@iconify-json/devicon": "^1.2.51",
|
|
27
26
|
"@iconify-json/heroicons": "1.2.3",
|
|
28
|
-
"@iconify-json/lucide": "^1.2.
|
|
27
|
+
"@iconify-json/lucide": "^1.2.76",
|
|
29
28
|
"@nuxt/kit": "4.2.1",
|
|
29
|
+
"@nuxt/ui": "4.2.1",
|
|
30
30
|
"@vue-flow/background": "^1.3.2",
|
|
31
31
|
"@vue-flow/controls": "^1.1.3",
|
|
32
|
-
"@vue-flow/core": "^1.
|
|
32
|
+
"@vue-flow/core": "^1.48.0",
|
|
33
33
|
"@vue-flow/minimap": "^1.5.4",
|
|
34
34
|
"defu": "^6.1.4",
|
|
35
35
|
"json-editor-vue": "^0.18.1",
|
|
36
36
|
"nuxt": "4.2.1",
|
|
37
37
|
"pathe": "^2.0.3",
|
|
38
|
-
"zod": "^4.1.
|
|
38
|
+
"zod": "^4.1.13"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@nuxt/module-builder": "^1.0.2",
|
|
42
42
|
"@nuxt/schema": "4.2.1",
|
|
43
43
|
"@types/node": "^24.10.1",
|
|
44
44
|
"typescript": "latest",
|
|
45
|
-
"vitest": "^4.0.
|
|
45
|
+
"vitest": "^4.0.14"
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<UModal v-model:open="isOpen">
|
|
3
|
-
<template #header>
|
|
4
|
-
<div class="flex items-center justify-between">
|
|
5
|
-
<div>
|
|
6
|
-
<h3 class="text-lg font-semibold">
|
|
7
|
-
Schedule Flow
|
|
8
|
-
</h3>
|
|
9
|
-
<p class="text-sm text-gray-500 mt-1">
|
|
10
|
-
{{ flowName }}
|
|
11
|
-
</p>
|
|
12
|
-
</div>
|
|
13
|
-
</div>
|
|
14
|
-
</template>
|
|
15
|
-
<template #body>
|
|
16
|
-
<div class="space-y-4">
|
|
17
|
-
<!-- Schedule Type -->
|
|
18
|
-
<div>
|
|
19
|
-
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
|
20
|
-
Schedule Type
|
|
21
|
-
</label>
|
|
22
|
-
<UTabs
|
|
23
|
-
v-model="scheduleType"
|
|
24
|
-
:items="typeOptions"
|
|
25
|
-
size="sm"
|
|
26
|
-
variant="pill"
|
|
27
|
-
color="neutral"
|
|
28
|
-
/>
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<!-- Cron Pattern -->
|
|
32
|
-
<div v-if="scheduleType === 'cron'">
|
|
33
|
-
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
|
34
|
-
Pattern
|
|
35
|
-
</label>
|
|
36
|
-
<USelectMenu
|
|
37
|
-
v-model="selectedPreset"
|
|
38
|
-
:items="cronPresets"
|
|
39
|
-
class="w-full"
|
|
40
|
-
placeholder="Select preset or custom"
|
|
41
|
-
/>
|
|
42
|
-
<UInput
|
|
43
|
-
v-if="selectedPreset?.value === 'custom'"
|
|
44
|
-
v-model="customCron"
|
|
45
|
-
placeholder="0 2 * * *"
|
|
46
|
-
class="mt-2 w-full"
|
|
47
|
-
/>
|
|
48
|
-
</div>
|
|
49
|
-
|
|
50
|
-
<!-- Delay -->
|
|
51
|
-
<div v-else-if="scheduleType === 'delay'">
|
|
52
|
-
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
|
53
|
-
Delay
|
|
54
|
-
</label>
|
|
55
|
-
<div class="flex gap-2">
|
|
56
|
-
<UInput
|
|
57
|
-
v-model="delayValue"
|
|
58
|
-
type="number"
|
|
59
|
-
placeholder="5"
|
|
60
|
-
class="flex-1"
|
|
61
|
-
/>
|
|
62
|
-
<USelectMenu
|
|
63
|
-
v-model="delayUnit"
|
|
64
|
-
:items="delayUnits"
|
|
65
|
-
class="w-32"
|
|
66
|
-
/>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
|
|
70
|
-
<!-- Input Data -->
|
|
71
|
-
<div>
|
|
72
|
-
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
|
73
|
-
Input Data (JSON)
|
|
74
|
-
</label>
|
|
75
|
-
<UTextarea
|
|
76
|
-
v-model="inputJson"
|
|
77
|
-
placeholder="{ "key": "value" }"
|
|
78
|
-
:rows="4"
|
|
79
|
-
class="font-mono text-sm w-full"
|
|
80
|
-
/>
|
|
81
|
-
<p
|
|
82
|
-
v-if="jsonError"
|
|
83
|
-
class="text-xs text-red-500 mt-1"
|
|
84
|
-
>
|
|
85
|
-
{{ jsonError }}
|
|
86
|
-
</p>
|
|
87
|
-
</div>
|
|
88
|
-
|
|
89
|
-
<!-- Description -->
|
|
90
|
-
<div>
|
|
91
|
-
<label class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block">
|
|
92
|
-
Description (optional)
|
|
93
|
-
</label>
|
|
94
|
-
<UInput
|
|
95
|
-
v-model="description"
|
|
96
|
-
class="w-full"
|
|
97
|
-
placeholder="Daily cleanup job"
|
|
98
|
-
/>
|
|
99
|
-
</div>
|
|
100
|
-
</div>
|
|
101
|
-
</template>
|
|
102
|
-
<template #footer>
|
|
103
|
-
<div class="flex justify-end gap-2">
|
|
104
|
-
<UButton
|
|
105
|
-
color="neutral"
|
|
106
|
-
variant="ghost"
|
|
107
|
-
@click="isOpen = false"
|
|
108
|
-
>
|
|
109
|
-
Cancel
|
|
110
|
-
</UButton>
|
|
111
|
-
<UButton
|
|
112
|
-
color="primary"
|
|
113
|
-
:loading="isSubmitting"
|
|
114
|
-
:disabled="!canSubmit"
|
|
115
|
-
@click="handleSubmit"
|
|
116
|
-
>
|
|
117
|
-
Schedule Flow
|
|
118
|
-
</UButton>
|
|
119
|
-
</div>
|
|
120
|
-
</template>
|
|
121
|
-
</UModal>
|
|
122
|
-
</template>
|
|
123
|
-
|
|
124
|
-
<script setup>
|
|
125
|
-
import { ref, computed, watch } from "#imports";
|
|
126
|
-
import { UModal, UButton, UTabs, USelectMenu, UInput, UTextarea } from "#components";
|
|
127
|
-
const props = defineProps({
|
|
128
|
-
flowName: { type: String, required: true }
|
|
129
|
-
});
|
|
130
|
-
const emit = defineEmits(["scheduled"]);
|
|
131
|
-
const isOpen = defineModel({ type: Boolean, ...{ default: false } });
|
|
132
|
-
const scheduleType = ref("cron");
|
|
133
|
-
const typeOptions = [
|
|
134
|
-
{ label: "Recurring (Cron)", value: "cron" },
|
|
135
|
-
{ label: "One-time (Delay)", value: "delay" }
|
|
136
|
-
];
|
|
137
|
-
const cronPresets = [
|
|
138
|
-
{ label: "Every minute", value: "* * * * *" },
|
|
139
|
-
{ label: "Every 5 minutes", value: "*/5 * * * *" },
|
|
140
|
-
{ label: "Every hour", value: "0 * * * *" },
|
|
141
|
-
{ label: "Daily at 2 AM", value: "0 2 * * *" },
|
|
142
|
-
{ label: "Daily at noon", value: "0 12 * * *" },
|
|
143
|
-
{ label: "Weekly (Monday 9 AM)", value: "0 9 * * 1" },
|
|
144
|
-
{ label: "Monthly (1st at midnight)", value: "0 0 1 * *" },
|
|
145
|
-
{ label: "Custom", value: "custom" }
|
|
146
|
-
];
|
|
147
|
-
const selectedPreset = ref(cronPresets[3]);
|
|
148
|
-
const customCron = ref("");
|
|
149
|
-
const delayValue = ref("5");
|
|
150
|
-
const delayUnit = ref({ label: "Minutes", value: 6e4 });
|
|
151
|
-
const delayUnits = [
|
|
152
|
-
{ label: "Seconds", value: 1e3 },
|
|
153
|
-
{ label: "Minutes", value: 6e4 },
|
|
154
|
-
{ label: "Hours", value: 36e5 },
|
|
155
|
-
{ label: "Days", value: 864e5 }
|
|
156
|
-
];
|
|
157
|
-
const inputJson = ref("");
|
|
158
|
-
const description = ref("");
|
|
159
|
-
const isSubmitting = ref(false);
|
|
160
|
-
const jsonError = computed(() => {
|
|
161
|
-
if (!inputJson.value)
|
|
162
|
-
return null;
|
|
163
|
-
try {
|
|
164
|
-
JSON.parse(inputJson.value);
|
|
165
|
-
return null;
|
|
166
|
-
} catch (e) {
|
|
167
|
-
return `Invalid JSON: ${e.message}`;
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
const canSubmit = computed(() => {
|
|
171
|
-
if (jsonError.value)
|
|
172
|
-
return false;
|
|
173
|
-
if (scheduleType.value === "cron") {
|
|
174
|
-
if (selectedPreset.value?.value === "custom") {
|
|
175
|
-
return customCron.value.trim().length > 0;
|
|
176
|
-
}
|
|
177
|
-
return selectedPreset.value?.value != null;
|
|
178
|
-
} else {
|
|
179
|
-
const num = Number.parseInt(delayValue.value);
|
|
180
|
-
return !Number.isNaN(num) && num > 0;
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
const handleSubmit = async () => {
|
|
184
|
-
isSubmitting.value = true;
|
|
185
|
-
try {
|
|
186
|
-
let input = {};
|
|
187
|
-
if (inputJson.value) {
|
|
188
|
-
input = JSON.parse(inputJson.value);
|
|
189
|
-
}
|
|
190
|
-
const body = {
|
|
191
|
-
input,
|
|
192
|
-
metadata: {
|
|
193
|
-
description: description.value || void 0
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
if (scheduleType.value === "cron") {
|
|
197
|
-
body.cron = selectedPreset.value?.value === "custom" ? customCron.value : selectedPreset.value?.value;
|
|
198
|
-
} else {
|
|
199
|
-
const num = Number.parseInt(delayValue.value);
|
|
200
|
-
body.delay = num * delayUnit.value.value;
|
|
201
|
-
}
|
|
202
|
-
await $fetch(`/api/_flows/${props.flowName}/schedule`, {
|
|
203
|
-
method: "POST",
|
|
204
|
-
body
|
|
205
|
-
});
|
|
206
|
-
emit("scheduled");
|
|
207
|
-
isOpen.value = false;
|
|
208
|
-
inputJson.value = "";
|
|
209
|
-
description.value = "";
|
|
210
|
-
customCron.value = "";
|
|
211
|
-
delayValue.value = "5";
|
|
212
|
-
} finally {
|
|
213
|
-
isSubmitting.value = false;
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
watch(isOpen, (newVal) => {
|
|
217
|
-
if (!newVal) {
|
|
218
|
-
scheduleType.value = "cron";
|
|
219
|
-
selectedPreset.value = cronPresets[3];
|
|
220
|
-
inputJson.value = "";
|
|
221
|
-
description.value = "";
|
|
222
|
-
customCron.value = "";
|
|
223
|
-
delayValue.value = "5";
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
</script>
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="space-y-4">
|
|
3
|
-
<div v-if="!schedules || schedules.length === 0">
|
|
4
|
-
<UAlert
|
|
5
|
-
color="info"
|
|
6
|
-
title="No schedules configured"
|
|
7
|
-
variant="subtle"
|
|
8
|
-
icon="i-lucide-info"
|
|
9
|
-
description="Create a schedule to run this flow automatically"
|
|
10
|
-
/>
|
|
11
|
-
</div>
|
|
12
|
-
|
|
13
|
-
<div
|
|
14
|
-
v-for="schedule in schedules"
|
|
15
|
-
:key="schedule.id"
|
|
16
|
-
class="border border-gray-200 dark:border-gray-800 rounded-lg p-4"
|
|
17
|
-
>
|
|
18
|
-
<div class="flex items-start justify-between">
|
|
19
|
-
<div class="flex-1">
|
|
20
|
-
<div class="flex items-center gap-2 mb-2">
|
|
21
|
-
<UIcon
|
|
22
|
-
name="i-lucide-clock"
|
|
23
|
-
class="text-blue-500"
|
|
24
|
-
/>
|
|
25
|
-
<span class="font-medium text-sm">
|
|
26
|
-
{{ schedule.schedule.cron || "One-time delay" }}
|
|
27
|
-
</span>
|
|
28
|
-
</div>
|
|
29
|
-
<div class="text-xs text-gray-500 space-y-1">
|
|
30
|
-
<div>
|
|
31
|
-
<span class="font-medium">Next run:</span>
|
|
32
|
-
{{ formatDate(schedule.nextRun) }}
|
|
33
|
-
</div>
|
|
34
|
-
<div v-if="schedule.metadata?.description">
|
|
35
|
-
<span class="font-medium">Description:</span>
|
|
36
|
-
{{ schedule.metadata.description }}
|
|
37
|
-
</div>
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
40
|
-
<UButton
|
|
41
|
-
icon="i-lucide-trash-2"
|
|
42
|
-
color="error"
|
|
43
|
-
variant="ghost"
|
|
44
|
-
size="xs"
|
|
45
|
-
:loading="deletingId === schedule.id"
|
|
46
|
-
@click="handleDelete(schedule.id)"
|
|
47
|
-
/>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
</template>
|
|
52
|
-
|
|
53
|
-
<script setup>
|
|
54
|
-
import { ref, onMounted } from "#imports";
|
|
55
|
-
import { UAlert, UButton, UIcon } from "#components";
|
|
56
|
-
const props = defineProps({
|
|
57
|
-
flowName: { type: String, required: true }
|
|
58
|
-
});
|
|
59
|
-
const emit = defineEmits(["updated"]);
|
|
60
|
-
const schedules = ref([]);
|
|
61
|
-
const deletingId = ref(null);
|
|
62
|
-
const loadSchedules = async () => {
|
|
63
|
-
try {
|
|
64
|
-
const data = await $fetch(`/api/_flows/${props.flowName}/schedules`);
|
|
65
|
-
schedules.value = data;
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.error("Failed to load schedules:", error);
|
|
68
|
-
schedules.value = [];
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
const handleDelete = async (id) => {
|
|
72
|
-
deletingId.value = id;
|
|
73
|
-
try {
|
|
74
|
-
await $fetch(`/api/_flows/${props.flowName}/schedules/${id}`, {
|
|
75
|
-
method: "DELETE"
|
|
76
|
-
});
|
|
77
|
-
await loadSchedules();
|
|
78
|
-
emit("updated");
|
|
79
|
-
} catch (error) {
|
|
80
|
-
console.error("Failed to delete schedule:", error);
|
|
81
|
-
} finally {
|
|
82
|
-
deletingId.value = null;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
const formatDate = (date) => {
|
|
86
|
-
if (!date)
|
|
87
|
-
return "N/A";
|
|
88
|
-
return new Date(date).toLocaleString("en-US", {
|
|
89
|
-
dateStyle: "medium",
|
|
90
|
-
timeStyle: "short"
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
onMounted(() => {
|
|
94
|
-
loadSchedules();
|
|
95
|
-
});
|
|
96
|
-
defineExpose({
|
|
97
|
-
loadSchedules
|
|
98
|
-
});
|
|
99
|
-
</script>
|