@dnax/core 0.66.0 → 0.66.3

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/app/ctrl.ts CHANGED
@@ -20,6 +20,25 @@ const actions = [
20
20
  "listActivity",
21
21
  ];
22
22
 
23
+ const forbiddenPipelineOperators = [
24
+ "$merge",
25
+ "$out",
26
+ "$currentOp",
27
+ "$function",
28
+ "$accumulator",
29
+ "$collMod",
30
+ "$collStats",
31
+ "$explain",
32
+ "$indexStats",
33
+ "$planCacheStats",
34
+ "$where",
35
+ "$listLocalSessions",
36
+ "$listSessions",
37
+ "$search",
38
+ "$listCatalog",
39
+ "$unionWith",
40
+ ];
41
+
23
42
  function getAction(action: string) {
24
43
  if (actions?.includes(action)) {
25
44
  return true;
@@ -27,4 +46,4 @@ function getAction(action: string) {
27
46
  return false;
28
47
  }
29
48
 
30
- export { getAction };
49
+ export { getAction, forbiddenPipelineOperators };
package/app/hono.ts CHANGED
@@ -6,6 +6,8 @@ import path from "path";
6
6
  import mime from "mime-types";
7
7
  import { setApiStudio } from "./studio";
8
8
  import { Hono } from "hono";
9
+ import { compress } from "hono/compress";
10
+
9
11
  import { getCookie, setCookie } from "hono/cookie";
10
12
  import colors from "@colors/colors/safe";
11
13
  import { serveStatic, getConnInfo } from "hono/bun";
@@ -24,7 +26,7 @@ import {
24
26
  pick,
25
27
  stringToBoolean,
26
28
  } from "../utils";
27
- import { getAction } from "./ctrl";
29
+ import { forbiddenPipelineOperators, getAction } from "./ctrl";
28
30
  import { getCollection } from "../lib/collection";
29
31
  import { useRest } from "../driver/mongo/rest";
30
32
  import moment from "moment";
@@ -42,6 +44,7 @@ import { logger } from "hono/logger";
42
44
  import { csrf } from "hono/csrf";
43
45
  import { v4 } from "uuid";
44
46
  import type { SearchParams } from "meilisearch";
47
+ import { utils } from "..";
45
48
  const cache = bentoCache.namespace("DNAX_API");
46
49
  const app = new Hono();
47
50
  const API_PATH = "/api";
@@ -92,6 +95,7 @@ function HonoInstance(): typeof app {
92
95
  })
93
96
  );
94
97
  app.use(secureHeaders());
98
+ //app.use(compress());
95
99
 
96
100
  //eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoibm9tIiwiaWF0IjoxNzE3Nzc0MDQzLCJleHAiOjE3MTc3NzQxMDN9.Ud4-0y8pa4SMIcSn8PU1A-sjC-hT4ZVe_u3AdChyIJU
97
101
  // Middlewares Injection
