@mongoosejs/studio 0.3.8 → 0.3.10
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/Dashboard/createDashboard.js +2 -2
- package/backend/actions/Dashboard/deleteDashboard.js +4 -2
- package/backend/actions/Dashboard/getDashboard.js +81 -76
- package/backend/actions/Dashboard/getDashboards.js +2 -2
- package/backend/actions/Dashboard/updateDashboard.js +2 -2
- package/backend/db/dashboardResultSchema.js +37 -0
- package/backend/index.js +2 -0
- package/frontend/public/app.js +1 -1
- package/index.d.ts +6 -1
- package/package.json +1 -1
|
@@ -17,9 +17,9 @@ const CreateDashboardParams = new Archetype({
|
|
|
17
17
|
}
|
|
18
18
|
}).compile('CreateDashboardParams');
|
|
19
19
|
|
|
20
|
-
module.exports = ({
|
|
20
|
+
module.exports = ({ studioConnection }) => async function createDashboard(params) {
|
|
21
21
|
const { title, code, roles } = new CreateDashboardParams(params);
|
|
22
|
-
const Dashboard =
|
|
22
|
+
const Dashboard = studioConnection.model('__Studio_Dashboard');
|
|
23
23
|
|
|
24
24
|
await authorize('Dashboard.createDashboard', roles);
|
|
25
25
|
|
|
@@ -13,12 +13,14 @@ const DeleteDashboardParams = new Archetype({
|
|
|
13
13
|
}
|
|
14
14
|
}).compile('DeleteDashboardParams');
|
|
15
15
|
|
|
16
|
-
module.exports = ({
|
|
16
|
+
module.exports = ({ studioConnection }) => async function deleteDashboard(params) {
|
|
17
17
|
const { dashboardId, roles } = new DeleteDashboardParams(params);
|
|
18
|
-
const Dashboard =
|
|
18
|
+
const Dashboard = studioConnection.model('__Studio_Dashboard');
|
|
19
|
+
const DashboardResult = studioConnection.model('__Studio_DashboardResult');
|
|
19
20
|
|
|
20
21
|
await authorize('Dashboard.deleteDashboard', roles);
|
|
21
22
|
|
|
22
23
|
const result = await Dashboard.deleteOne({ _id: dashboardId }).orFail();
|
|
24
|
+
await DashboardResult.deleteMany({ dashboardId });
|
|
23
25
|
return { result };
|
|
24
26
|
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const Archetype = require('archetype');
|
|
4
|
-
const vm = require('vm');
|
|
5
4
|
const authorize = require('../../authorize');
|
|
5
|
+
const mongoose = require('mongoose');
|
|
6
6
|
const { defaultMothershipURL } = require('../../../constants');
|
|
7
7
|
|
|
8
8
|
const GetDashboardParams = new Archetype({
|
|
9
9
|
dashboardId: {
|
|
10
|
-
$type:
|
|
10
|
+
$type: mongoose.Types.ObjectId,
|
|
11
11
|
$required: true
|
|
12
12
|
},
|
|
13
13
|
evaluate: {
|
|
@@ -17,16 +17,20 @@ const GetDashboardParams = new Archetype({
|
|
|
17
17
|
$type: 'string'
|
|
18
18
|
},
|
|
19
19
|
$workspaceId: {
|
|
20
|
-
$type:
|
|
20
|
+
$type: mongoose.Types.ObjectId
|
|
21
|
+
},
|
|
22
|
+
userId: {
|
|
23
|
+
$type: mongoose.Types.ObjectId
|
|
21
24
|
},
|
|
22
25
|
roles: {
|
|
23
26
|
$type: ['string']
|
|
24
27
|
}
|
|
25
28
|
}).compile('GetDashboardParams');
|
|
26
29
|
|
|
27
|
-
module.exports = ({
|
|
28
|
-
const { $workspaceId, authorization, dashboardId, evaluate, roles } = new GetDashboardParams(params);
|
|
29
|
-
const Dashboard =
|
|
30
|
+
module.exports = ({ studioConnection, options }) => async function getDashboard(params) {
|
|
31
|
+
const { $workspaceId, authorization, userId, dashboardId, evaluate, roles } = new GetDashboardParams(params);
|
|
32
|
+
const Dashboard = studioConnection.model('__Studio_Dashboard');
|
|
33
|
+
const DashboardResult = studioConnection.model('__Studio_DashboardResult');
|
|
30
34
|
const mothershipUrl = options?._mothershipUrl ?? defaultMothershipURL;
|
|
31
35
|
|
|
32
36
|
await authorize('Dashboard.getDashboard', roles);
|
|
@@ -34,9 +38,8 @@ module.exports = ({ db, options }) => async function getDashboard(params) {
|
|
|
34
38
|
const dashboard = await Dashboard.findOne({ _id: dashboardId });
|
|
35
39
|
if (evaluate) {
|
|
36
40
|
let result = null;
|
|
37
|
-
const startExec = startDashboardEvaluate(dashboardId, $workspaceId,
|
|
38
|
-
// Avoid unhandled promise
|
|
39
|
-
startExec.catch(() => {});
|
|
41
|
+
const startExec = startDashboardEvaluate(DashboardResult, dashboardId, $workspaceId, userId);
|
|
42
|
+
startExec.catch(() => {}); // Avoid unhandled promise rejections - we will handle this error later.
|
|
40
43
|
try {
|
|
41
44
|
result = await dashboard.evaluate();
|
|
42
45
|
} catch (error) {
|
|
@@ -45,12 +48,11 @@ module.exports = ({ db, options }) => async function getDashboard(params) {
|
|
|
45
48
|
return {};
|
|
46
49
|
}
|
|
47
50
|
return completeDashboardEvaluate(
|
|
51
|
+
DashboardResult,
|
|
48
52
|
dashboardResult._id,
|
|
49
|
-
$workspaceId,
|
|
50
|
-
authorization,
|
|
51
53
|
null,
|
|
52
54
|
{ message: error.message },
|
|
53
|
-
|
|
55
|
+
'failed'
|
|
54
56
|
);
|
|
55
57
|
});
|
|
56
58
|
return { dashboard, dashboardResult, error: { message: error.message } };
|
|
@@ -62,12 +64,11 @@ module.exports = ({ db, options }) => async function getDashboard(params) {
|
|
|
62
64
|
return {};
|
|
63
65
|
}
|
|
64
66
|
return completeDashboardEvaluate(
|
|
67
|
+
DashboardResult,
|
|
65
68
|
dashboardResult._id,
|
|
66
|
-
$workspaceId,
|
|
67
|
-
authorization,
|
|
68
69
|
result,
|
|
69
70
|
undefined,
|
|
70
|
-
|
|
71
|
+
'completed'
|
|
71
72
|
);
|
|
72
73
|
});
|
|
73
74
|
|
|
@@ -76,77 +77,70 @@ module.exports = ({ db, options }) => async function getDashboard(params) {
|
|
|
76
77
|
return { dashboard, error: { message: error.message } };
|
|
77
78
|
}
|
|
78
79
|
} else {
|
|
79
|
-
const { dashboardResults } = await getDashboardResults(
|
|
80
|
+
const { dashboardResults } = await getDashboardResults(
|
|
81
|
+
DashboardResult,
|
|
82
|
+
dashboardId,
|
|
83
|
+
$workspaceId,
|
|
84
|
+
authorization,
|
|
85
|
+
mothershipUrl
|
|
86
|
+
);
|
|
80
87
|
return { dashboard, dashboardResults };
|
|
81
88
|
}
|
|
82
89
|
};
|
|
83
90
|
|
|
84
|
-
async function completeDashboardEvaluate(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
})
|
|
102
|
-
}).then(response => {
|
|
103
|
-
if (response.status < 200 || response.status >= 400) {
|
|
104
|
-
return response.json().then(data => {
|
|
105
|
-
throw new Error(`completeDashboardEvaluate error: ${data.message}`);
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
return response;
|
|
91
|
+
async function completeDashboardEvaluate(DashboardResult, dashboardResultId, result, error, status) {
|
|
92
|
+
const dashboardResult = await DashboardResult.findById(dashboardResultId).orFail();
|
|
93
|
+
dashboardResult.finishedEvaluatingAt = new Date();
|
|
94
|
+
dashboardResult.result = result;
|
|
95
|
+
dashboardResult.error = error;
|
|
96
|
+
dashboardResult.status = status;
|
|
97
|
+
await dashboardResult.save();
|
|
98
|
+
return { dashboardResult };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function startDashboardEvaluate(DashboardResult, dashboardId, workspaceId, userId) {
|
|
102
|
+
const dashboardResult = await DashboardResult.create({
|
|
103
|
+
dashboardId,
|
|
104
|
+
workspaceId,
|
|
105
|
+
userId,
|
|
106
|
+
startedEvaluatingAt: new Date(),
|
|
107
|
+
status: 'in_progress'
|
|
109
108
|
});
|
|
110
109
|
|
|
111
|
-
return
|
|
110
|
+
return { dashboardResult };
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
async function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const headers = { 'Content-Type': 'application/json' };
|
|
119
|
-
if (authorization) {
|
|
120
|
-
headers.Authorization = authorization;
|
|
113
|
+
async function getDashboardResults(DashboardResult, dashboardId, workspaceId, authorization, mothershipUrl) {
|
|
114
|
+
const filter = { dashboardId };
|
|
115
|
+
if (workspaceId != null) {
|
|
116
|
+
filter.workspaceId = workspaceId;
|
|
121
117
|
}
|
|
122
|
-
const response = await fetch(`${_mothershipUrl}/startDashboardEvaluate`, {
|
|
123
|
-
method: 'POST',
|
|
124
|
-
headers,
|
|
125
|
-
body: JSON.stringify({
|
|
126
|
-
dashboardId,
|
|
127
|
-
workspaceId,
|
|
128
|
-
startedEvaluatingAt: new Date()
|
|
129
|
-
})
|
|
130
|
-
}).then(response => {
|
|
131
|
-
if (response.status < 200 || response.status >= 400) {
|
|
132
|
-
return response.json().then(data => {
|
|
133
|
-
throw new Error(`startDashboardEvaluate error: ${data.message}`);
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
return response;
|
|
137
|
-
});
|
|
138
118
|
|
|
139
|
-
|
|
119
|
+
const [localResultsRes, remoteResultsRes] = await Promise.allSettled([
|
|
120
|
+
DashboardResult.find(filter).sort({ _id: -1 }).limit(10),
|
|
121
|
+
getMothershipDashboardResults(dashboardId, workspaceId, authorization, mothershipUrl)
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
const localResults = localResultsRes.status === 'fulfilled' ? localResultsRes.value : [];
|
|
125
|
+
const remoteResults = remoteResultsRes.status === 'fulfilled' ? remoteResultsRes.value : [];
|
|
126
|
+
|
|
127
|
+
const dashboardResults = localResults.concat(remoteResults)
|
|
128
|
+
.sort((a, b) => getSortTime(b) - getSortTime(a))
|
|
129
|
+
.slice(0, 10);
|
|
130
|
+
|
|
131
|
+
return { dashboardResults };
|
|
140
132
|
}
|
|
141
133
|
|
|
142
|
-
async function
|
|
134
|
+
async function getMothershipDashboardResults(dashboardId, workspaceId, authorization, mothershipUrl) {
|
|
143
135
|
if (!workspaceId) {
|
|
144
|
-
return
|
|
136
|
+
return [];
|
|
145
137
|
}
|
|
138
|
+
|
|
146
139
|
const headers = { 'Content-Type': 'application/json' };
|
|
147
140
|
if (authorization) {
|
|
148
141
|
headers.Authorization = authorization;
|
|
149
142
|
}
|
|
143
|
+
|
|
150
144
|
const response = await fetch(`${mothershipUrl}/getDashboardResults`, {
|
|
151
145
|
method: 'POST',
|
|
152
146
|
headers,
|
|
@@ -154,14 +148,25 @@ async function getDashboardResults(dashboardId, workspaceId, authorization, moth
|
|
|
154
148
|
dashboardId,
|
|
155
149
|
workspaceId
|
|
156
150
|
})
|
|
157
|
-
}).then(response => {
|
|
158
|
-
if (response.status < 200 || response.status >= 400) {
|
|
159
|
-
return response.json().then(data => {
|
|
160
|
-
throw new Error(`getDashboardResults error: ${data.message}`);
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
return response;
|
|
164
151
|
});
|
|
165
152
|
|
|
166
|
-
|
|
153
|
+
if (response.status < 200 || response.status >= 400) {
|
|
154
|
+
let message = `getDashboardResults error: ${response.status}`;
|
|
155
|
+
try {
|
|
156
|
+
const data = await response.json();
|
|
157
|
+
message = `getDashboardResults error: ${data.message}`;
|
|
158
|
+
} catch {
|
|
159
|
+
// Ignore parse errors and keep the generic status-based message.
|
|
160
|
+
}
|
|
161
|
+
throw new Error(message);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const data = await response.json();
|
|
165
|
+
return Array.isArray(data.dashboardResults) ? data.dashboardResults : [];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function getSortTime(result) {
|
|
169
|
+
const candidate = result?.finishedEvaluatingAt ?? result?.startedEvaluatingAt ?? result?.createdAt ?? result?._id;
|
|
170
|
+
const time = new Date(candidate).getTime();
|
|
171
|
+
return Number.isNaN(time) ? 0 : time;
|
|
167
172
|
}
|
|
@@ -9,8 +9,8 @@ const GetDashboardParams = new Archetype({
|
|
|
9
9
|
}
|
|
10
10
|
}).compile('GetDashboardParams');
|
|
11
11
|
|
|
12
|
-
module.exports = ({
|
|
13
|
-
const Dashboard =
|
|
12
|
+
module.exports = ({ studioConnection }) => async function getDashboards(params) {
|
|
13
|
+
const Dashboard = studioConnection.model('__Studio_Dashboard');
|
|
14
14
|
const { roles } = new GetDashboardParams(params);
|
|
15
15
|
|
|
16
16
|
await authorize('Dashboard.getDashboards', roles);
|
|
@@ -23,10 +23,10 @@ const UpdateDashboardParams = new Archetype({
|
|
|
23
23
|
}
|
|
24
24
|
}).compile('UpdateDashboardParams');
|
|
25
25
|
|
|
26
|
-
module.exports = ({
|
|
26
|
+
module.exports = ({ studioConnection }) => async function updateDashboard(params) {
|
|
27
27
|
const { dashboardId, code, title, description, roles, evaluate } = new UpdateDashboardParams(params);
|
|
28
28
|
|
|
29
|
-
const Dashboard =
|
|
29
|
+
const Dashboard = studioConnection.models['__Studio_Dashboard'];
|
|
30
30
|
|
|
31
31
|
await authorize('Dashboard.updateDashboard', roles);
|
|
32
32
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const mongoose = require('mongoose');
|
|
4
|
+
|
|
5
|
+
const dashboardResultSchema = new mongoose.Schema({
|
|
6
|
+
dashboardId: {
|
|
7
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
8
|
+
ref: '__Studio_Dashboard',
|
|
9
|
+
required: true
|
|
10
|
+
},
|
|
11
|
+
workspaceId: {
|
|
12
|
+
type: mongoose.Schema.Types.ObjectId
|
|
13
|
+
},
|
|
14
|
+
userId: {
|
|
15
|
+
type: mongoose.Schema.Types.ObjectId
|
|
16
|
+
},
|
|
17
|
+
startedEvaluatingAt: {
|
|
18
|
+
type: Date
|
|
19
|
+
},
|
|
20
|
+
finishedEvaluatingAt: {
|
|
21
|
+
type: Date
|
|
22
|
+
},
|
|
23
|
+
status: {
|
|
24
|
+
type: String,
|
|
25
|
+
enum: ['in_progress', 'completed', 'failed'],
|
|
26
|
+
required: true
|
|
27
|
+
},
|
|
28
|
+
error: new mongoose.Schema({
|
|
29
|
+
message: String,
|
|
30
|
+
extra: String
|
|
31
|
+
}, { _id: false }),
|
|
32
|
+
result: mongoose.Schema.Types.Mixed
|
|
33
|
+
}, { timestamps: true });
|
|
34
|
+
|
|
35
|
+
dashboardResultSchema.index({ dashboardId: 1 });
|
|
36
|
+
|
|
37
|
+
module.exports = dashboardResultSchema;
|
package/backend/index.js
CHANGED
|
@@ -7,12 +7,14 @@ const mongoose = require('mongoose');
|
|
|
7
7
|
const chatMessageSchema = require('./db/chatMessageSchema');
|
|
8
8
|
const chatThreadSchema = require('./db/chatThreadSchema');
|
|
9
9
|
const dashboardSchema = require('./db/dashboardSchema');
|
|
10
|
+
const dashboardResultSchema = require('./db/dashboardResultSchema');
|
|
10
11
|
|
|
11
12
|
module.exports = function backend(db, studioConnection, options) {
|
|
12
13
|
db = db || mongoose.connection;
|
|
13
14
|
|
|
14
15
|
studioConnection = studioConnection ?? db;
|
|
15
16
|
const Dashboard = studioConnection.model('__Studio_Dashboard', dashboardSchema, 'studio__dashboards');
|
|
17
|
+
const DashboardResult = studioConnection.model('__Studio_DashboardResult', dashboardResultSchema, 'studio__dashboardResults');
|
|
16
18
|
const ChatMessage = studioConnection.model('__Studio_ChatMessage', chatMessageSchema, 'studio__chatMessages');
|
|
17
19
|
const ChatThread = studioConnection.model('__Studio_ChatThread', chatThreadSchema, 'studio__chatThreads');
|
|
18
20
|
|
package/frontend/public/app.js
CHANGED
|
@@ -62835,7 +62835,7 @@ var src_default = VueToastificationPlugin;
|
|
|
62835
62835
|
(module) {
|
|
62836
62836
|
|
|
62837
62837
|
"use strict";
|
|
62838
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.3.
|
|
62838
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.3.10","description":"A Mongoose-native MongoDB UI with schema-aware autocomplete, AI-assisted queries, and dashboards that understand your models - not just your data.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.0","marked":"15.0.12","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","time-commando":"1.0.1","xss":"^1.0.15"},"peerDependencies":{"mongoose":"7.x || 8.x || ^9.0.0"},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","seed":"node seed/index.js","start":"node ./local.js","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js"}}');
|
|
62839
62839
|
|
|
62840
62840
|
/***/ }
|
|
62841
62841
|
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
declare module '@mongoosejs/studio' {
|
|
2
2
|
import { RequestHandler } from 'express';
|
|
3
|
+
import { Connection, Mongoose } from 'mongoose';
|
|
3
4
|
|
|
4
|
-
const express: (
|
|
5
|
+
const express: (
|
|
6
|
+
path: string,
|
|
7
|
+
connOrMongoose?: Connection | Mongoose,
|
|
8
|
+
options?: { apiKey?: string }
|
|
9
|
+
) => RequestHandler;
|
|
5
10
|
|
|
6
11
|
const studio: {
|
|
7
12
|
express: typeof express;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mongoosejs/studio",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
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": {
|