@mongoosejs/studio 0.2.10 → 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 +1 -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 +1 -0
- package/eslint.config.js +1 -0
- package/express.js +1 -0
- package/frontend/public/app.js +14306 -13379
- package/frontend/public/tw.css +311 -4
- package/frontend/src/api.js +40 -0
- package/frontend/src/document/document.html +22 -0
- package/frontend/src/document/document.js +13 -1
- 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 +36 -1
- 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,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
const authorize = require('../../authorize');
|
|
5
|
+
const mongoose = require('mongoose');
|
|
6
|
+
const util = require('util');
|
|
7
|
+
const vm = require('vm');
|
|
8
|
+
|
|
9
|
+
const ExecuteDocumentScriptParams = new Archetype({
|
|
10
|
+
model: {
|
|
11
|
+
$type: 'string',
|
|
12
|
+
$required: true
|
|
13
|
+
},
|
|
14
|
+
documentId: {
|
|
15
|
+
$type: 'string',
|
|
16
|
+
$required: true
|
|
17
|
+
},
|
|
18
|
+
script: {
|
|
19
|
+
$type: 'string',
|
|
20
|
+
$required: true
|
|
21
|
+
},
|
|
22
|
+
roles: {
|
|
23
|
+
$type: ['string']
|
|
24
|
+
}
|
|
25
|
+
}).compile('ExecuteDocumentScriptParams');
|
|
26
|
+
|
|
27
|
+
module.exports = ({ db }) => async function executeDocumentScript(params) {
|
|
28
|
+
const { model, documentId, script, roles } = new ExecuteDocumentScriptParams(params);
|
|
29
|
+
|
|
30
|
+
await authorize('Model.executeDocumentScript', roles);
|
|
31
|
+
|
|
32
|
+
const Model = db.models[model];
|
|
33
|
+
if (Model == null) {
|
|
34
|
+
throw new Error(`Model ${model} not found`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const doc = await Model.findById(documentId).setOptions({ sanitizeFilter: true }).orFail();
|
|
38
|
+
|
|
39
|
+
const logs = [];
|
|
40
|
+
if (!db.Types) {
|
|
41
|
+
db.Types = mongoose.Types;
|
|
42
|
+
}
|
|
43
|
+
const sandbox = { db, mongoose, doc, console: {}, ObjectId: mongoose.Types.ObjectId };
|
|
44
|
+
|
|
45
|
+
sandbox.console.log = function() {
|
|
46
|
+
const args = Array.from(arguments);
|
|
47
|
+
logs.push(args.map(arg => typeof arg === 'object' ? util.inspect(arg) : arg).join(' '));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const context = vm.createContext(sandbox);
|
|
51
|
+
const result = await vm.runInContext(wrappedScript(script), context);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
result,
|
|
55
|
+
logs: logs.join('\n')
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const wrappedScript = script => `(async () => {
|
|
60
|
+
${script}
|
|
61
|
+
})()`;
|
|
@@ -6,6 +6,7 @@ exports.createDocument = require('./createDocument');
|
|
|
6
6
|
exports.deleteDocument = require('./deleteDocument');
|
|
7
7
|
exports.deleteDocuments = require('./deleteDocuments');
|
|
8
8
|
exports.dropIndex = require('./dropIndex');
|
|
9
|
+
exports.executeDocumentScript = require('./executeDocumentScript');
|
|
9
10
|
exports.exportQueryResults = require('./exportQueryResults');
|
|
10
11
|
exports.getDocument = require('./getDocument');
|
|
11
12
|
exports.getDocuments = require('./getDocuments');
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
const mongoose = require('mongoose');
|
|
5
|
+
|
|
6
|
+
const CancelTaskParams = new Archetype({
|
|
7
|
+
taskId: {
|
|
8
|
+
$type: mongoose.Types.ObjectId,
|
|
9
|
+
$required: true
|
|
10
|
+
}
|
|
11
|
+
}).compile('CancelTaskParams');
|
|
12
|
+
|
|
13
|
+
module.exports = ({ db }) => async function cancelTask(params) {
|
|
14
|
+
params = new CancelTaskParams(params);
|
|
15
|
+
const { taskId } = params;
|
|
16
|
+
const { Task } = db.models;
|
|
17
|
+
|
|
18
|
+
const task = await Task.findOne({ _id: taskId }).orFail();
|
|
19
|
+
|
|
20
|
+
const cancelledTask = await Task.cancelTask({ _id: taskId });
|
|
21
|
+
return {
|
|
22
|
+
task: cancelledTask
|
|
23
|
+
};
|
|
24
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
|
|
5
|
+
const CreateTaskParams = new Archetype({
|
|
6
|
+
name: {
|
|
7
|
+
$type: 'string',
|
|
8
|
+
$required: true
|
|
9
|
+
},
|
|
10
|
+
scheduledAt: {
|
|
11
|
+
$type: Date,
|
|
12
|
+
$required: true
|
|
13
|
+
},
|
|
14
|
+
repeatAfterMS: {
|
|
15
|
+
$type: 'number'
|
|
16
|
+
},
|
|
17
|
+
payload: {
|
|
18
|
+
$type: Archetype.Any
|
|
19
|
+
}
|
|
20
|
+
}).compile('CreateTaskParams');
|
|
21
|
+
|
|
22
|
+
module.exports = ({ db }) => async function createTask(params) {
|
|
23
|
+
params = new CreateTaskParams(params);
|
|
24
|
+
|
|
25
|
+
const { name, scheduledAt, payload, repeatAfterMS } = params;
|
|
26
|
+
const { Task } = db.models;
|
|
27
|
+
|
|
28
|
+
const task = await Task.schedule(name, scheduledAt, payload, repeatAfterMS);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
task
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
|
|
5
|
+
const GetTasksParams = new Archetype({
|
|
6
|
+
start: {
|
|
7
|
+
$type: Date
|
|
8
|
+
},
|
|
9
|
+
end: {
|
|
10
|
+
$type: Date
|
|
11
|
+
},
|
|
12
|
+
status: {
|
|
13
|
+
$type: 'string'
|
|
14
|
+
},
|
|
15
|
+
name: {
|
|
16
|
+
$type: 'string'
|
|
17
|
+
}
|
|
18
|
+
}).compile('GetTasksParams');
|
|
19
|
+
|
|
20
|
+
module.exports = ({ db }) => async function getTasks(params) {
|
|
21
|
+
params = new GetTasksParams(params);
|
|
22
|
+
const { start, end, status, name } = params;
|
|
23
|
+
const { Task } = db.models;
|
|
24
|
+
|
|
25
|
+
const filter = {};
|
|
26
|
+
|
|
27
|
+
if (start && end) {
|
|
28
|
+
filter.scheduledAt = { $gte: start, $lt: end };
|
|
29
|
+
} else if (start) {
|
|
30
|
+
filter.scheduledAt = { $gte: start };
|
|
31
|
+
}
|
|
32
|
+
if (status) {
|
|
33
|
+
filter.status = status;
|
|
34
|
+
}
|
|
35
|
+
if (name) {
|
|
36
|
+
filter.name = { $regex: name, $options: 'i' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const tasks = await Task.find(filter);
|
|
40
|
+
|
|
41
|
+
// Define all possible statuses
|
|
42
|
+
const allStatuses = ['pending', 'in_progress', 'succeeded', 'failed', 'cancelled', 'unknown'];
|
|
43
|
+
|
|
44
|
+
// Initialize groupedTasks with all statuses
|
|
45
|
+
const groupedTasks = allStatuses.reduce((groups, status) => {
|
|
46
|
+
groups[status] = [];
|
|
47
|
+
return groups;
|
|
48
|
+
}, {});
|
|
49
|
+
|
|
50
|
+
// Group tasks by status
|
|
51
|
+
tasks.forEach(task => {
|
|
52
|
+
const taskStatus = task.status || 'unknown';
|
|
53
|
+
if (groupedTasks.hasOwnProperty(taskStatus)) {
|
|
54
|
+
groupedTasks[taskStatus].push(task);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
tasks,
|
|
60
|
+
groupedTasks
|
|
61
|
+
};
|
|
62
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
const mongoose = require('mongoose');
|
|
5
|
+
|
|
6
|
+
const RescheduleTaskParams = new Archetype({
|
|
7
|
+
taskId: {
|
|
8
|
+
$type: mongoose.Types.ObjectId,
|
|
9
|
+
$required: true
|
|
10
|
+
},
|
|
11
|
+
scheduledAt: {
|
|
12
|
+
$type: Date,
|
|
13
|
+
$required: true
|
|
14
|
+
}
|
|
15
|
+
}).compile('RescheduleTaskParams');
|
|
16
|
+
|
|
17
|
+
module.exports = ({ db }) => async function rescheduleTask(params) {
|
|
18
|
+
params = new RescheduleTaskParams(params);
|
|
19
|
+
const { taskId, scheduledAt } = params;
|
|
20
|
+
const { Task } = db.models;
|
|
21
|
+
|
|
22
|
+
const task = await Task.findOne({ _id: taskId }).orFail();
|
|
23
|
+
|
|
24
|
+
if (scheduledAt < Date.now()) {
|
|
25
|
+
throw new Error('Cannot reschedule a task for the past');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (task.status != 'pending') {
|
|
29
|
+
throw new Error('Cannot reschedule a task that is not pending');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
task.scheduledAt = scheduledAt;
|
|
33
|
+
|
|
34
|
+
await task.save();
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
task
|
|
38
|
+
};
|
|
39
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Archetype = require('archetype');
|
|
4
|
+
const mongoose = require('mongoose');
|
|
5
|
+
|
|
6
|
+
const RunTaskParams = new Archetype({
|
|
7
|
+
taskId: {
|
|
8
|
+
$type: mongoose.Types.ObjectId,
|
|
9
|
+
$required: true
|
|
10
|
+
}
|
|
11
|
+
}).compile('RunTaskParams');
|
|
12
|
+
|
|
13
|
+
module.exports = ({ db }) => async function runTask(params) {
|
|
14
|
+
params = new RunTaskParams(params);
|
|
15
|
+
const { taskId } = params;
|
|
16
|
+
const { Task } = db.models;
|
|
17
|
+
|
|
18
|
+
const task = await Task.findOne({ _id: taskId }).orFail();
|
|
19
|
+
|
|
20
|
+
const executedTask = await Task.execute(task);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
task: executedTask
|
|
24
|
+
};
|
|
25
|
+
};
|
package/backend/actions/index.js
CHANGED
package/backend/authorize.js
CHANGED
|
@@ -17,6 +17,7 @@ const actionsToRequiredRoles = {
|
|
|
17
17
|
'Model.deleteDocument': ['owner', 'admin', 'member'],
|
|
18
18
|
'Model.deleteDocuments': ['owner', 'admin', 'member'],
|
|
19
19
|
'Model.dropIndex': ['owner', 'admin'],
|
|
20
|
+
'Model.executeDocumentScript': ['owner', 'admin', 'member'],
|
|
20
21
|
'Model.exportQueryResults': ['owner', 'admin', 'member', 'readonly'],
|
|
21
22
|
'Model.getDocument': ['owner', 'admin', 'member', 'readonly'],
|
|
22
23
|
'Model.getDocuments': ['owner', 'admin', 'member', 'readonly'],
|
package/eslint.config.js
CHANGED
package/express.js
CHANGED
|
@@ -81,6 +81,7 @@ module.exports = async function mongooseStudioExpressApp(apiUrl, conn, options)
|
|
|
81
81
|
);
|
|
82
82
|
|
|
83
83
|
const { config } = await frontend(apiUrl, false, options, workspace);
|
|
84
|
+
config.enableTaskVisualizer = options.enableTaskVisualizer;
|
|
84
85
|
router.get('/config.js', function (req, res) {
|
|
85
86
|
res.setHeader('Content-Type', 'application/javascript');
|
|
86
87
|
res.end(`window.MONGOOSE_STUDIO_CONFIG = ${JSON.stringify(config, null, 2)};`);
|