@@ -506,6 +510,15 @@ function HonoInstance(): typeof app {
506
510
 
507
511
  // aggregate
508
512
  if (action == "aggregate") {
513
+ if (
514
+ utils.containsForbiddenOperators(
515
+ body?.pipeline || [],
516
+ forbiddenPipelineOperators
517
+ ) &&
518
+ !col?.customApi?.aggregate
519
+ ) {
520
+ return fn.error("Forbidden operators in pipeline", 403);
521
+ }
509
522
  response = await rest.aggregate(collection, body.pipeline || []);
510
523
  }
511
524
 
package/app/index.ts CHANGED
@@ -9,10 +9,10 @@ import pidusage from "pidusage";
9
9
  import "@colors/colors";
10
10
  import pkg from "../package.json";
11
11
  import findPort from "find-open-port";
12
- import { Server } from "socket.io";
13
12
  import { webSocketServer } from "../lib/socket/instance";
14
13
  import { HonoInstance } from "./hono";
15
14
  import { utils } from "..";
15
+ import type { Server } from "bun";
16
16
  type configRunApp = {
17
17
  register?: Array<Function>;
18
18
  beforeStart?: Array<Function>;
@@ -21,7 +21,7 @@ type configRunApp = {
21
21
  destroy?: Array<Function>;
22
22
  dbSync?: Array<Function>;
23
23
  };
24
- async function runApp(config?: configRunApp, clb?: Function) {
24
+ async function runApp(config?: configRunApp, clb?: Function): Promise<Server> {
25
25
  hookDatabase.cb = config?.dbSync || [];
26
26
  await loadCfg(); // Load Config
27
27
 
@@ -39,7 +39,7 @@ async function runApp(config?: configRunApp, clb?: Function) {
39
39
  }); */
40
40
 
41
41
  // available port Server
42
- await findPort
42
+ return await findPort
43
43
  .isAvailable(PORT)
44
44
  .then(async (available: boolean) => {
45
45
  // Start App server
@@ -76,36 +76,48 @@ async function runApp(config?: configRunApp, clb?: Function) {
76
76
  websocket: webSocketServer(server),
77
77
  });
78
78
 
79
- console.log();
79
+ let serverName =
80
+ process.env.SERVER_NAME || Cfg?.server?.name || "SERVER";
81
+
82
+ /* console.log(
83
+ boxen(`SERVER : ${PORT}`, {
84
+ borderStyle: "round",
85
+ title: `@dnax/server ${pkg.version}`,
86
+ padding: 1,
87
+ dimBorder: true,
88
+ titleAlignment: "center",
89
+ //float: "center",
90
+ //margin: 1,
91
+ borderColor: "gray",
92
+ })
93
+ ); */
80
94
  let info = "";
81
- info += "SERVER :".gray.italic.underline;
95
+ let envName = process.env?.NODE_ENV || "dev";
96
+ info += `${serverName}`.gray.underline.bold;
82
97
  info += `\n`;
83
98
  info += `\n`;
84
- info +=
85
- `Env: ${
86
- process.env.NODE_ENV === "production"
87
- ? "production"
88
- : process.env?.NODE_ENV || "development"
89
- }`.green + "\n";
90
- info += `Server: http://localhost:${PORT}\n`.green;
99
+ info += `Env: ${envName}`.green.bold + "\n";
100
+ info += `url: http://localhost:${PORT}\n`.gray.bold;
91
101
  //info += `Jwt Secret: ${Cfg.server.jwt?.secret}\n`.gray;
92
102
  info += `\n`;
93
- info += "TENANTS :".gray.italic.underline;
103
+ info += "TENANTS :".gray.underline.bold;
94
104
  info += `\n`;
95
105
  Cfg.tenants?.map((t: any) => {
96
- info += `\n${t?.name?.blue || "_"} : ${t?.id?.green}`.italic;
106
+ info += `\n${t?.name?.blue || "_"} : ${t?.id?.green}`.bold;
97
107
  });
98
108
  info += `\n\n`;
99
109
  info += `🔄 ${new Date().toLocaleString()}`.gray;
100
110
 
101
111
  if (clb) clb();
102
112
 
113
+ console.log();
103
114
  console.log(
104
115
  boxen(info, {
105
- borderStyle: "round",
116
+ borderStyle: "double",
106
117
  title: `@dnax/server ${pkg.version}`,
107
- padding: 1,
118
+ padding: 1.5,
108
119
  dimBorder: true,
120
+
109
121
  titleAlignment: "center",
110
122
  //float: "center",
111
123
  //margin: 1,
@@ -122,6 +134,8 @@ async function runApp(config?: configRunApp, clb?: Function) {
122
134
  }
123
135
  }
124
136
  }
137
+
138
+ return server;
125
139
  })
126
140
  .catch((err: any) => {
127
141
  console.error("Warning", err?.message);
@@ -37,7 +37,7 @@ ch.watch(".", {
37
37
  /node_modules|node_modules\/.*/,
38
38
  /node_modules/,
39
39
  /(^|[\/\\])\../,
40
- "*/node_modules",
40
+ "node_modules/**/*",
41
41
  ".env.*",
42
42
  ".env",
43
43
  ".nitro",
@@ -73,6 +73,8 @@ const omitUpdate = [
73
73
  "$set.updatedAt",
74
74
  "$unset",
75
75
  "$rename",
76
+ "$replaceRoot",
77
+ "$replaceWith",
76
78
  ];
77
79
 
78
80
  class useRest {
@@ -258,7 +260,7 @@ class useRest {
258
260
  let result = {
259
261
  docs: [],
260
262
  };
261
-
263
+ let useCustomApi = options?.useCustomApi ?? this.#useCustomApi;
262
264
  let useHook = options?.useHook ?? this.#useHook;
263
265
  let sharedData = {};
264
266
  let col = getCollection(collection, this.#tenant_id);
@@ -297,6 +299,21 @@ class useRest {
297
299
  });
298
300
  }
299
301
 
302
+ if (col?.customApi?.aggregate && useCustomApi) {
303
+ let result = await col?.customApi?.aggregate({
304
+ error: fn.error,
305
+ io: Cfg.io,
306
+ session: sessionStorage(),
307
+ pipeline: toJson(pipeline),
308
+ rest: new useRest({
309
+ useHook: false,
310
+ tenant_id: this.#tenant_id,
311
+ useCustomApi: false,
312
+ }),
313
+ });
314
+ return resolve(result);
315
+ }
316
+
300
317
  result.docs = await this.#tenant.database.db
301
318
  ?.collection(collection)
302
319
  .aggregate(toBson(pipeline), {
package/index.ts CHANGED
@@ -11,6 +11,7 @@ import { FilesystemSftpAdapter, MinioAdapter } from "./lib/media";
11
11
  import { crypt } from "./lib/crypto";
12
12
  import { contextStorage } from "./lib/asyncLocalStorage";
13
13
  import { Cron as Task } from "croner";
14
+ import { useWorkflow } from "./lib/workflow";
14
15
  import { serveStatic } from "hono/bun";
15
16
  // Adapter
16
17
 
@@ -37,4 +38,5 @@ export {
37
38
  $,
38
39
  contextStorage,
39
40
  Adapter,
41
+ //useWorkflow,
40
42
  };
package/lib/collection.ts CHANGED
@@ -14,6 +14,7 @@ import consola from "consola";
14
14
 
15
15
  async function loadAllCollections() {
16
16
  let collections: Collection[] = [];
17
+ //collections.push(workflowCol);
17
18
  if (Cfg.tenants) {
18
19
  for await (let t of Cfg.tenants) {
19
20
  let tenantPath = `${t.dir}/collections/**/**.model.{ts,js}`;
@@ -0,0 +1,84 @@
1
+ import { useRest } from "../../driver/mongo";
2
+ import { v4 } from "uuid";
3
+
4
+ type WorkFlowConfig = {
5
+ tenant_id: string;
6
+ version?: string;
7
+ };
8
+
9
+ type StepMeta = {
10
+ ms?: number;
11
+ [key: string]: any;
12
+ };
13
+
14
+ type StepStatus = "done" | "failed";
15
+
16
+ type StepFunction<TData = any> = (ctx: {
17
+ rest: useRest;
18
+ traceId: string;
19
+ data: TData;
20
+ meta?: Record<string, any>;
21
+ output: any;
22
+ }) => Promise<void>;
23
+
24
+ type Step = {
25
+ name: string;
26
+ handler: StepFunction;
27
+ };
28
+
29
+ class Workflow<Tdata> {
30
+ name: string;
31
+ config: WorkFlowConfig;
32
+ #rest: useRest;
33
+ traceId?: string;
34
+ #steps: Step[] = [];
35
+ constructor(name: string, config: WorkFlowConfig) {
36
+ this.name = name;
37
+ this.config = config;
38
+ this.#rest = new useRest({
39
+ tenant_id: config.tenant_id,
40
+ });
41
+ }
42
+
43
+ step(name: string, handler: StepFunction<Tdata>) {
44
+ this.#steps.push({
45
+ name,
46
+ handler,
47
+ });
48
+ return this;
49
+ }
50
+
51
+ async run(data: Tdata, traceId?: string): Promise<void> {
52
+ this.traceId = traceId || v4();
53
+ let output: any = {};
54
+ for (const step of this.#steps) {
55
+ let startAt = Date.now();
56
+ let ev = {
57
+ name: step.name,
58
+ traceId: this.traceId,
59
+ duration: 0,
60
+ status: "pending",
61
+ startedAt: new Date(),
62
+ endedAt: new Date(),
63
+ };
64
+ output = await step.handler({
65
+ rest: this.#rest,
66
+ traceId: this.traceId,
67
+ data,
68
+ output,
69
+ });
70
+ let endAt = Date.now();
71
+ // duration en ms
72
+ let duration = endAt - startAt;
73
+ ev.duration = duration;
74
+ ev.status = "done";
75
+ ev.endedAt = new Date();
76
+ }
77
+ }
78
+ }
79
+
80
+ function useWorkflow<T>(name: string, config: WorkFlowConfig) {
81
+ return new Workflow<T>(name, config);
82
+ }
83
+
84
+ export { useWorkflow };
@@ -0,0 +1,64 @@
1
+ import { useRest } from "../../driver/mongo";
2
+
3
+ export type StepActions = {
4
+ done: (comment?: string) => void;
5
+ fail: (comment?: string) => void;
6
+ abort: (comment?: string) => void;
7
+ };
8
+
9
+ export type Step<Name extends string = string> = {
10
+ name: Name;
11
+ description?: string;
12
+ exec: (ctx: { data: any; rest: useRest; action?: StepActions }) => void;
13
+ };
14
+
15
+ export type stepData = {
16
+ name: string;
17
+ data: any;
18
+ at: Date;
19
+ comment?: string;
20
+ description?: string;
21
+ status: "done" | "failed" | "aborted";
22
+ traceId: string;
23
+ ms: number;
24
+ };
25
+
26
+ export type EventStep = {
27
+ status: "failed" | "aborted" | "done";
28
+ data: any;
29
+ at: Date;
30
+ name: string;
31
+ };
32
+
33
+ export type WorkflowData = {
34
+ _id: string;
35
+ name: string;
36
+ traceId: string;
37
+ currentStep: string;
38
+ steps: stepData[];
39
+ events: [];
40
+ status:
41
+ | "processing"
42
+ | "completed"
43
+ | "failed"
44
+ | "aborted"
45
+ | "cancelled"
46
+ | "initialized";
47
+ };
48
+
49
+ export type WorkflowType = {
50
+ traceId: string;
51
+ _id: string;
52
+ name: string;
53
+ currentStep: string;
54
+ failedAtStep?: string;
55
+ steps: Step[];
56
+ events: [];
57
+ completedIf: () => boolean;
58
+ failedIf: () => boolean;
59
+ abortedIf: () => boolean;
60
+ };
61
+
62
+ export type StepInstance = {
63
+ run: (stepName: string, data?: any) => Promise<void>;
64
+ };
package/package.json CHANGED
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "name": "@dnax/core",
3
- "version": "0.66.0",
3
+ "version": "0.66.3",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
- "bin": {
7
- "dnaxi": "./bin/index.ts"
8
- },
6
+ "bin": {},
9
7
  "devDependencies": {
10
8
  "@types/bun": "latest",
11
9
  "@types/dot-object": "^2.1.6",
@@ -22,7 +20,6 @@
22
20
  "typescript": "^5.8.3"
23
21
  },
24
22
  "dependencies": {
25
- "@clack/prompts": "^0.10.0",
26
23
  "@colors/colors": "^1.6.0",
27
24
  "@lukeed/ms": "^2.0.2",
28
25
  "bentocache": "^1.2.1",
@@ -47,14 +44,13 @@
47
44
  "mingo": "^6.5.0",
48
45
  "minio": "^8.0.5",
49
46
  "moment": "^2.30.1",
50
- "mongodb": "6.15.0",
47
+ "mongodb": "6.17.0",
51
48
  "nodemailer": "^6.9.14",
52
49
  "pidusage": "^4.0.0",
53
50
  "pizzip": "^3.1.8",
54
51
  "radash": "^12.1.0",
55
52
  "rfc6902": "^5.1.2",
56
53
  "sharp": "^0.33.5",
57
- "signaldb": "^0.24.5",
58
54
  "ssh2": "^1.16.0",
59
55
  "ufo": "^1.5.4",
60
56
  "urlencode": "^2.0.0",
package/types/index.ts CHANGED
@@ -3,6 +3,7 @@ import { Cron } from "croner";
3
3
  //import type{ updateParams } from "./../driver/mongo/@types";
4
4
  //import * as v from "valibot";
5
5
  import type { Db, MongoClient } from "mongodb";
6
+ import { WorkflowInstance } from "../lib/workflow";
6
7
 
7
8
  import { useRest } from "../driver/mongo/rest";
8
9
  import { sessionStorage } from "../lib/asyncLocalStorage";
@@ -271,6 +272,7 @@ export type ctxApi = {
271
272
  session?: sessionCtx;
272
273
  io: socketIoType;
273
274
  params?: findParam;
275
+ pipeline?: Array<object>;
274
276
  id?: string;
275
277
  ids?: string[];
276
278
  error: typeof fn.error;
@@ -307,6 +309,7 @@ export type Collection = {
307
309
  filesystemAdapter?: any;
308
310
  };
309
311
  customApi?: {
312
+ aggregate?: (ctx: ctxApi) => Array<object> | null | undefined;
310
313
  insertOne?: (ctx: ctxApi) => object | null | undefined;
311
314
  insertMany?: (ctx: ctxApi) => Array<object> | null | undefined;
312
315
  updateOne?: (ctx: ctxApi) => object | null | undefined;
@@ -546,6 +549,7 @@ export type Config = {
546
549
  onError?: (error: ApiError) => Promise<ApiError> | ApiError | void;
547
550
  };
548
551
  server: {
552
+ name?: string;
549
553
  id?: string;
550
554
  logger?: Boolean | loggerFunction;
551
555
  whiteListIps?: Array<string>;
package/utils/index.ts CHANGED
@@ -540,6 +540,15 @@ const file = {
540
540
  },
541
541
  };
542
542
 
543
+ function containsForbiddenOperators(
544
+ pipeline: any[],
545
+ forbiddenOperators: string[]
546
+ ): boolean {
547
+ return pipeline.some((stage) =>
548
+ Object.keys(stage).some((operator) => forbiddenOperators.includes(operator))
549
+ );
550
+ }
551
+
543
552
  const contextError = ContextError;
544
553
  export {
545
554
  file,
@@ -575,4 +584,5 @@ export {
575
584
  deepEqual,
576
585
  applyPatch,
577
586
  createPatch,
587
+ containsForbiddenOperators,
578
588
  };