@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/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 tableNameSingular = table.name.singular.toLowerCase();
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 validateSuperAdminPermission(tableNamePlural, input, req);
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 validateSuperAdminPermission(tableNamePlural, input, req);
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
- const result = await db3.from(tableNamePlural).select(requestedFields).where({ id: args.id }).first();
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 baseQuery = db3(tableNamePlural);
2200
- baseQuery = applyFilters(baseQuery, filters);
2201
- const [{ count }] = await baseQuery.clone().count("* as count");
2202
- const itemCount = Number(count);
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 = baseQuery.clone();
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 jobStatistics query for jobs table
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 fullSDL = typeDefs + mutationDefs + modelDefs;
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 validateSuperAdminPermission = async (tableNamePlural, input, req) => {
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 statisticsSchema = {
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([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema, variablesSchema]);
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
- const agentsFromDb = await db3.from("agents").select("*");
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?.model?.provider,
3320
- model: backend?.model?.modelId,
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
- const agent = await db3.from("agents").where({ id }).first();
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
- const agent = await db3.from("agents").where({
4229
- id: req.params.id
4230
- }).first();
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 import_bcryptjs2 = __toESM(require("bcryptjs"), 1);
6606
+ var import_bcryptjs3 = __toESM(require("bcryptjs"), 1);
6097
6607
  var SALT_ROUNDS = 12;
6098
6608
  async function encryptString(string) {
6099
- const hash = await import_bcryptjs2.default.hash(string, SALT_ROUNDS);
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
- if (!await knex.schema.hasTable("agent_sessions")) {
6150
- console.log("[EXULU] Creating agent_sessions table.");
6151
- await knex.schema.createTable("agent_sessions", (table) => {
6152
- table.uuid("id").primary().defaultTo(knex.fn.uuid());
6153
- table.timestamp("createdAt").defaultTo(knex.fn.now());
6154
- table.timestamp("updatedAt").defaultTo(knex.fn.now());
6155
- for (const field of agentSessionsSchema.fields) {
6156
- const { type, name, default: defaultValue, unique } = field;
6157
- if (!type || !name) {
6158
- continue;
6159
- }
6160
- mapType(table, type, sanitizeName(name), defaultValue, unique);
6161
- }
6162
- });
6163
- }
6164
- if (!await knex.schema.hasTable("agent_messages")) {
6165
- console.log("[EXULU] Creating agent_messages table.");
6166
- await knex.schema.createTable("agent_messages", (table) => {
6167
- table.uuid("id").primary().defaultTo(knex.fn.uuid());
6168
- table.timestamp("createdAt").defaultTo(knex.fn.now());
6169
- table.timestamp("updatedAt").defaultTo(knex.fn.now());
6170
- for (const field of agentMessagesSchema.fields) {
6171
- const { type, name, default: defaultValue, unique } = field;
6172
- if (!type || !name) {
6173
- continue;
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
- mapType(table, type, sanitizeName(name), defaultValue, unique);
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 usersSchema.fields) {
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.");