@exulu/backend 0.1.8 → 0.2.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
@@ -58,19 +58,26 @@ var redisServer = {
58
58
  // src/redis/client.ts
59
59
  var client = {};
60
60
  async function redisClient() {
61
- if (!redisServer.host || !redisServer.port) {
61
+ console.log("[EXULU] redisClient", redisServer);
62
+ if (!redisServer.host?.length || !redisServer.port?.length) {
63
+ console.error(`[EXULU] no redis server configured.`);
62
64
  return {
63
65
  client: null
64
66
  };
65
67
  }
66
68
  if (!client["exulu"]) {
67
- const url = `redis://${redisServer.host}:${redisServer.port}`;
68
- console.log(`[EXULU] connecting to redis.`);
69
- client["exulu"] = (0, import_redis.createClient)({
70
- // todo add password
71
- url
72
- });
73
- await client["exulu"].connect();
69
+ try {
70
+ const url = `redis://${redisServer.host}:${redisServer.port}`;
71
+ console.log(`[EXULU] connecting to redis.`);
72
+ client["exulu"] = (0, import_redis.createClient)({
73
+ // todo add password
74
+ url
75
+ });
76
+ await client["exulu"].connect();
77
+ } catch (error) {
78
+ console.error(`[EXULU] error connecting to redis.`, error);
79
+ throw error;
80
+ }
74
81
  }
75
82
  return {
76
83
  client: client["exulu"]
@@ -113,25 +120,30 @@ var import_knex3 = require("pgvector/knex");
113
120
  var db = {};
114
121
  async function postgresClient() {
115
122
  if (!db["exulu"]) {
116
- console.log("[EXULU] Initializing exulu database.");
117
- console.log(process.env.POSTGRES_DB_HOST);
118
- console.log(process.env.POSTGRES_DB_PORT);
119
- console.log(process.env.POSTGRES_DB_USER);
120
- console.log(process.env.POSTGRES_DB_PASSWORD);
121
- console.log(process.env.POSTGRES_DB_SSL);
122
- const knex = (0, import_knex.default)({
123
- client: "pg",
124
- connection: {
125
- host: process.env.POSTGRES_DB_HOST,
126
- port: parseInt(process.env.POSTGRES_DB_PORT || "5432"),
127
- user: process.env.POSTGRES_DB_USER,
128
- database: "exulu",
129
- password: process.env.POSTGRES_DB_PASSWORD,
130
- ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
131
- }
132
- });
133
- await knex.schema.createExtensionIfNotExists("vector");
134
- db["exulu"] = knex;
123
+ try {
124
+ console.log("[EXULU] Initializing exulu database.");
125
+ console.log(process.env.POSTGRES_DB_HOST);
126
+ console.log(process.env.POSTGRES_DB_PORT);
127
+ console.log(process.env.POSTGRES_DB_USER);
128
+ console.log(process.env.POSTGRES_DB_PASSWORD);
129
+ console.log(process.env.POSTGRES_DB_SSL);
130
+ const knex = (0, import_knex.default)({
131
+ client: "pg",
132
+ connection: {
133
+ host: process.env.POSTGRES_DB_HOST,
134
+ port: parseInt(process.env.POSTGRES_DB_PORT || "5432"),
135
+ user: process.env.POSTGRES_DB_USER,
136
+ database: "exulu",
137
+ password: process.env.POSTGRES_DB_PASSWORD,
138
+ ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
139
+ }
140
+ });
141
+ await knex.schema.createExtensionIfNotExists("vector");
142
+ db["exulu"] = knex;
143
+ } catch (error) {
144
+ console.error("[EXULU] Error initializing exulu database.", error);
145
+ throw error;
146
+ }
135
147
  }
136
148
  return {
137
149
  db: db["exulu"]
@@ -398,6 +410,7 @@ var sanitizeName = (name) => {
398
410
  };
399
411
 
400
412
  // src/postgres/init-db.ts
413
+ var import_bcryptjs = __toESM(require("bcryptjs"), 1);
401
414
  var up = async function(knex) {
402
415
  if (!await knex.schema.hasTable("roles")) {
403
416
  await knex.schema.createTable("roles", (table) => {
@@ -545,6 +558,11 @@ var up = async function(knex) {
545
558
  });
546
559
  }
547
560
  };
561
+ var SALT_ROUNDS = 12;
562
+ async function encryptApiKey(apikey) {
563
+ const hash = await import_bcryptjs.default.hash(apikey, SALT_ROUNDS);
564
+ return hash;
565
+ }
548
566
  var execute = async () => {
549
567
  console.log("[EXULU] Initializing database.");
550
568
  const { db: db2 } = await postgresClient();
@@ -563,6 +581,10 @@ var execute = async () => {
563
581
  } else {
564
582
  roleId = existingRole.id;
565
583
  }
584
+ const newKeyName = "exulu_default_key";
585
+ const plainKey = `sk_${Math.random().toString(36).substring(2, 15)}_${Math.random().toString(36).substring(2, 15)}`;
586
+ const postFix = `/${newKeyName.toLowerCase().trim().replaceAll(" ", "_")}`;
587
+ const encryptedKey = await encryptApiKey(plainKey);
566
588
  const existingUser = await db2.from("users").where({ email: "admin@exulu.com" }).first();
567
589
  if (!existingUser) {
568
590
  console.log("[EXULU] Creating default admin user.");
@@ -572,11 +594,28 @@ var execute = async () => {
572
594
  super_admin: true,
573
595
  createdAt: /* @__PURE__ */ new Date(),
574
596
  updatedAt: /* @__PURE__ */ new Date(),
597
+ type: "user",
598
+ // password: "admin", todo add this again when we implement password auth / encryption as alternative to OTP
599
+ role: roleId
600
+ });
601
+ }
602
+ const existingApiUser = await db2.from("users").where({ email: "api@exulu.com" }).first();
603
+ if (!existingApiUser) {
604
+ console.log("[EXULU] Creating default api user.");
605
+ await db2.from("users").insert({
606
+ name: "exulu",
607
+ email: "admin@exulu.com",
608
+ super_admin: true,
609
+ createdAt: /* @__PURE__ */ new Date(),
610
+ updatedAt: /* @__PURE__ */ new Date(),
611
+ type: "api",
612
+ apikey: `${encryptedKey}${postFix}`,
575
613
  // password: "admin", todo add this again when we implement password auth / encryption as alternative to OTP
576
614
  role: roleId
577
615
  });
578
616
  }
579
617
  console.log("[EXULU] Database initialized.");
618
+ console.log("[EXULU] Default api key: ", `${encryptedKey}${postFix}`);
580
619
  return;
581
620
  };
582
621
 
@@ -1428,7 +1467,7 @@ var import_express = require("express");
1428
1467
  var import_jwt = require("next-auth/jwt");
1429
1468
 
1430
1469
  // src/auth/auth.ts
1431
- var import_bcryptjs = __toESM(require("bcryptjs"), 1);
1470
+ var import_bcryptjs2 = __toESM(require("bcryptjs"), 1);
1432
1471
  var authentication = async ({
1433
1472
  apikey,
1434
1473
  authtoken,
@@ -1519,11 +1558,11 @@ var authentication = async ({
1519
1558
  code: 401
1520
1559
  };
1521
1560
  }
1522
- const filtered = users.filter(({ apiKey, id }) => apiKey.includes(keyName));
1561
+ const filtered = users.filter(({ apikey: apikey2, id }) => apikey2.includes(keyName));
1523
1562
  for (const user of filtered) {
1524
- const lastSlashIndex = user.apiKey.lastIndexOf("/");
1525
- const compareValue = lastSlashIndex !== -1 ? user.apiKey.substring(0, lastSlashIndex) : user.apiKey;
1526
- const isMatch = await import_bcryptjs.default.compare(keyValue, compareValue);
1563
+ const lastSlashIndex = user.apikey.lastIndexOf("/");
1564
+ const compareValue = lastSlashIndex !== -1 ? user.apikey.substring(0, lastSlashIndex) : user.apikey;
1565
+ const isMatch = await import_bcryptjs2.default.compare(keyValue, compareValue);
1527
1566
  if (isMatch) {
1528
1567
  await db2.from("users").where({ id: user.id }).update({
1529
1568
  lastUsed: /* @__PURE__ */ new Date()
@@ -1714,6 +1753,10 @@ var ExuluQueues = class {
1714
1753
  if (existing) {
1715
1754
  return existing;
1716
1755
  }
1756
+ if (!redisServer.host?.length || !redisServer.port?.length) {
1757
+ console.error(`[EXULU] no redis server configured, but you are trying to use a queue ( ${name}), likely in an agent or workflow (look for ExuluQueues.use() ).`);
1758
+ throw new Error(`[EXULU] no redis server configured.`);
1759
+ }
1717
1760
  const newQueue = new import_bullmq5.Queue(`${name}`, { connection: redisServer });
1718
1761
  this.queues.push(newQueue);
1719
1762
  return newQueue;
@@ -2133,9 +2176,6 @@ var createUppyRoutes = async (app) => {
2133
2176
  return stsClient;
2134
2177
  }
2135
2178
  app.use(bodyParser.urlencoded({ extended: true }), bodyParser.json());
2136
- app.get("/", (req, res) => {
2137
- res.json("Exulu upload server.");
2138
- });
2139
2179
  app.get("/s3/list", async (req, res, next) => {
2140
2180
  const apikey = req.headers["exulu-api-key"] || null;
2141
2181
  let authtoken = null;
@@ -2562,7 +2602,11 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
2562
2602
  { route: "/items/export/:context", method: "GET", note: "Export items from context" },
2563
2603
  { route: "/graphql", method: "POST", note: "GraphQL endpoint" }
2564
2604
  );
2565
- await createRecurringJobs();
2605
+ if (redisServer.host?.length && redisServer.port?.length) {
2606
+ await createRecurringJobs();
2607
+ } else {
2608
+ console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
2609
+ }
2566
2610
  const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema]);
2567
2611
  const server = new import_server3.ApolloServer({ schema, introspection: true });
2568
2612
  await server.start();
@@ -2622,7 +2666,6 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
2622
2666
  }
2623
2667
  });
2624
2668
  });
2625
- console.log("tools", tools);
2626
2669
  app.get("/tools", async (req, res) => {
2627
2670
  res.status(200).json(tools.map((tool) => ({
2628
2671
  id: tool.id,
@@ -3283,6 +3326,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3283
3326
  return;
3284
3327
  }
3285
3328
  if (agent.rateLimit) {
3329
+ console.log("[EXULU] rate limiting agent.", agent.rateLimit);
3286
3330
  const limit = await rateLimiter(
3287
3331
  agent.rateLimit.name || agent.id,
3288
3332
  agent.rateLimit.rate_limit.time,
package/dist/index.js CHANGED
@@ -17,19 +17,26 @@ var redisServer = {
17
17
  // src/redis/client.ts
18
18
  var client = {};
19
19
  async function redisClient() {
20
- if (!redisServer.host || !redisServer.port) {
20
+ console.log("[EXULU] redisClient", redisServer);
21
+ if (!redisServer.host?.length || !redisServer.port?.length) {
22
+ console.error(`[EXULU] no redis server configured.`);
21
23
  return {
22
24
  client: null
23
25
  };
24
26
  }
25
27
  if (!client["exulu"]) {
26
- const url = `redis://${redisServer.host}:${redisServer.port}`;
27
- console.log(`[EXULU] connecting to redis.`);
28
- client["exulu"] = createClient({
29
- // todo add password
30
- url
31
- });
32
- await client["exulu"].connect();
28
+ try {
29
+ const url = `redis://${redisServer.host}:${redisServer.port}`;
30
+ console.log(`[EXULU] connecting to redis.`);
31
+ client["exulu"] = createClient({
32
+ // todo add password
33
+ url
34
+ });
35
+ await client["exulu"].connect();
36
+ } catch (error) {
37
+ console.error(`[EXULU] error connecting to redis.`, error);
38
+ throw error;
39
+ }
33
40
  }
34
41
  return {
35
42
  client: client["exulu"]
@@ -72,25 +79,30 @@ import "pgvector/knex";
72
79
  var db = {};
73
80
  async function postgresClient() {
74
81
  if (!db["exulu"]) {
75
- console.log("[EXULU] Initializing exulu database.");
76
- console.log(process.env.POSTGRES_DB_HOST);
77
- console.log(process.env.POSTGRES_DB_PORT);
78
- console.log(process.env.POSTGRES_DB_USER);
79
- console.log(process.env.POSTGRES_DB_PASSWORD);
80
- console.log(process.env.POSTGRES_DB_SSL);
81
- const knex = Knex({
82
- client: "pg",
83
- connection: {
84
- host: process.env.POSTGRES_DB_HOST,
85
- port: parseInt(process.env.POSTGRES_DB_PORT || "5432"),
86
- user: process.env.POSTGRES_DB_USER,
87
- database: "exulu",
88
- password: process.env.POSTGRES_DB_PASSWORD,
89
- ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
90
- }
91
- });
92
- await knex.schema.createExtensionIfNotExists("vector");
93
- db["exulu"] = knex;
82
+ try {
83
+ console.log("[EXULU] Initializing exulu database.");
84
+ console.log(process.env.POSTGRES_DB_HOST);
85
+ console.log(process.env.POSTGRES_DB_PORT);
86
+ console.log(process.env.POSTGRES_DB_USER);
87
+ console.log(process.env.POSTGRES_DB_PASSWORD);
88
+ console.log(process.env.POSTGRES_DB_SSL);
89
+ const knex = Knex({
90
+ client: "pg",
91
+ connection: {
92
+ host: process.env.POSTGRES_DB_HOST,
93
+ port: parseInt(process.env.POSTGRES_DB_PORT || "5432"),
94
+ user: process.env.POSTGRES_DB_USER,
95
+ database: "exulu",
96
+ password: process.env.POSTGRES_DB_PASSWORD,
97
+ ssl: process.env.POSTGRES_DB_SSL === "true" ? { rejectUnauthorized: false } : false
98
+ }
99
+ });
100
+ await knex.schema.createExtensionIfNotExists("vector");
101
+ db["exulu"] = knex;
102
+ } catch (error) {
103
+ console.error("[EXULU] Error initializing exulu database.", error);
104
+ throw error;
105
+ }
94
106
  }
95
107
  return {
96
108
  db: db["exulu"]
@@ -357,6 +369,7 @@ var sanitizeName = (name) => {
357
369
  };
358
370
 
359
371
  // src/postgres/init-db.ts
372
+ import bcrypt from "bcryptjs";
360
373
  var up = async function(knex) {
361
374
  if (!await knex.schema.hasTable("roles")) {
362
375
  await knex.schema.createTable("roles", (table) => {
@@ -504,6 +517,11 @@ var up = async function(knex) {
504
517
  });
505
518
  }
506
519
  };
520
+ var SALT_ROUNDS = 12;
521
+ async function encryptApiKey(apikey) {
522
+ const hash = await bcrypt.hash(apikey, SALT_ROUNDS);
523
+ return hash;
524
+ }
507
525
  var execute = async () => {
508
526
  console.log("[EXULU] Initializing database.");
509
527
  const { db: db2 } = await postgresClient();
@@ -522,6 +540,10 @@ var execute = async () => {
522
540
  } else {
523
541
  roleId = existingRole.id;
524
542
  }
543
+ const newKeyName = "exulu_default_key";
544
+ const plainKey = `sk_${Math.random().toString(36).substring(2, 15)}_${Math.random().toString(36).substring(2, 15)}`;
545
+ const postFix = `/${newKeyName.toLowerCase().trim().replaceAll(" ", "_")}`;
546
+ const encryptedKey = await encryptApiKey(plainKey);
525
547
  const existingUser = await db2.from("users").where({ email: "admin@exulu.com" }).first();
526
548
  if (!existingUser) {
527
549
  console.log("[EXULU] Creating default admin user.");
@@ -531,11 +553,28 @@ var execute = async () => {
531
553
  super_admin: true,
532
554
  createdAt: /* @__PURE__ */ new Date(),
533
555
  updatedAt: /* @__PURE__ */ new Date(),
556
+ type: "user",
557
+ // password: "admin", todo add this again when we implement password auth / encryption as alternative to OTP
558
+ role: roleId
559
+ });
560
+ }
561
+ const existingApiUser = await db2.from("users").where({ email: "api@exulu.com" }).first();
562
+ if (!existingApiUser) {
563
+ console.log("[EXULU] Creating default api user.");
564
+ await db2.from("users").insert({
565
+ name: "exulu",
566
+ email: "admin@exulu.com",
567
+ super_admin: true,
568
+ createdAt: /* @__PURE__ */ new Date(),
569
+ updatedAt: /* @__PURE__ */ new Date(),
570
+ type: "api",
571
+ apikey: `${encryptedKey}${postFix}`,
534
572
  // password: "admin", todo add this again when we implement password auth / encryption as alternative to OTP
535
573
  role: roleId
536
574
  });
537
575
  }
538
576
  console.log("[EXULU] Database initialized.");
577
+ console.log("[EXULU] Default api key: ", `${encryptedKey}${postFix}`);
539
578
  return;
540
579
  };
541
580
 
@@ -1387,7 +1426,7 @@ import "express";
1387
1426
  import { getToken } from "next-auth/jwt";
1388
1427
 
1389
1428
  // src/auth/auth.ts
1390
- import bcrypt from "bcryptjs";
1429
+ import bcrypt2 from "bcryptjs";
1391
1430
  var authentication = async ({
1392
1431
  apikey,
1393
1432
  authtoken,
@@ -1478,11 +1517,11 @@ var authentication = async ({
1478
1517
  code: 401
1479
1518
  };
1480
1519
  }
1481
- const filtered = users.filter(({ apiKey, id }) => apiKey.includes(keyName));
1520
+ const filtered = users.filter(({ apikey: apikey2, id }) => apikey2.includes(keyName));
1482
1521
  for (const user of filtered) {
1483
- const lastSlashIndex = user.apiKey.lastIndexOf("/");
1484
- const compareValue = lastSlashIndex !== -1 ? user.apiKey.substring(0, lastSlashIndex) : user.apiKey;
1485
- const isMatch = await bcrypt.compare(keyValue, compareValue);
1522
+ const lastSlashIndex = user.apikey.lastIndexOf("/");
1523
+ const compareValue = lastSlashIndex !== -1 ? user.apikey.substring(0, lastSlashIndex) : user.apikey;
1524
+ const isMatch = await bcrypt2.compare(keyValue, compareValue);
1486
1525
  if (isMatch) {
1487
1526
  await db2.from("users").where({ id: user.id }).update({
1488
1527
  lastUsed: /* @__PURE__ */ new Date()
@@ -1673,6 +1712,10 @@ var ExuluQueues = class {
1673
1712
  if (existing) {
1674
1713
  return existing;
1675
1714
  }
1715
+ if (!redisServer.host?.length || !redisServer.port?.length) {
1716
+ console.error(`[EXULU] no redis server configured, but you are trying to use a queue ( ${name}), likely in an agent or workflow (look for ExuluQueues.use() ).`);
1717
+ throw new Error(`[EXULU] no redis server configured.`);
1718
+ }
1676
1719
  const newQueue = new Queue3(`${name}`, { connection: redisServer });
1677
1720
  this.queues.push(newQueue);
1678
1721
  return newQueue;
@@ -2092,9 +2135,6 @@ var createUppyRoutes = async (app) => {
2092
2135
  return stsClient;
2093
2136
  }
2094
2137
  app.use(bodyParser.urlencoded({ extended: true }), bodyParser.json());
2095
- app.get("/", (req, res) => {
2096
- res.json("Exulu upload server.");
2097
- });
2098
2138
  app.get("/s3/list", async (req, res, next) => {
2099
2139
  const apikey = req.headers["exulu-api-key"] || null;
2100
2140
  let authtoken = null;
@@ -2521,7 +2561,11 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
2521
2561
  { route: "/items/export/:context", method: "GET", note: "Export items from context" },
2522
2562
  { route: "/graphql", method: "POST", note: "GraphQL endpoint" }
2523
2563
  );
2524
- await createRecurringJobs();
2564
+ if (redisServer.host?.length && redisServer.port?.length) {
2565
+ await createRecurringJobs();
2566
+ } else {
2567
+ console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
2568
+ }
2525
2569
  const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema]);
2526
2570
  const server = new ApolloServer({ schema, introspection: true });
2527
2571
  await server.start();
@@ -2581,7 +2625,6 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
2581
2625
  }
2582
2626
  });
2583
2627
  });
2584
- console.log("tools", tools);
2585
2628
  app.get("/tools", async (req, res) => {
2586
2629
  res.status(200).json(tools.map((tool) => ({
2587
2630
  id: tool.id,
@@ -3242,6 +3285,7 @@ var createExpressRoutes = async (app, agents, embedders, tools, workflows, conte
3242
3285
  return;
3243
3286
  }
3244
3287
  if (agent.rateLimit) {
3288
+ console.log("[EXULU] rate limiting agent.", agent.rateLimit);
3245
3289
  const limit = await rateLimiter(
3246
3290
  agent.rateLimit.name || agent.id,
3247
3291
  agent.rateLimit.rate_limit.time,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exulu/backend",
3
3
  "author": "Qventu Bv.",
4
- "version": "0.1.8",
4
+ "version": "0.2.0",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {