@exulu/backend 1.9.0 → 1.11.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/CHANGELOG.md +2 -2
- package/dist/index.cjs +600 -152
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +600 -152
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -308,46 +308,55 @@ var mapType = (t, type, name, defaultValue, unique) => {
|
|
|
308
308
|
if (type === "text") {
|
|
309
309
|
t.text(name);
|
|
310
310
|
if (unique) t.unique(name);
|
|
311
|
+
if (defaultValue) t.defaultTo(defaultValue);
|
|
311
312
|
return;
|
|
312
313
|
}
|
|
313
314
|
if (type === "longText") {
|
|
314
315
|
t.text(name);
|
|
315
316
|
if (unique) t.unique(name);
|
|
317
|
+
if (defaultValue) t.defaultTo(defaultValue);
|
|
316
318
|
return;
|
|
317
319
|
}
|
|
318
320
|
if (type === "shortText") {
|
|
319
321
|
t.string(name, 100);
|
|
320
322
|
if (unique) t.unique(name);
|
|
323
|
+
if (defaultValue) t.defaultTo(defaultValue);
|
|
321
324
|
return;
|
|
322
325
|
}
|
|
323
326
|
if (type === "number") {
|
|
324
327
|
t.float(name);
|
|
325
328
|
if (unique) t.unique(name);
|
|
329
|
+
if (defaultValue) t.defaultTo(defaultValue);
|
|
326
330
|
return;
|
|
327
331
|
}
|
|
328
332
|
if (type === "boolean") {
|
|
329
333
|
t.boolean(name).defaultTo(defaultValue || false);
|
|
330
334
|
if (unique) t.unique(name);
|
|
335
|
+
if (defaultValue) t.defaultTo(defaultValue);
|
|
331
336
|
return;
|
|
332
337
|
}
|
|
333
338
|
if (type === "code") {
|
|
334
339
|
t.text(name);
|
|
335
340
|
if (unique) t.unique(name);
|
|
341
|
+
if (defaultValue) t.defaultTo(defaultValue);
|
|
336
342
|
return;
|
|
337
343
|
}
|
|
338
344
|
if (type === "json") {
|
|
339
345
|
t.jsonb(name);
|
|
340
346
|
if (unique) t.unique(name);
|
|
347
|
+
if (defaultValue) t.defaultTo(defaultValue);
|
|
341
348
|
return;
|
|
342
349
|
}
|
|
343
350
|
if (type === "date") {
|
|
344
351
|
t.timestamp(name);
|
|
345
352
|
if (unique) t.unique(name);
|
|
353
|
+
if (defaultValue) t.defaultTo(defaultValue);
|
|
346
354
|
return;
|
|
347
355
|
}
|
|
348
356
|
if (type === "uuid") {
|
|
349
357
|
t.uuid(name);
|
|
350
358
|
if (unique) t.unique(name);
|
|
359
|
+
if (defaultValue) t.defaultTo(defaultValue);
|
|
351
360
|
return;
|
|
352
361
|
}
|
|
353
362
|
throw new Error("Invalid type: " + type);
|
|
@@ -576,6 +585,12 @@ var ExuluAgent = class {
|
|
|
576
585
|
this.slug = `/agents/${generateSlug(this.name)}/run`;
|
|
577
586
|
this.model = this.config?.model;
|
|
578
587
|
}
|
|
588
|
+
get providerName() {
|
|
589
|
+
return this.config?.model?.create({ apiKey: "" })?.provider || "";
|
|
590
|
+
}
|
|
591
|
+
get modelName() {
|
|
592
|
+
return this.config?.model?.create({ apiKey: "" })?.modelId || "";
|
|
593
|
+
}
|
|
579
594
|
// Exports the agent as a tool that can be used by another agent
|
|
580
595
|
// todo test this
|
|
581
596
|
tool = () => {
|
|
@@ -1938,6 +1953,7 @@ var import_schema = require("@graphql-tools/schema");
|
|
|
1938
1953
|
var import_graphql_type_json = __toESM(require("graphql-type-json"), 1);
|
|
1939
1954
|
var import_graphql = require("graphql");
|
|
1940
1955
|
var import_crypto_js2 = __toESM(require("crypto-js"), 1);
|
|
1956
|
+
var import_bcryptjs2 = __toESM(require("bcryptjs"), 1);
|
|
1941
1957
|
var GraphQLDate = new import_graphql.GraphQLScalarType({
|
|
1942
1958
|
name: "Date",
|
|
1943
1959
|
description: "Date custom scalar type",
|
|
@@ -2005,17 +2021,21 @@ function createTypeDefs(table) {
|
|
|
2005
2021
|
const required = field.required ? "!" : "";
|
|
2006
2022
|
return ` ${field.name}: ${type}${required}`;
|
|
2007
2023
|
});
|
|
2024
|
+
const rbacField = table.RBAC ? " RBAC: RBACData" : "";
|
|
2008
2025
|
const typeDef = `
|
|
2009
2026
|
type ${table.name.singular} {
|
|
2010
2027
|
${fields.join("\n")}
|
|
2011
|
-
id: ID!
|
|
2028
|
+
${table.fields.find((field) => field.name === "id") ? "" : "id: ID!"}
|
|
2012
2029
|
createdAt: Date!
|
|
2013
2030
|
updatedAt: Date!
|
|
2031
|
+
${rbacField}
|
|
2014
2032
|
}
|
|
2015
2033
|
`;
|
|
2034
|
+
const rbacInputField = table.RBAC ? " RBAC: RBACInput" : "";
|
|
2016
2035
|
const inputDef = `
|
|
2017
2036
|
input ${table.name.singular}Input {
|
|
2018
2037
|
${table.fields.map((f) => ` ${f.name}: ${map(f)}`).join("\n")}
|
|
2038
|
+
${rbacInputField}
|
|
2019
2039
|
}
|
|
2020
2040
|
`;
|
|
2021
2041
|
return typeDef + inputDef;
|
|
@@ -2084,33 +2104,184 @@ var getRequestedFields = (info) => {
|
|
|
2084
2104
|
acc[field.name.value] = true;
|
|
2085
2105
|
return acc;
|
|
2086
2106
|
}, {}));
|
|
2087
|
-
return fields.filter((field) => field !== "pageInfo" && field !== "items");
|
|
2107
|
+
return fields.filter((field) => field !== "pageInfo" && field !== "items" && field !== "RBAC");
|
|
2108
|
+
};
|
|
2109
|
+
var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRbacRecords) => {
|
|
2110
|
+
const { users = [], roles = [] } = rbacData;
|
|
2111
|
+
if (!existingRbacRecords) {
|
|
2112
|
+
existingRbacRecords = await db3.from("rbac").where({
|
|
2113
|
+
entity: entityName,
|
|
2114
|
+
target_resource_id: resourceId
|
|
2115
|
+
}).select("*");
|
|
2116
|
+
}
|
|
2117
|
+
const newUserRecords = new Set(users.map((u) => `${u.id}:${u.rights}`));
|
|
2118
|
+
const newRoleRecords = new Set(roles.map((r) => `${r.id}:${r.rights}`));
|
|
2119
|
+
const existingUserRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "User").map((r) => `${r.user_id}:${r.rights}`));
|
|
2120
|
+
const existingRoleRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "Role").map((r) => `${r.role_id}:${r.rights}`));
|
|
2121
|
+
const usersToCreate = users.filter((u) => !existingUserRecords.has(`${u.id}:${u.rights}`));
|
|
2122
|
+
const rolesToCreate = roles.filter((r) => !existingRoleRecords.has(`${r.id}:${r.rights}`));
|
|
2123
|
+
const usersToRemove = existingRbacRecords.filter((r) => r.access_type === "User" && !newUserRecords.has(`${r.user_id}:${r.rights}`));
|
|
2124
|
+
const rolesToRemove = existingRbacRecords.filter((r) => r.access_type === "Role" && !newRoleRecords.has(`${r.role_id}:${r.rights}`));
|
|
2125
|
+
if (usersToRemove.length > 0) {
|
|
2126
|
+
await db3.from("rbac").whereIn("id", usersToRemove.map((r) => r.id)).del();
|
|
2127
|
+
}
|
|
2128
|
+
if (rolesToRemove.length > 0) {
|
|
2129
|
+
await db3.from("rbac").whereIn("id", rolesToRemove.map((r) => r.id)).del();
|
|
2130
|
+
}
|
|
2131
|
+
const recordsToInsert = [];
|
|
2132
|
+
usersToCreate.forEach((user) => {
|
|
2133
|
+
recordsToInsert.push({
|
|
2134
|
+
entity: entityName,
|
|
2135
|
+
access_type: "User",
|
|
2136
|
+
target_resource_id: resourceId,
|
|
2137
|
+
user_id: user.id,
|
|
2138
|
+
rights: user.rights,
|
|
2139
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
2140
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2141
|
+
});
|
|
2142
|
+
});
|
|
2143
|
+
rolesToCreate.forEach((role) => {
|
|
2144
|
+
recordsToInsert.push({
|
|
2145
|
+
entity: entityName,
|
|
2146
|
+
access_type: "Role",
|
|
2147
|
+
target_resource_id: resourceId,
|
|
2148
|
+
role_id: role.id,
|
|
2149
|
+
rights: role.rights,
|
|
2150
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
2151
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2152
|
+
});
|
|
2153
|
+
});
|
|
2154
|
+
if (recordsToInsert.length > 0) {
|
|
2155
|
+
await db3.from("rbac").insert(recordsToInsert);
|
|
2156
|
+
}
|
|
2088
2157
|
};
|
|
2089
2158
|
function createMutations(table) {
|
|
2090
2159
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2091
|
-
const
|
|
2160
|
+
const validateWriteAccess = async (id, context) => {
|
|
2161
|
+
const { db: db3, req, user } = context;
|
|
2162
|
+
const hasRBAC = table.RBAC === true;
|
|
2163
|
+
if (!hasRBAC) {
|
|
2164
|
+
return true;
|
|
2165
|
+
}
|
|
2166
|
+
if (user.type === "api" || user.super_admin === true) {
|
|
2167
|
+
return true;
|
|
2168
|
+
}
|
|
2169
|
+
try {
|
|
2170
|
+
const authResult = await requestValidators.authenticate(req);
|
|
2171
|
+
if (authResult.error || !authResult.user) {
|
|
2172
|
+
throw new Error("Authentication required");
|
|
2173
|
+
}
|
|
2174
|
+
const user2 = authResult.user;
|
|
2175
|
+
const record = await db3.from(tableNamePlural).select(["rights_mode", "created_by"]).where({ id }).first();
|
|
2176
|
+
if (!record) {
|
|
2177
|
+
throw new Error("Record not found");
|
|
2178
|
+
}
|
|
2179
|
+
if (record.rights_mode === "public") {
|
|
2180
|
+
return true;
|
|
2181
|
+
}
|
|
2182
|
+
if (record.rights_mode === "private") {
|
|
2183
|
+
if (record.created_by === user2.id) {
|
|
2184
|
+
return true;
|
|
2185
|
+
}
|
|
2186
|
+
throw new Error("Only the creator can edit this private record");
|
|
2187
|
+
}
|
|
2188
|
+
if (record.rights_mode === "users") {
|
|
2189
|
+
const rbacRecord = await db3.from("rbac").where({
|
|
2190
|
+
entity: table.name.singular,
|
|
2191
|
+
target_resource_id: id,
|
|
2192
|
+
access_type: "User",
|
|
2193
|
+
user_id: user2.id,
|
|
2194
|
+
rights: "write"
|
|
2195
|
+
}).first();
|
|
2196
|
+
if (rbacRecord) {
|
|
2197
|
+
return true;
|
|
2198
|
+
}
|
|
2199
|
+
throw new Error("Insufficient user permissions to edit this record");
|
|
2200
|
+
}
|
|
2201
|
+
if (record.rights_mode === "roles" && user2.role) {
|
|
2202
|
+
const rbacRecord = await db3.from("rbac").where({
|
|
2203
|
+
entity: table.name.singular,
|
|
2204
|
+
target_resource_id: id,
|
|
2205
|
+
access_type: "Role",
|
|
2206
|
+
role_id: user2.role,
|
|
2207
|
+
rights: "write"
|
|
2208
|
+
}).first();
|
|
2209
|
+
if (rbacRecord) {
|
|
2210
|
+
return true;
|
|
2211
|
+
}
|
|
2212
|
+
throw new Error("Insufficient role permissions to edit this record");
|
|
2213
|
+
}
|
|
2214
|
+
throw new Error("Insufficient permissions to edit this record");
|
|
2215
|
+
} catch (error) {
|
|
2216
|
+
console.error("Write access validation error:", error);
|
|
2217
|
+
throw error;
|
|
2218
|
+
}
|
|
2219
|
+
};
|
|
2092
2220
|
return {
|
|
2093
2221
|
[`${tableNamePlural}CreateOne`]: async (_, args, context, info) => {
|
|
2094
2222
|
const { db: db3 } = context;
|
|
2095
2223
|
const requestedFields = getRequestedFields(info);
|
|
2096
2224
|
let { input } = args;
|
|
2225
|
+
const rbacData = input.RBAC;
|
|
2226
|
+
delete input.RBAC;
|
|
2227
|
+
delete input.created_by;
|
|
2097
2228
|
input = encryptSensitiveFields(input);
|
|
2229
|
+
if (table.RBAC) {
|
|
2230
|
+
input.created_by = context.user.id;
|
|
2231
|
+
}
|
|
2232
|
+
if (table.name.singular === "user" && context.user?.super_admin !== true) {
|
|
2233
|
+
throw new Error("You are not authorized to create users");
|
|
2234
|
+
}
|
|
2235
|
+
if (table.name.singular === "user" && input.password) {
|
|
2236
|
+
input.password = await import_bcryptjs2.default.hash(input.password, 10);
|
|
2237
|
+
}
|
|
2238
|
+
Object.keys(input).forEach((key) => {
|
|
2239
|
+
if (table.fields.find((field) => field.name === key)?.type === "json") {
|
|
2240
|
+
if (typeof input[key] === "object" || Array.isArray(input[key])) {
|
|
2241
|
+
input[key] = JSON.stringify(input[key]);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
});
|
|
2098
2245
|
const results = await db3(tableNamePlural).insert({
|
|
2099
2246
|
...input,
|
|
2247
|
+
...table.RBAC ? { rights_mode: "private" } : {},
|
|
2100
2248
|
createdAt: /* @__PURE__ */ new Date(),
|
|
2101
2249
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2102
2250
|
}).returning(requestedFields);
|
|
2251
|
+
if (table.RBAC && rbacData && results[0]) {
|
|
2252
|
+
await handleRBACUpdate(db3, table.name.singular, results[0].id, rbacData, []);
|
|
2253
|
+
}
|
|
2103
2254
|
return results[0];
|
|
2104
2255
|
},
|
|
2105
2256
|
[`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
|
|
2106
2257
|
const { db: db3, req } = context;
|
|
2107
2258
|
let { where, input } = args;
|
|
2108
|
-
await
|
|
2259
|
+
await validateCreateOrRemoveSuperAdminPermission(tableNamePlural, input, req);
|
|
2260
|
+
if (where.id) {
|
|
2261
|
+
await validateWriteAccess(where.id, context);
|
|
2262
|
+
}
|
|
2263
|
+
const rbacData = input.RBAC;
|
|
2264
|
+
delete input.RBAC;
|
|
2265
|
+
delete input.created_by;
|
|
2109
2266
|
input = encryptSensitiveFields(input);
|
|
2267
|
+
Object.keys(input).forEach((key) => {
|
|
2268
|
+
if (table.fields.find((field) => field.name === key)?.type === "json") {
|
|
2269
|
+
if (typeof input[key] === "object" || Array.isArray(input[key])) {
|
|
2270
|
+
input[key] = JSON.stringify(input[key]);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2110
2274
|
await db3(tableNamePlural).where(where).update({
|
|
2111
2275
|
...input,
|
|
2112
2276
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2113
2277
|
});
|
|
2278
|
+
if (table.RBAC && rbacData && where.id) {
|
|
2279
|
+
const existingRbacRecords = await db3.from("rbac").where({
|
|
2280
|
+
entity: table.name.singular,
|
|
2281
|
+
target_resource_id: where.id
|
|
2282
|
+
}).select("*");
|
|
2283
|
+
await handleRBACUpdate(db3, table.name.singular, where.id, rbacData, existingRbacRecords);
|
|
2284
|
+
}
|
|
2114
2285
|
const requestedFields = getRequestedFields(info);
|
|
2115
2286
|
const result = await db3.from(tableNamePlural).select(requestedFields).where(where).first();
|
|
2116
2287
|
return result;
|
|
@@ -2118,12 +2289,30 @@ function createMutations(table) {
|
|
|
2118
2289
|
[`${tableNamePlural}UpdateOneById`]: async (_, args, context, info) => {
|
|
2119
2290
|
const { db: db3, req } = context;
|
|
2120
2291
|
let { id, input } = args;
|
|
2121
|
-
await
|
|
2292
|
+
await validateCreateOrRemoveSuperAdminPermission(tableNamePlural, input, req);
|
|
2293
|
+
await validateWriteAccess(id, context);
|
|
2294
|
+
const rbacData = input.RBAC;
|
|
2295
|
+
delete input.RBAC;
|
|
2296
|
+
delete input.created_by;
|
|
2122
2297
|
input = encryptSensitiveFields(input);
|
|
2298
|
+
Object.keys(input).forEach((key) => {
|
|
2299
|
+
if (table.fields.find((field) => field.name === key)?.type === "json") {
|
|
2300
|
+
if (typeof input[key] === "object" || Array.isArray(input[key])) {
|
|
2301
|
+
input[key] = JSON.stringify(input[key]);
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
});
|
|
2123
2305
|
await db3(tableNamePlural).where({ id }).update({
|
|
2124
2306
|
...input,
|
|
2125
2307
|
updatedAt: /* @__PURE__ */ new Date()
|
|
2126
2308
|
});
|
|
2309
|
+
if (table.RBAC && rbacData) {
|
|
2310
|
+
const existingRbacRecords = await db3.from("rbac").where({
|
|
2311
|
+
entity: table.name.singular,
|
|
2312
|
+
target_resource_id: id
|
|
2313
|
+
}).select("*");
|
|
2314
|
+
await handleRBACUpdate(db3, table.name.singular, id, rbacData, existingRbacRecords);
|
|
2315
|
+
}
|
|
2127
2316
|
const requestedFields = getRequestedFields(info);
|
|
2128
2317
|
const result = await db3.from(tableNamePlural).select(requestedFields).where({ id }).first();
|
|
2129
2318
|
return result;
|
|
@@ -2133,7 +2322,16 @@ function createMutations(table) {
|
|
|
2133
2322
|
const { where } = args;
|
|
2134
2323
|
const requestedFields = getRequestedFields(info);
|
|
2135
2324
|
const result = await db3.from(tableNamePlural).select(requestedFields).where(where).first();
|
|
2325
|
+
if (!result) {
|
|
2326
|
+
throw new Error("Record not found");
|
|
2327
|
+
}
|
|
2136
2328
|
await db3(tableNamePlural).where(where).del();
|
|
2329
|
+
if (table.RBAC) {
|
|
2330
|
+
await db3.from("rbac").where({
|
|
2331
|
+
entity: table.name.singular,
|
|
2332
|
+
target_resource_id: result.id
|
|
2333
|
+
}).del();
|
|
2334
|
+
}
|
|
2137
2335
|
return result;
|
|
2138
2336
|
},
|
|
2139
2337
|
[`${tableNamePlural}RemoveOneById`]: async (_, args, context, info) => {
|
|
@@ -2141,11 +2339,54 @@ function createMutations(table) {
|
|
|
2141
2339
|
const { db: db3 } = context;
|
|
2142
2340
|
const requestedFields = getRequestedFields(info);
|
|
2143
2341
|
const result = await db3.from(tableNamePlural).select(requestedFields).where({ id }).first();
|
|
2342
|
+
if (!result) {
|
|
2343
|
+
throw new Error("Record not found");
|
|
2344
|
+
}
|
|
2144
2345
|
await db3(tableNamePlural).where({ id }).del();
|
|
2346
|
+
if (table.RBAC) {
|
|
2347
|
+
await db3.from("rbac").where({
|
|
2348
|
+
entity: table.name.singular,
|
|
2349
|
+
target_resource_id: id
|
|
2350
|
+
}).del();
|
|
2351
|
+
}
|
|
2145
2352
|
return result;
|
|
2146
2353
|
}
|
|
2147
2354
|
};
|
|
2148
2355
|
}
|
|
2356
|
+
var applyAccessControl = (table, user, query) => {
|
|
2357
|
+
console.log("table", table);
|
|
2358
|
+
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2359
|
+
const hasRBAC = table.RBAC === true;
|
|
2360
|
+
if (!hasRBAC) {
|
|
2361
|
+
return query;
|
|
2362
|
+
}
|
|
2363
|
+
if (user.type === "api" || user.super_admin === true) {
|
|
2364
|
+
return query;
|
|
2365
|
+
}
|
|
2366
|
+
try {
|
|
2367
|
+
query = query.where(function() {
|
|
2368
|
+
this.where("rights_mode", "public");
|
|
2369
|
+
this.orWhere("created_by", user.id);
|
|
2370
|
+
this.orWhere(function() {
|
|
2371
|
+
this.where("rights_mode", "users").whereExists(function() {
|
|
2372
|
+
this.select("*").from("rbac").whereRaw("rbac.target_resource_id = " + tableNamePlural + ".id").where("rbac.entity", table.name.singular).where("rbac.access_type", "User").where("rbac.user_id", user.id);
|
|
2373
|
+
});
|
|
2374
|
+
});
|
|
2375
|
+
if (user.role) {
|
|
2376
|
+
console.log("user.role", user.role);
|
|
2377
|
+
this.orWhere(function() {
|
|
2378
|
+
this.where("rights_mode", "roles").whereExists(function() {
|
|
2379
|
+
this.select("*").from("rbac").whereRaw("rbac.target_resource_id = " + tableNamePlural + ".id").where("rbac.entity", table.name.singular).where("rbac.access_type", "Role").where("rbac.role_id", user.role);
|
|
2380
|
+
});
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
});
|
|
2384
|
+
} catch (error) {
|
|
2385
|
+
console.error("Access control error:", error);
|
|
2386
|
+
return query.where("1", "=", "0");
|
|
2387
|
+
}
|
|
2388
|
+
return query;
|
|
2389
|
+
};
|
|
2149
2390
|
function createQueries(table) {
|
|
2150
2391
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2151
2392
|
const tableNameSingular = table.name.singular.toLowerCase();
|
|
@@ -2180,7 +2421,9 @@ function createQueries(table) {
|
|
|
2180
2421
|
[`${tableNameSingular}ById`]: async (_, args, context, info) => {
|
|
2181
2422
|
const { db: db3 } = context;
|
|
2182
2423
|
const requestedFields = getRequestedFields(info);
|
|
2183
|
-
|
|
2424
|
+
let query = db3.from(tableNamePlural).select(requestedFields).where({ id: args.id });
|
|
2425
|
+
query = applyAccessControl(table, context.user, query);
|
|
2426
|
+
const result = await query.first();
|
|
2184
2427
|
return result;
|
|
2185
2428
|
},
|
|
2186
2429
|
[`${tableNameSingular}One`]: async (_, args, context, info) => {
|
|
@@ -2189,6 +2432,7 @@ function createQueries(table) {
|
|
|
2189
2432
|
const requestedFields = getRequestedFields(info);
|
|
2190
2433
|
let query = db3.from(tableNamePlural).select(requestedFields);
|
|
2191
2434
|
query = applyFilters(query, filters);
|
|
2435
|
+
query = applyAccessControl(table, context.user, query);
|
|
2192
2436
|
query = applySorting(query, sort);
|
|
2193
2437
|
const result = await query.first();
|
|
2194
2438
|
return result;
|
|
@@ -2196,15 +2440,19 @@ function createQueries(table) {
|
|
|
2196
2440
|
[`${tableNamePlural}Pagination`]: async (_, args, context, info) => {
|
|
2197
2441
|
const { limit = 10, page = 0, filters = [], sort } = args;
|
|
2198
2442
|
const { db: db3 } = context;
|
|
2199
|
-
let
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2443
|
+
let countQuery = db3(tableNamePlural);
|
|
2444
|
+
countQuery = applyFilters(countQuery, filters);
|
|
2445
|
+
countQuery = applyAccessControl(table, context.user, countQuery);
|
|
2446
|
+
console.log("countQuery", countQuery);
|
|
2447
|
+
const countResult = await countQuery.count("* as count");
|
|
2448
|
+
const itemCount = Number(countResult[0]?.count || 0);
|
|
2203
2449
|
const pageCount = Math.ceil(itemCount / limit);
|
|
2204
2450
|
const currentPage = page;
|
|
2205
2451
|
const hasPreviousPage = currentPage > 1;
|
|
2206
2452
|
const hasNextPage = currentPage < pageCount - 1;
|
|
2207
|
-
let dataQuery =
|
|
2453
|
+
let dataQuery = db3(tableNamePlural);
|
|
2454
|
+
dataQuery = applyFilters(dataQuery, filters);
|
|
2455
|
+
dataQuery = applyAccessControl(table, context.user, dataQuery);
|
|
2208
2456
|
const requestedFields = getRequestedFields(info);
|
|
2209
2457
|
dataQuery = applySorting(dataQuery, sort);
|
|
2210
2458
|
if (page > 1) {
|
|
@@ -2222,7 +2470,28 @@ function createQueries(table) {
|
|
|
2222
2470
|
items
|
|
2223
2471
|
};
|
|
2224
2472
|
},
|
|
2225
|
-
// Add
|
|
2473
|
+
// Add generic statistics query for all tables
|
|
2474
|
+
[`${tableNamePlural}Statistics`]: async (_, args, context, info) => {
|
|
2475
|
+
const { filters = [], groupBy } = args;
|
|
2476
|
+
const { db: db3 } = context;
|
|
2477
|
+
let query = db3(tableNamePlural);
|
|
2478
|
+
query = applyFilters(query, filters);
|
|
2479
|
+
query = applyAccessControl(table, context.user, query);
|
|
2480
|
+
if (groupBy) {
|
|
2481
|
+
const results = await query.select(groupBy).count("* as count").groupBy(groupBy);
|
|
2482
|
+
return results.map((r) => ({
|
|
2483
|
+
group: r[groupBy],
|
|
2484
|
+
count: Number(r.count)
|
|
2485
|
+
}));
|
|
2486
|
+
} else {
|
|
2487
|
+
const [{ count }] = await query.count("* as count");
|
|
2488
|
+
return [{
|
|
2489
|
+
group: "total",
|
|
2490
|
+
count: Number(count)
|
|
2491
|
+
}];
|
|
2492
|
+
}
|
|
2493
|
+
},
|
|
2494
|
+
// Add jobStatistics query for jobs table (backward compatibility)
|
|
2226
2495
|
...tableNamePlural === "jobs" ? {
|
|
2227
2496
|
jobStatistics: async (_, args, context, info) => {
|
|
2228
2497
|
const { user, agent, from, to } = args;
|
|
@@ -2240,6 +2509,11 @@ function createQueries(table) {
|
|
|
2240
2509
|
if (to) {
|
|
2241
2510
|
query = query.where("createdAt", "<=", to);
|
|
2242
2511
|
}
|
|
2512
|
+
query = applyAccessControl(table, context.user, query);
|
|
2513
|
+
const runningQuery = query.clone().whereIn("status", ["active", "waiting", "delayed", "paused"]);
|
|
2514
|
+
const [{ runningCount }] = await runningQuery.count("* as runningCount");
|
|
2515
|
+
const erroredQuery = query.clone().whereIn("status", ["failed", "stuck"]);
|
|
2516
|
+
const [{ erroredCount }] = await erroredQuery.count("* as erroredCount");
|
|
2243
2517
|
const completedQuery = query.clone().where("status", "completed");
|
|
2244
2518
|
const [{ completedCount }] = await completedQuery.count("* as completedCount");
|
|
2245
2519
|
const failedQuery = query.clone().where("status", "failed");
|
|
@@ -2247,6 +2521,8 @@ function createQueries(table) {
|
|
|
2247
2521
|
const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db3.raw('AVG("duration") as averageDuration'));
|
|
2248
2522
|
const [{ averageDuration }] = await durationQuery;
|
|
2249
2523
|
return {
|
|
2524
|
+
runningCount: Number(runningCount),
|
|
2525
|
+
erroredCount: Number(erroredCount),
|
|
2250
2526
|
completedCount: Number(completedCount),
|
|
2251
2527
|
failedCount: Number(failedCount),
|
|
2252
2528
|
averageDuration: averageDuration ? Number(averageDuration) : 0
|
|
@@ -2255,12 +2531,59 @@ function createQueries(table) {
|
|
|
2255
2531
|
} : {}
|
|
2256
2532
|
};
|
|
2257
2533
|
}
|
|
2534
|
+
var RBACResolver = async (db3, table, entityName, resourceId, rights_mode) => {
|
|
2535
|
+
const rbacRecords = await db3.from("rbac").where({
|
|
2536
|
+
entity: entityName,
|
|
2537
|
+
target_resource_id: resourceId
|
|
2538
|
+
}).select("*");
|
|
2539
|
+
const users = rbacRecords.filter((r) => r.access_type === "User").map((r) => ({ id: r.user_id, rights: r.rights }));
|
|
2540
|
+
const roles = rbacRecords.filter((r) => r.access_type === "Role").map((r) => ({ id: r.role_id, rights: r.rights }));
|
|
2541
|
+
let type = rights_mode || "private";
|
|
2542
|
+
if (type === "users" && users.length === 0) type = "private";
|
|
2543
|
+
if (type === "roles" && roles.length === 0) type = "private";
|
|
2544
|
+
return {
|
|
2545
|
+
type,
|
|
2546
|
+
users,
|
|
2547
|
+
roles
|
|
2548
|
+
};
|
|
2549
|
+
};
|
|
2258
2550
|
function createSDL(tables) {
|
|
2259
2551
|
console.log("[EXULU] Creating SDL");
|
|
2260
2552
|
let typeDefs = `
|
|
2261
2553
|
scalar JSON
|
|
2262
2554
|
scalar Date
|
|
2263
2555
|
|
|
2556
|
+
type RBACData {
|
|
2557
|
+
type: String!
|
|
2558
|
+
users: [RBACUser!]
|
|
2559
|
+
roles: [RBACRole!]
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
type RBACUser {
|
|
2563
|
+
id: ID!
|
|
2564
|
+
rights: String!
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
type RBACRole {
|
|
2568
|
+
id: ID!
|
|
2569
|
+
rights: String!
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
input RBACInput {
|
|
2573
|
+
users: [RBACUserInput!]
|
|
2574
|
+
roles: [RBACRoleInput!]
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
input RBACUserInput {
|
|
2578
|
+
id: ID!
|
|
2579
|
+
rights: String!
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
input RBACRoleInput {
|
|
2583
|
+
id: ID!
|
|
2584
|
+
rights: String!
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2264
2587
|
type Query {
|
|
2265
2588
|
`;
|
|
2266
2589
|
let mutationDefs = `
|
|
@@ -2269,6 +2592,9 @@ function createSDL(tables) {
|
|
|
2269
2592
|
let modelDefs = "";
|
|
2270
2593
|
const resolvers = { JSON: import_graphql_type_json.default, Date: GraphQLDate, Query: {}, Mutation: {} };
|
|
2271
2594
|
for (const table of tables) {
|
|
2595
|
+
if (table.graphql === false) {
|
|
2596
|
+
continue;
|
|
2597
|
+
}
|
|
2272
2598
|
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2273
2599
|
const tableNameSingular = table.name.singular.toLowerCase();
|
|
2274
2600
|
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
@@ -2277,6 +2603,7 @@ function createSDL(tables) {
|
|
|
2277
2603
|
${tableNameSingular}ById(id: ID!): ${tableNameSingular}
|
|
2278
2604
|
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
2279
2605
|
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
2606
|
+
${tableNamePlural}Statistics(filters: [Filter${tableNameSingularUpperCaseFirst}], groupBy: String): [StatisticsResult]!
|
|
2280
2607
|
${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
|
|
2281
2608
|
`;
|
|
2282
2609
|
mutationDefs += `
|
|
@@ -2305,6 +2632,8 @@ type PageInfo {
|
|
|
2305
2632
|
if (tableNamePlural === "jobs") {
|
|
2306
2633
|
modelDefs += `
|
|
2307
2634
|
type JobStatistics {
|
|
2635
|
+
runningCount: Int!
|
|
2636
|
+
erroredCount: Int!
|
|
2308
2637
|
completedCount: Int!
|
|
2309
2638
|
failedCount: Int!
|
|
2310
2639
|
averageDuration: Float!
|
|
@@ -2313,10 +2642,29 @@ type JobStatistics {
|
|
|
2313
2642
|
}
|
|
2314
2643
|
Object.assign(resolvers.Query, createQueries(table));
|
|
2315
2644
|
Object.assign(resolvers.Mutation, createMutations(table));
|
|
2645
|
+
if (table.RBAC) {
|
|
2646
|
+
const rbacResolverName = table.name.singular;
|
|
2647
|
+
if (!resolvers[rbacResolverName]) {
|
|
2648
|
+
resolvers[rbacResolverName] = {};
|
|
2649
|
+
}
|
|
2650
|
+
resolvers[rbacResolverName].RBAC = async (parent, args, context) => {
|
|
2651
|
+
const { db: db3 } = context;
|
|
2652
|
+
const resourceId = parent.id;
|
|
2653
|
+
const entityName = table.name.singular;
|
|
2654
|
+
const rights_mode = parent.rights_mode;
|
|
2655
|
+
return RBACResolver(db3, table, entityName, resourceId, rights_mode);
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2316
2658
|
}
|
|
2317
2659
|
typeDefs += "}\n";
|
|
2318
2660
|
mutationDefs += "}\n";
|
|
2319
|
-
const
|
|
2661
|
+
const genericTypes = `
|
|
2662
|
+
type StatisticsResult {
|
|
2663
|
+
group: String!
|
|
2664
|
+
count: Int!
|
|
2665
|
+
}
|
|
2666
|
+
`;
|
|
2667
|
+
const fullSDL = typeDefs + mutationDefs + modelDefs + genericTypes;
|
|
2320
2668
|
const schema = (0, import_schema.makeExecutableSchema)({
|
|
2321
2669
|
typeDefs: fullSDL,
|
|
2322
2670
|
resolvers
|
|
@@ -2353,7 +2701,7 @@ var encryptSensitiveFields = (input) => {
|
|
|
2353
2701
|
}
|
|
2354
2702
|
return input;
|
|
2355
2703
|
};
|
|
2356
|
-
var
|
|
2704
|
+
var validateCreateOrRemoveSuperAdminPermission = async (tableNamePlural, input, req) => {
|
|
2357
2705
|
if (tableNamePlural === "users" && input.super_admin !== void 0) {
|
|
2358
2706
|
const authResult = await requestValidators.authenticate(req);
|
|
2359
2707
|
if (authResult.error || !authResult.user) {
|
|
@@ -2404,6 +2752,10 @@ var agentSessionsSchema = {
|
|
|
2404
2752
|
// next auth stores users with id type SERIAL, so we need to use number
|
|
2405
2753
|
type: "number"
|
|
2406
2754
|
},
|
|
2755
|
+
{
|
|
2756
|
+
name: "role",
|
|
2757
|
+
type: "uuid"
|
|
2758
|
+
},
|
|
2407
2759
|
{
|
|
2408
2760
|
name: "title",
|
|
2409
2761
|
type: "text"
|
|
@@ -2416,6 +2768,11 @@ var usersSchema = {
|
|
|
2416
2768
|
singular: "user"
|
|
2417
2769
|
},
|
|
2418
2770
|
fields: [
|
|
2771
|
+
{
|
|
2772
|
+
name: "id",
|
|
2773
|
+
type: "number",
|
|
2774
|
+
index: true
|
|
2775
|
+
},
|
|
2419
2776
|
{
|
|
2420
2777
|
name: "firstname",
|
|
2421
2778
|
type: "text"
|
|
@@ -2467,6 +2824,10 @@ var usersSchema = {
|
|
|
2467
2824
|
name: "last_used",
|
|
2468
2825
|
type: "date"
|
|
2469
2826
|
},
|
|
2827
|
+
{
|
|
2828
|
+
name: "password",
|
|
2829
|
+
type: "text"
|
|
2830
|
+
},
|
|
2470
2831
|
{
|
|
2471
2832
|
name: "anthropic_token",
|
|
2472
2833
|
type: "text"
|
|
@@ -2498,7 +2859,7 @@ var rolesSchema = {
|
|
|
2498
2859
|
}
|
|
2499
2860
|
]
|
|
2500
2861
|
};
|
|
2501
|
-
var
|
|
2862
|
+
var statisticsSchema2 = {
|
|
2502
2863
|
name: {
|
|
2503
2864
|
plural: "statistics",
|
|
2504
2865
|
singular: "statistic"
|
|
@@ -2664,6 +3025,7 @@ var agentsSchema = {
|
|
|
2664
3025
|
plural: "agents",
|
|
2665
3026
|
singular: "agent"
|
|
2666
3027
|
},
|
|
3028
|
+
RBAC: true,
|
|
2667
3029
|
fields: [
|
|
2668
3030
|
{
|
|
2669
3031
|
name: "name",
|
|
@@ -2694,11 +3056,6 @@ var agentsSchema = {
|
|
|
2694
3056
|
type: "boolean",
|
|
2695
3057
|
default: false
|
|
2696
3058
|
},
|
|
2697
|
-
{
|
|
2698
|
-
name: "public",
|
|
2699
|
-
type: "boolean",
|
|
2700
|
-
default: false
|
|
2701
|
-
},
|
|
2702
3059
|
{
|
|
2703
3060
|
name: "tools",
|
|
2704
3061
|
type: "json"
|
|
@@ -2728,6 +3085,128 @@ var variablesSchema = {
|
|
|
2728
3085
|
}
|
|
2729
3086
|
]
|
|
2730
3087
|
};
|
|
3088
|
+
var rbacSchema = {
|
|
3089
|
+
name: {
|
|
3090
|
+
plural: "rbac",
|
|
3091
|
+
singular: "rbac"
|
|
3092
|
+
},
|
|
3093
|
+
graphql: false,
|
|
3094
|
+
fields: [
|
|
3095
|
+
{
|
|
3096
|
+
name: "entity",
|
|
3097
|
+
type: "text",
|
|
3098
|
+
required: true
|
|
3099
|
+
},
|
|
3100
|
+
{
|
|
3101
|
+
name: "access_type",
|
|
3102
|
+
type: "text",
|
|
3103
|
+
required: true
|
|
3104
|
+
},
|
|
3105
|
+
{
|
|
3106
|
+
name: "target_resource_id",
|
|
3107
|
+
type: "uuid",
|
|
3108
|
+
required: true
|
|
3109
|
+
},
|
|
3110
|
+
{
|
|
3111
|
+
name: "role_id",
|
|
3112
|
+
type: "uuid"
|
|
3113
|
+
},
|
|
3114
|
+
{
|
|
3115
|
+
name: "user_id",
|
|
3116
|
+
type: "number"
|
|
3117
|
+
},
|
|
3118
|
+
{
|
|
3119
|
+
name: "rights",
|
|
3120
|
+
type: "text",
|
|
3121
|
+
required: true
|
|
3122
|
+
}
|
|
3123
|
+
]
|
|
3124
|
+
};
|
|
3125
|
+
var workflowTemplatesSchema = {
|
|
3126
|
+
name: {
|
|
3127
|
+
plural: "workflow_templates",
|
|
3128
|
+
singular: "workflow_template"
|
|
3129
|
+
},
|
|
3130
|
+
RBAC: true,
|
|
3131
|
+
fields: [
|
|
3132
|
+
{
|
|
3133
|
+
name: "name",
|
|
3134
|
+
type: "text",
|
|
3135
|
+
required: true
|
|
3136
|
+
},
|
|
3137
|
+
{
|
|
3138
|
+
name: "description",
|
|
3139
|
+
type: "text"
|
|
3140
|
+
},
|
|
3141
|
+
{
|
|
3142
|
+
name: "owner",
|
|
3143
|
+
type: "number",
|
|
3144
|
+
required: true
|
|
3145
|
+
},
|
|
3146
|
+
{
|
|
3147
|
+
name: "visibility",
|
|
3148
|
+
type: "text",
|
|
3149
|
+
required: true
|
|
3150
|
+
},
|
|
3151
|
+
{
|
|
3152
|
+
name: "shared_user_ids",
|
|
3153
|
+
type: "json"
|
|
3154
|
+
},
|
|
3155
|
+
{
|
|
3156
|
+
name: "shared_role_ids",
|
|
3157
|
+
type: "json"
|
|
3158
|
+
},
|
|
3159
|
+
{
|
|
3160
|
+
name: "variables",
|
|
3161
|
+
type: "json"
|
|
3162
|
+
},
|
|
3163
|
+
{
|
|
3164
|
+
name: "steps_json",
|
|
3165
|
+
type: "json",
|
|
3166
|
+
required: true
|
|
3167
|
+
},
|
|
3168
|
+
{
|
|
3169
|
+
name: "example_metadata_json",
|
|
3170
|
+
type: "json"
|
|
3171
|
+
}
|
|
3172
|
+
]
|
|
3173
|
+
};
|
|
3174
|
+
var addRBACfields = (schema) => {
|
|
3175
|
+
if (schema.RBAC) {
|
|
3176
|
+
console.log(`[EXULU] Adding rights_mode field to ${schema.name.plural} table.`);
|
|
3177
|
+
schema.fields.push({
|
|
3178
|
+
name: "rights_mode",
|
|
3179
|
+
type: "text",
|
|
3180
|
+
required: false,
|
|
3181
|
+
default: "private"
|
|
3182
|
+
});
|
|
3183
|
+
schema.fields.push({
|
|
3184
|
+
name: "created_by",
|
|
3185
|
+
type: "number",
|
|
3186
|
+
required: true,
|
|
3187
|
+
default: 0
|
|
3188
|
+
});
|
|
3189
|
+
}
|
|
3190
|
+
return schema;
|
|
3191
|
+
};
|
|
3192
|
+
var coreSchemas = {
|
|
3193
|
+
get: () => {
|
|
3194
|
+
return {
|
|
3195
|
+
agentsSchema: () => addRBACfields(agentsSchema),
|
|
3196
|
+
agentMessagesSchema: () => addRBACfields(agentMessagesSchema),
|
|
3197
|
+
agentSessionsSchema: () => addRBACfields(agentSessionsSchema),
|
|
3198
|
+
usersSchema: () => addRBACfields(usersSchema),
|
|
3199
|
+
rolesSchema: () => addRBACfields(rolesSchema),
|
|
3200
|
+
statisticsSchema: () => addRBACfields(statisticsSchema2),
|
|
3201
|
+
workflowSchema: () => addRBACfields(workflowSchema),
|
|
3202
|
+
evalResultsSchema: () => addRBACfields(evalResultsSchema),
|
|
3203
|
+
jobsSchema: () => addRBACfields(jobsSchema),
|
|
3204
|
+
variablesSchema: () => addRBACfields(variablesSchema),
|
|
3205
|
+
rbacSchema: () => addRBACfields(rbacSchema),
|
|
3206
|
+
workflowTemplatesSchema: () => addRBACfields(workflowTemplatesSchema)
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
};
|
|
2731
3210
|
|
|
2732
3211
|
// src/registry/uppy.ts
|
|
2733
3212
|
var import_express = require("express");
|
|
@@ -3161,6 +3640,7 @@ var REQUEST_SIZE_LIMIT = "50mb";
|
|
|
3161
3640
|
var global_queues = {
|
|
3162
3641
|
logs_cleaner: "logs-cleaner"
|
|
3163
3642
|
};
|
|
3643
|
+
var { agentsSchema: agentsSchema2, evalResultsSchema: evalResultsSchema2, jobsSchema: jobsSchema2, agentSessionsSchema: agentSessionsSchema2, agentMessagesSchema: agentMessagesSchema2, rolesSchema: rolesSchema2, usersSchema: usersSchema2, workflowSchema: workflowSchema2, variablesSchema: variablesSchema2, workflowTemplatesSchema: workflowTemplatesSchema2, rbacSchema: rbacSchema2 } = coreSchemas.get();
|
|
3164
3644
|
var createRecurringJobs = async () => {
|
|
3165
3645
|
console.log("[EXULU] creating recurring jobs.");
|
|
3166
3646
|
const recurringJobSchedulersLogs = [];
|
|
@@ -3263,7 +3743,22 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
|
|
|
3263
3743
|
} else {
|
|
3264
3744
|
console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
|
|
3265
3745
|
}
|
|
3266
|
-
const schema = createSDL(
|
|
3746
|
+
const schema = createSDL(
|
|
3747
|
+
[
|
|
3748
|
+
usersSchema2(),
|
|
3749
|
+
rolesSchema2(),
|
|
3750
|
+
agentsSchema2(),
|
|
3751
|
+
jobsSchema2(),
|
|
3752
|
+
workflowSchema2(),
|
|
3753
|
+
evalResultsSchema2(),
|
|
3754
|
+
agentSessionsSchema2(),
|
|
3755
|
+
agentMessagesSchema2(),
|
|
3756
|
+
variablesSchema2(),
|
|
3757
|
+
workflowTemplatesSchema2(),
|
|
3758
|
+
statisticsSchema(),
|
|
3759
|
+
rbacSchema2()
|
|
3760
|
+
]
|
|
3761
|
+
);
|
|
3267
3762
|
console.log("[EXULU] graphql server");
|
|
3268
3763
|
const server = new import_server3.ApolloServer({
|
|
3269
3764
|
cache: new import_utils2.InMemoryLRUCache(),
|
|
@@ -3286,7 +3781,8 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
|
|
|
3286
3781
|
const { db: db3 } = await postgresClient();
|
|
3287
3782
|
return {
|
|
3288
3783
|
req,
|
|
3289
|
-
db: db3
|
|
3784
|
+
db: db3,
|
|
3785
|
+
user: authenticationResult.user
|
|
3290
3786
|
};
|
|
3291
3787
|
}
|
|
3292
3788
|
})
|
|
@@ -3306,7 +3802,10 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
|
|
|
3306
3802
|
return;
|
|
3307
3803
|
}
|
|
3308
3804
|
const { db: db3 } = await postgresClient();
|
|
3309
|
-
|
|
3805
|
+
let query = db3("agents");
|
|
3806
|
+
query.select("*");
|
|
3807
|
+
query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
|
|
3808
|
+
const agentsFromDb = await query;
|
|
3310
3809
|
res.status(200).json(agentsFromDb.map((agent) => {
|
|
3311
3810
|
const backend = agents.find((a) => a.id === agent.backend);
|
|
3312
3811
|
if (!backend) {
|
|
@@ -3316,15 +3815,16 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
|
|
|
3316
3815
|
name: agent.name,
|
|
3317
3816
|
id: agent.id,
|
|
3318
3817
|
description: agent.description,
|
|
3319
|
-
provider: backend
|
|
3320
|
-
model: backend
|
|
3818
|
+
provider: backend.providerName,
|
|
3819
|
+
model: backend.modelName,
|
|
3321
3820
|
active: agent.active,
|
|
3322
|
-
public: agent.public,
|
|
3323
3821
|
type: agent.type,
|
|
3822
|
+
rights_mode: agent.rights_mode,
|
|
3324
3823
|
slug: backend?.slug,
|
|
3325
3824
|
rateLimit: backend?.rateLimit,
|
|
3326
3825
|
streaming: backend?.streaming,
|
|
3327
3826
|
capabilities: backend?.capabilities,
|
|
3827
|
+
RBAC: agent.RBAC,
|
|
3328
3828
|
// todo add contexts
|
|
3329
3829
|
availableTools: tools,
|
|
3330
3830
|
enabledTools: agent.tools
|
|
@@ -3345,7 +3845,11 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
|
|
|
3345
3845
|
});
|
|
3346
3846
|
return;
|
|
3347
3847
|
}
|
|
3348
|
-
|
|
3848
|
+
let query = db3("agents");
|
|
3849
|
+
query.select("*");
|
|
3850
|
+
query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
|
|
3851
|
+
query.where({ id });
|
|
3852
|
+
const agent = await query.first();
|
|
3349
3853
|
if (!agent) {
|
|
3350
3854
|
res.status(400).json({
|
|
3351
3855
|
message: "Agent not found in database."
|
|
@@ -3354,6 +3858,8 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
|
|
|
3354
3858
|
}
|
|
3355
3859
|
console.log("[EXULU] agent", agent);
|
|
3356
3860
|
const backend = agents.find((a) => a.id === agent.backend);
|
|
3861
|
+
console.log("[EXULU] agent", agent);
|
|
3862
|
+
const RBAC = await RBACResolver(db3, agentsSchema2(), agentsSchema2().name.singular, agent.id, agent.rights_mode);
|
|
3357
3863
|
res.status(200).json({
|
|
3358
3864
|
...{
|
|
3359
3865
|
name: agent.name,
|
|
@@ -3364,9 +3870,11 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
|
|
|
3364
3870
|
active: agent.active,
|
|
3365
3871
|
public: agent.public,
|
|
3366
3872
|
type: agent.type,
|
|
3873
|
+
rights_mode: agent.rights_mode,
|
|
3367
3874
|
slug: backend?.slug,
|
|
3368
3875
|
rateLimit: backend?.rateLimit,
|
|
3369
3876
|
streaming: backend?.streaming,
|
|
3877
|
+
RBAC,
|
|
3370
3878
|
capabilities: backend?.capabilities,
|
|
3371
3879
|
providerApiKey: agent.providerApiKey,
|
|
3372
3880
|
// todo add contexts
|
|
@@ -4225,9 +4733,11 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
|
|
|
4225
4733
|
return;
|
|
4226
4734
|
}
|
|
4227
4735
|
const { db: db3 } = await postgresClient();
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4736
|
+
let query = db3("agents");
|
|
4737
|
+
query.select("*");
|
|
4738
|
+
query = applyAccessControl(agentsSchema2(), authenticationResult.user, query);
|
|
4739
|
+
query.where({ id: req.params.id });
|
|
4740
|
+
const agent = await query.first();
|
|
4231
4741
|
if (!agent) {
|
|
4232
4742
|
const arrayBuffer = createCustomAnthropicStreamingMessage(`
|
|
4233
4743
|
\x1B[41m -- Agent ${req.params.id} not found or you do not have access to it. --
|
|
@@ -6093,10 +6603,10 @@ var SentenceChunker = class _SentenceChunker extends BaseChunker {
|
|
|
6093
6603
|
};
|
|
6094
6604
|
|
|
6095
6605
|
// src/auth/generate-key.ts
|
|
6096
|
-
var
|
|
6606
|
+
var import_bcryptjs3 = __toESM(require("bcryptjs"), 1);
|
|
6097
6607
|
var SALT_ROUNDS = 12;
|
|
6098
6608
|
async function encryptString(string) {
|
|
6099
|
-
const hash = await
|
|
6609
|
+
const hash = await import_bcryptjs3.default.hash(string, SALT_ROUNDS);
|
|
6100
6610
|
return hash;
|
|
6101
6611
|
}
|
|
6102
6612
|
var generateApiKey = async (name, email) => {
|
|
@@ -6144,127 +6654,63 @@ var generateApiKey = async (name, email) => {
|
|
|
6144
6654
|
};
|
|
6145
6655
|
|
|
6146
6656
|
// src/postgres/init-db.ts
|
|
6657
|
+
var { agentsSchema: agentsSchema3, evalResultsSchema: evalResultsSchema3, jobsSchema: jobsSchema3, agentSessionsSchema: agentSessionsSchema3, agentMessagesSchema: agentMessagesSchema3, rolesSchema: rolesSchema3, usersSchema: usersSchema3, statisticsSchema: statisticsSchema3, variablesSchema: variablesSchema3, workflowTemplatesSchema: workflowTemplatesSchema3, rbacSchema: rbacSchema3 } = coreSchemas.get();
|
|
6658
|
+
var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
|
|
6659
|
+
for (const field of fields) {
|
|
6660
|
+
const { type, name, default: defaultValue, unique } = field;
|
|
6661
|
+
if (!type || !name) {
|
|
6662
|
+
continue;
|
|
6663
|
+
}
|
|
6664
|
+
const sanitizedName = sanitizeName(name);
|
|
6665
|
+
if (skipFields.includes(name)) {
|
|
6666
|
+
continue;
|
|
6667
|
+
}
|
|
6668
|
+
const hasColumn = await knex.schema.hasColumn(tableName, sanitizedName);
|
|
6669
|
+
if (!hasColumn) {
|
|
6670
|
+
console.log(`[EXULU] Adding missing field '${sanitizedName}' to ${tableName} table.`);
|
|
6671
|
+
await knex.schema.alterTable(tableName, (table) => {
|
|
6672
|
+
mapType(table, type, sanitizedName, defaultValue, unique);
|
|
6673
|
+
});
|
|
6674
|
+
}
|
|
6675
|
+
console.log(`[EXULU] Field '${sanitizedName}' already exists in ${tableName} table.`);
|
|
6676
|
+
}
|
|
6677
|
+
};
|
|
6147
6678
|
var up = async function(knex) {
|
|
6148
6679
|
console.log("[EXULU] Database up.");
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
}
|
|
6175
|
-
mapType(table, type, sanitizeName(name), defaultValue, unique);
|
|
6176
|
-
}
|
|
6177
|
-
});
|
|
6178
|
-
}
|
|
6179
|
-
if (!await knex.schema.hasTable("roles")) {
|
|
6180
|
-
console.log("[EXULU] Creating roles table.");
|
|
6181
|
-
await knex.schema.createTable("roles", (table) => {
|
|
6182
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
6183
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
6184
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
6185
|
-
for (const field of rolesSchema.fields) {
|
|
6186
|
-
const { type, name, default: defaultValue, unique } = field;
|
|
6187
|
-
if (!type || !name) {
|
|
6188
|
-
continue;
|
|
6189
|
-
}
|
|
6190
|
-
mapType(table, type, sanitizeName(name), defaultValue, unique);
|
|
6191
|
-
}
|
|
6192
|
-
});
|
|
6193
|
-
}
|
|
6194
|
-
if (!await knex.schema.hasTable("eval_results")) {
|
|
6195
|
-
console.log("[EXULU] Creating eval_results table.");
|
|
6196
|
-
await knex.schema.createTable("eval_results", (table) => {
|
|
6197
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
6198
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
6199
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
6200
|
-
for (const field of evalResultsSchema.fields) {
|
|
6201
|
-
const { type, name, default: defaultValue, unique } = field;
|
|
6202
|
-
if (!type || !name) {
|
|
6203
|
-
continue;
|
|
6204
|
-
}
|
|
6205
|
-
mapType(table, type, sanitizeName(name), defaultValue, unique);
|
|
6206
|
-
}
|
|
6207
|
-
});
|
|
6208
|
-
}
|
|
6209
|
-
if (!await knex.schema.hasTable("statistics")) {
|
|
6210
|
-
console.log("[EXULU] Creating statistics table.");
|
|
6211
|
-
await knex.schema.createTable("statistics", (table) => {
|
|
6212
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
6213
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
6214
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
6215
|
-
for (const field of statisticsSchema.fields) {
|
|
6216
|
-
const { type, name, default: defaultValue, unique } = field;
|
|
6217
|
-
if (!type || !name) {
|
|
6218
|
-
continue;
|
|
6219
|
-
}
|
|
6220
|
-
mapType(table, type, sanitizeName(name), defaultValue, unique);
|
|
6221
|
-
}
|
|
6222
|
-
});
|
|
6223
|
-
}
|
|
6224
|
-
if (!await knex.schema.hasTable("jobs")) {
|
|
6225
|
-
console.log("[EXULU] Creating jobs table.");
|
|
6226
|
-
await knex.schema.createTable("jobs", (table) => {
|
|
6227
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
6228
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
6229
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
6230
|
-
for (const field of jobsSchema.fields) {
|
|
6231
|
-
const { type, name, default: defaultValue, unique } = field;
|
|
6232
|
-
if (!type || !name) {
|
|
6233
|
-
continue;
|
|
6234
|
-
}
|
|
6235
|
-
mapType(table, type, sanitizeName(name), defaultValue, unique);
|
|
6236
|
-
}
|
|
6237
|
-
});
|
|
6238
|
-
}
|
|
6239
|
-
if (!await knex.schema.hasTable("agents")) {
|
|
6240
|
-
console.log("[EXULU] Creating agents table.");
|
|
6241
|
-
await knex.schema.createTable("agents", (table) => {
|
|
6242
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
6243
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
6244
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
6245
|
-
for (const field of agentsSchema.fields) {
|
|
6246
|
-
const { type, name, default: defaultValue, unique } = field;
|
|
6247
|
-
if (!type || !name) {
|
|
6248
|
-
continue;
|
|
6249
|
-
}
|
|
6250
|
-
mapType(table, type, sanitizeName(name), defaultValue, unique);
|
|
6251
|
-
}
|
|
6252
|
-
});
|
|
6253
|
-
}
|
|
6254
|
-
if (!await knex.schema.hasTable("variables")) {
|
|
6255
|
-
console.log("[EXULU] Creating variables table.");
|
|
6256
|
-
await knex.schema.createTable("variables", (table) => {
|
|
6257
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
6258
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
6259
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
6260
|
-
for (const field of variablesSchema.fields) {
|
|
6261
|
-
const { type, name, default: defaultValue, unique } = field;
|
|
6262
|
-
if (!type || !name) {
|
|
6263
|
-
continue;
|
|
6680
|
+
const schemas = [
|
|
6681
|
+
agentSessionsSchema3(),
|
|
6682
|
+
agentMessagesSchema3(),
|
|
6683
|
+
rolesSchema3(),
|
|
6684
|
+
evalResultsSchema3(),
|
|
6685
|
+
statisticsSchema3(),
|
|
6686
|
+
jobsSchema3(),
|
|
6687
|
+
rbacSchema3(),
|
|
6688
|
+
agentsSchema3(),
|
|
6689
|
+
variablesSchema3(),
|
|
6690
|
+
workflowTemplatesSchema3()
|
|
6691
|
+
];
|
|
6692
|
+
const createTable = async (schema) => {
|
|
6693
|
+
if (!await knex.schema.hasTable(schema.name.plural)) {
|
|
6694
|
+
console.log(`[EXULU] Creating ${schema.name.plural} table.`);
|
|
6695
|
+
await knex.schema.createTable(schema.name.plural, (table) => {
|
|
6696
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
6697
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
6698
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
6699
|
+
for (const field of schema.fields) {
|
|
6700
|
+
const { type, name, default: defaultValue, unique } = field;
|
|
6701
|
+
if (!type || !name) {
|
|
6702
|
+
continue;
|
|
6703
|
+
}
|
|
6704
|
+
mapType(table, type, sanitizeName(name), defaultValue, unique);
|
|
6264
6705
|
}
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6706
|
+
});
|
|
6707
|
+
} else {
|
|
6708
|
+
console.log(`[EXULU] Checking missing fields to ${schema.name.plural} table.`);
|
|
6709
|
+
await addMissingFields(knex, schema.name.plural, schema.fields);
|
|
6710
|
+
}
|
|
6711
|
+
};
|
|
6712
|
+
for (const schema of schemas) {
|
|
6713
|
+
await createTable(schema);
|
|
6268
6714
|
}
|
|
6269
6715
|
if (!await knex.schema.hasTable("verification_token")) {
|
|
6270
6716
|
console.log("[EXULU] Creating verification_token table.");
|
|
@@ -6286,10 +6732,10 @@ var up = async function(knex) {
|
|
|
6286
6732
|
table.string("email", 255);
|
|
6287
6733
|
table.timestamp("emailVerified", { useTz: true });
|
|
6288
6734
|
table.text("image");
|
|
6289
|
-
for (const field of
|
|
6735
|
+
for (const field of usersSchema3().fields) {
|
|
6290
6736
|
console.log("[EXULU] field", field);
|
|
6291
6737
|
const { type, name, default: defaultValue, unique } = field;
|
|
6292
|
-
if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
|
|
6738
|
+
if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image" || name === "password") {
|
|
6293
6739
|
continue;
|
|
6294
6740
|
}
|
|
6295
6741
|
if (!type || !name) {
|
|
@@ -6298,6 +6744,8 @@ var up = async function(knex) {
|
|
|
6298
6744
|
mapType(table, type, sanitizeName(name), defaultValue, unique);
|
|
6299
6745
|
}
|
|
6300
6746
|
});
|
|
6747
|
+
} else {
|
|
6748
|
+
await addMissingFields(knex, "users", usersSchema3().fields, ["id", "name", "email", "emailVerified", "image"]);
|
|
6301
6749
|
}
|
|
6302
6750
|
if (!await knex.schema.hasTable("accounts")) {
|
|
6303
6751
|
console.log("[EXULU] Creating accounts table.");
|