@mongoosejs/studio 0.2.9 → 0.2.11
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/backend/actions/Model/executeDocumentScript.js +61 -0
- package/backend/actions/Model/index.js +2 -0
- package/backend/actions/Model/listModels.js +36 -1
- package/backend/actions/Model/streamDocumentChanges.js +123 -0
- package/backend/actions/Task/cancelTask.js +24 -0
- package/backend/actions/Task/createTask.js +33 -0
- package/backend/actions/Task/getTasks.js +62 -0
- package/backend/actions/Task/index.js +7 -0
- package/backend/actions/Task/rescheduleTask.js +39 -0
- package/backend/actions/Task/runTask.js +25 -0
- package/backend/actions/index.js +1 -0
- package/backend/authorize.js +2 -0
- package/backend/index.js +7 -1
- package/eslint.config.js +4 -1
- package/express.js +4 -2
- package/frontend/public/app.js +14590 -13420
- package/frontend/public/tw.css +357 -4
- package/frontend/src/api.js +100 -0
- package/frontend/src/dashboard-result/dashboard-document/dashboard-document.html +4 -5
- package/frontend/src/dashboard-result/dashboard-document/dashboard-document.js +13 -14
- package/frontend/src/document/document.html +80 -0
- package/frontend/src/document/document.js +206 -19
- package/frontend/src/document/execute-script/execute-script.css +35 -0
- package/frontend/src/document/execute-script/execute-script.html +67 -0
- package/frontend/src/document/execute-script/execute-script.js +142 -0
- package/frontend/src/index.js +48 -4
- package/frontend/src/navbar/navbar.html +15 -2
- package/frontend/src/navbar/navbar.js +11 -0
- package/frontend/src/routes.js +13 -5
- package/frontend/src/tasks/task-details/task-details.html +284 -0
- package/frontend/src/tasks/task-details/task-details.js +182 -0
- package/frontend/src/tasks/tasks.css +0 -0
- package/frontend/src/tasks/tasks.html +220 -0
- package/frontend/src/tasks/tasks.js +372 -0
- package/package.json +4 -1
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const template = require('./tasks.html');
|
|
4
|
+
const api = require('../api');
|
|
5
|
+
|
|
6
|
+
module.exports = app => app.component('tasks', {
|
|
7
|
+
data: () => ({
|
|
8
|
+
status: 'init',
|
|
9
|
+
tasks: [],
|
|
10
|
+
groupedTasks: {},
|
|
11
|
+
selectedRange: 'today',
|
|
12
|
+
start: null,
|
|
13
|
+
end: null,
|
|
14
|
+
dateFilters: [
|
|
15
|
+
{ value: 'all', label: 'All Time' },
|
|
16
|
+
{ value: 'today', label: 'Today' },
|
|
17
|
+
{ value: 'yesterday', label: 'Yesterday' },
|
|
18
|
+
{ value: 'thisWeek', label: 'This Week' },
|
|
19
|
+
{ value: 'lastWeek', label: 'Last Week' },
|
|
20
|
+
{ value: 'thisMonth', label: 'This Month' },
|
|
21
|
+
{ value: 'lastMonth', label: 'Last Month' }
|
|
22
|
+
],
|
|
23
|
+
selectedStatus: 'all',
|
|
24
|
+
statusFilters: [
|
|
25
|
+
{ label: 'All', value: 'all' },
|
|
26
|
+
{ label: 'Pending', value: 'pending' },
|
|
27
|
+
// { label: 'In Progress', value: 'in_progress' },
|
|
28
|
+
{ label: 'Succeeded', value: 'succeeded' },
|
|
29
|
+
{ label: 'Failed', value: 'failed' },
|
|
30
|
+
{ label: 'Cancelled', value: 'cancelled' }
|
|
31
|
+
],
|
|
32
|
+
searchQuery: '',
|
|
33
|
+
searchTimeout: null,
|
|
34
|
+
// Task details view state
|
|
35
|
+
showTaskDetails: false,
|
|
36
|
+
selectedTaskGroup: null,
|
|
37
|
+
taskDetailsFilter: null,
|
|
38
|
+
// Create task modal state
|
|
39
|
+
showCreateTaskModal: false,
|
|
40
|
+
newTask: {
|
|
41
|
+
name: '',
|
|
42
|
+
scheduledAt: '',
|
|
43
|
+
parameters: '',
|
|
44
|
+
repeatInterval: ''
|
|
45
|
+
},
|
|
46
|
+
parametersEditor: null
|
|
47
|
+
}),
|
|
48
|
+
methods: {
|
|
49
|
+
async getTasks() {
|
|
50
|
+
const params = {};
|
|
51
|
+
if (this.selectedStatus == 'all') {
|
|
52
|
+
params.status = null;
|
|
53
|
+
} else {
|
|
54
|
+
params.status = this.selectedStatus;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (this.start && this.end) {
|
|
58
|
+
params.start = this.start;
|
|
59
|
+
params.end = this.end;
|
|
60
|
+
} else if (this.start) {
|
|
61
|
+
params.start = this.start;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (this.searchQuery.trim()) {
|
|
65
|
+
params.name = this.searchQuery.trim();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const { tasks, groupedTasks } = await api.Task.getTasks(params);
|
|
69
|
+
this.tasks = tasks;
|
|
70
|
+
this.groupedTasks = groupedTasks;
|
|
71
|
+
},
|
|
72
|
+
openTaskGroupDetails(group) {
|
|
73
|
+
this.selectedTaskGroup = group;
|
|
74
|
+
this.showTaskDetails = true;
|
|
75
|
+
},
|
|
76
|
+
openTaskGroupDetailsWithFilter(group, status) {
|
|
77
|
+
// Create a filtered version of the task group with only the specified status
|
|
78
|
+
const filteredGroup = {
|
|
79
|
+
...group,
|
|
80
|
+
tasks: group.tasks.filter(task => task.status === status),
|
|
81
|
+
filteredStatus: status
|
|
82
|
+
};
|
|
83
|
+
this.selectedTaskGroup = filteredGroup;
|
|
84
|
+
this.taskDetailsFilter = status;
|
|
85
|
+
this.showTaskDetails = true;
|
|
86
|
+
},
|
|
87
|
+
async onTaskCancelled() {
|
|
88
|
+
// Refresh the task data when a task is cancelled
|
|
89
|
+
await this.getTasks();
|
|
90
|
+
},
|
|
91
|
+
hideTaskDetails() {
|
|
92
|
+
this.showTaskDetails = false;
|
|
93
|
+
this.selectedTaskGroup = null;
|
|
94
|
+
this.taskDetailsFilter = null;
|
|
95
|
+
},
|
|
96
|
+
async onTaskCreated() {
|
|
97
|
+
// Refresh the task data when a new task is created
|
|
98
|
+
await this.getTasks();
|
|
99
|
+
},
|
|
100
|
+
formatDate(dateString) {
|
|
101
|
+
if (!dateString) return 'N/A';
|
|
102
|
+
return new Date(dateString).toLocaleString();
|
|
103
|
+
},
|
|
104
|
+
async createTask() {
|
|
105
|
+
try {
|
|
106
|
+
let parameters = {};
|
|
107
|
+
const parametersText = this.parametersEditor ? this.parametersEditor.getValue() : '';
|
|
108
|
+
if (parametersText.trim()) {
|
|
109
|
+
try {
|
|
110
|
+
parameters = JSON.parse(parametersText);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
console.error('Invalid JSON in parameters field:', e);
|
|
113
|
+
this.$toast.create({
|
|
114
|
+
title: 'Invalid JSON Parameters',
|
|
115
|
+
text: 'Please check your JSON syntax in the parameters field',
|
|
116
|
+
type: 'error',
|
|
117
|
+
timeout: 5000,
|
|
118
|
+
positionClass: 'bottomRight'
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Validate repeat interval
|
|
125
|
+
let repeatInterval = null;
|
|
126
|
+
if (this.newTask.repeatInterval && this.newTask.repeatInterval.trim()) {
|
|
127
|
+
const interval = parseInt(this.newTask.repeatInterval);
|
|
128
|
+
if (isNaN(interval) || interval < 0) {
|
|
129
|
+
console.error('Invalid repeat interval. Must be a positive number.');
|
|
130
|
+
this.$toast.create({
|
|
131
|
+
title: 'Invalid Repeat Interval',
|
|
132
|
+
text: 'Repeat interval must be a positive number (in milliseconds)',
|
|
133
|
+
type: 'error',
|
|
134
|
+
timeout: 5000,
|
|
135
|
+
positionClass: 'bottomRight'
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
repeatInterval = interval;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const taskData = {
|
|
143
|
+
name: this.newTask.name,
|
|
144
|
+
scheduledAt: this.newTask.scheduledAt,
|
|
145
|
+
payload: parameters,
|
|
146
|
+
repeatAfterMS: repeatInterval
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
console.log('Creating task:', taskData);
|
|
150
|
+
await api.Task.createTask(taskData);
|
|
151
|
+
|
|
152
|
+
// Show success message
|
|
153
|
+
this.$toast.create({
|
|
154
|
+
title: 'Task Created Successfully!',
|
|
155
|
+
text: `Task "${taskData.name}" has been scheduled`,
|
|
156
|
+
type: 'success',
|
|
157
|
+
timeout: 3000,
|
|
158
|
+
positionClass: 'bottomRight'
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Close modal (which will reset form)
|
|
162
|
+
this.closeCreateTaskModal();
|
|
163
|
+
|
|
164
|
+
// Refresh the task data
|
|
165
|
+
await this.getTasks();
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error('Error creating task:', error);
|
|
168
|
+
this.$toast.create({
|
|
169
|
+
title: 'Failed to Create Task',
|
|
170
|
+
text: error?.response?.data?.message || error.message || 'An unexpected error occurred',
|
|
171
|
+
type: 'error',
|
|
172
|
+
timeout: 5000,
|
|
173
|
+
positionClass: 'bottomRight'
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
resetCreateTaskForm() {
|
|
178
|
+
this.newTask = {
|
|
179
|
+
name: '',
|
|
180
|
+
scheduledAt: '',
|
|
181
|
+
parameters: '',
|
|
182
|
+
repeatInterval: ''
|
|
183
|
+
};
|
|
184
|
+
if (this.parametersEditor) {
|
|
185
|
+
this.parametersEditor.setValue('');
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
setDefaultCreateTaskValues() {
|
|
189
|
+
// Set default scheduled time to 1 hour from now
|
|
190
|
+
const defaultTime = new Date();
|
|
191
|
+
defaultTime.setHours(defaultTime.getHours() + 1);
|
|
192
|
+
this.newTask.scheduledAt = defaultTime.toISOString().slice(0, 16);
|
|
193
|
+
},
|
|
194
|
+
closeCreateTaskModal() {
|
|
195
|
+
this.showCreateTaskModal = false;
|
|
196
|
+
this.resetCreateTaskForm();
|
|
197
|
+
this.setDefaultCreateTaskValues();
|
|
198
|
+
},
|
|
199
|
+
initializeParametersEditor() {
|
|
200
|
+
if (this.$refs.parametersEditor && !this.parametersEditor) {
|
|
201
|
+
this.parametersEditor = CodeMirror.fromTextArea(this.$refs.parametersEditor, {
|
|
202
|
+
mode: 'javascript',
|
|
203
|
+
lineNumbers: true,
|
|
204
|
+
smartIndent: false,
|
|
205
|
+
theme: 'default'
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
openCreateTaskModal() {
|
|
210
|
+
this.showCreateTaskModal = true;
|
|
211
|
+
this.$nextTick(() => {
|
|
212
|
+
this.initializeParametersEditor();
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
getStatusColor(status) {
|
|
216
|
+
if (status === 'succeeded') {
|
|
217
|
+
// Green (success)
|
|
218
|
+
return 'bg-green-100 text-green-800';
|
|
219
|
+
} else if (status === 'pending') {
|
|
220
|
+
// Yellow (waiting)
|
|
221
|
+
return 'bg-yellow-100 text-yellow-800';
|
|
222
|
+
} else if (status === 'cancelled') {
|
|
223
|
+
// Gray (neutral/aborted)
|
|
224
|
+
return 'bg-gray-100 text-gray-800';
|
|
225
|
+
} else if (status === 'failed') {
|
|
226
|
+
// Red (error)
|
|
227
|
+
return 'bg-red-100 text-red-800';
|
|
228
|
+
} else if (status === 'in_progress') {
|
|
229
|
+
// Blue (active/running)
|
|
230
|
+
return 'bg-blue-100 text-blue-800';
|
|
231
|
+
} else {
|
|
232
|
+
// Default (fallback)
|
|
233
|
+
return 'bg-slate-100 text-slate-800';
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
async resetFilters() {
|
|
237
|
+
this.selectedStatus = 'all';
|
|
238
|
+
this.selectedRange = 'today';
|
|
239
|
+
this.searchQuery = '';
|
|
240
|
+
await this.updateDateRange();
|
|
241
|
+
},
|
|
242
|
+
async setStatusFilter(status) {
|
|
243
|
+
this.selectedStatus = status;
|
|
244
|
+
await this.getTasks();
|
|
245
|
+
},
|
|
246
|
+
async onSearchInput() {
|
|
247
|
+
// Debounce the search to avoid too many API calls
|
|
248
|
+
clearTimeout(this.searchTimeout);
|
|
249
|
+
this.searchTimeout = setTimeout(async() => {
|
|
250
|
+
await this.getTasks();
|
|
251
|
+
}, 300);
|
|
252
|
+
},
|
|
253
|
+
async updateDateRange() {
|
|
254
|
+
const now = new Date();
|
|
255
|
+
let start, end;
|
|
256
|
+
|
|
257
|
+
switch (this.selectedRange) {
|
|
258
|
+
case 'today':
|
|
259
|
+
start = new Date();
|
|
260
|
+
start.setHours(0, 0, 0, 0);
|
|
261
|
+
end = new Date();
|
|
262
|
+
end.setHours(23, 59, 59, 999);
|
|
263
|
+
break;
|
|
264
|
+
case 'yesterday':
|
|
265
|
+
start = new Date();
|
|
266
|
+
start.setDate(start.getDate() - 1);
|
|
267
|
+
start.setHours(0, 0, 0, 0);
|
|
268
|
+
end = new Date();
|
|
269
|
+
break;
|
|
270
|
+
case 'thisWeek':
|
|
271
|
+
start = new Date(now.getTime() - (7 * 86400000));
|
|
272
|
+
start.setHours(0, 0, 0, 0);
|
|
273
|
+
end = new Date();
|
|
274
|
+
end.setHours(23, 59, 59, 999);
|
|
275
|
+
break;
|
|
276
|
+
case 'lastWeek':
|
|
277
|
+
start = new Date(now.getTime() - (14 * 86400000));
|
|
278
|
+
start.setHours(0, 0, 0, 0);
|
|
279
|
+
end = new Date(now.getTime() - (7 * 86400000));
|
|
280
|
+
end.setHours(23, 59, 59, 999);
|
|
281
|
+
break;
|
|
282
|
+
case 'thisMonth':
|
|
283
|
+
start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
284
|
+
end = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
|
|
285
|
+
break;
|
|
286
|
+
case 'lastMonth':
|
|
287
|
+
start = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
|
288
|
+
end = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999);
|
|
289
|
+
break;
|
|
290
|
+
case 'all':
|
|
291
|
+
default:
|
|
292
|
+
this.start = null;
|
|
293
|
+
this.end = null;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
this.start = start;
|
|
298
|
+
this.end = end;
|
|
299
|
+
|
|
300
|
+
await this.getTasks();
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
computed: {
|
|
304
|
+
tasksByName() {
|
|
305
|
+
const groups = {};
|
|
306
|
+
|
|
307
|
+
// Process tasks from groupedTasks to create name-based groups
|
|
308
|
+
Object.entries(this.groupedTasks).forEach(([status, tasks]) => {
|
|
309
|
+
tasks.forEach(task => {
|
|
310
|
+
if (!groups[task.name]) {
|
|
311
|
+
groups[task.name] = {
|
|
312
|
+
name: task.name,
|
|
313
|
+
tasks: [],
|
|
314
|
+
statusCounts: {
|
|
315
|
+
pending: 0,
|
|
316
|
+
succeeded: 0,
|
|
317
|
+
failed: 0,
|
|
318
|
+
cancelled: 0
|
|
319
|
+
},
|
|
320
|
+
totalCount: 0,
|
|
321
|
+
lastRun: null
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
groups[task.name].tasks.push(task);
|
|
326
|
+
groups[task.name].totalCount++;
|
|
327
|
+
|
|
328
|
+
// Count status using the status from groupedTasks
|
|
329
|
+
if (groups[task.name].statusCounts.hasOwnProperty(status)) {
|
|
330
|
+
groups[task.name].statusCounts[status]++;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Track last run time
|
|
334
|
+
const taskTime = new Date(task.scheduledAt || task.createdAt || 0);
|
|
335
|
+
if (!groups[task.name].lastRun || taskTime > new Date(groups[task.name].lastRun)) {
|
|
336
|
+
groups[task.name].lastRun = taskTime;
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Convert to array and sort alphabetically by name
|
|
342
|
+
return Object.values(groups).sort((a, b) => {
|
|
343
|
+
return a.name.localeCompare(b.name);
|
|
344
|
+
});
|
|
345
|
+
},
|
|
346
|
+
succeededCount() {
|
|
347
|
+
return this.groupedTasks.succeeded ? this.groupedTasks.succeeded.length : 0;
|
|
348
|
+
},
|
|
349
|
+
failedCount() {
|
|
350
|
+
return this.groupedTasks.failed ? this.groupedTasks.failed.length : 0;
|
|
351
|
+
},
|
|
352
|
+
cancelledCount() {
|
|
353
|
+
return this.groupedTasks.cancelled ? this.groupedTasks.cancelled.length : 0;
|
|
354
|
+
},
|
|
355
|
+
pendingCount() {
|
|
356
|
+
return this.groupedTasks.pending ? this.groupedTasks.pending.length : 0;
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
mounted: async function() {
|
|
360
|
+
await this.updateDateRange();
|
|
361
|
+
await this.getTasks();
|
|
362
|
+
this.status = 'loaded';
|
|
363
|
+
this.setDefaultCreateTaskValues();
|
|
364
|
+
},
|
|
365
|
+
beforeDestroy() {
|
|
366
|
+
if (this.parametersEditor) {
|
|
367
|
+
this.parametersEditor.toTextArea();
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
template: template
|
|
372
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mongoosejs/studio",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "A Mongoose-native MongoDB UI with schema-aware autocomplete, AI-assisted queries, and dashboards that understand your models - not just your data.",
|
|
5
5
|
"homepage": "https://mongoosestudio.app/",
|
|
6
6
|
"repository": {
|
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"mongoose": "7.x || 8.x || ^9.0.0"
|
|
30
30
|
},
|
|
31
|
+
"optionalPeerDependencies": {
|
|
32
|
+
"@mongoosejs/task": "0.5.x || 0.6.x"
|
|
33
|
+
},
|
|
31
34
|
"devDependencies": {
|
|
32
35
|
"@masteringjs/eslint-config": "0.1.1",
|
|
33
36
|
"axios": "1.2.2",
|