@dnax/core 0.52.2 → 0.52.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 +1 -0
- package/app/hono.ts +69 -29
- package/driver/mongo/rest.ts +189 -5
- package/lib/collection.ts +29 -2
- package/lib/crypto.ts +13 -7
- package/lib/index.ts +5 -0
- package/lib/meilisearch/index.ts +25 -0
- package/package.json +2 -1
- package/types/index.ts +76 -15
package/app/ctrl.ts
CHANGED
package/app/hono.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { consola } from "consola";
|
|
|
14
14
|
import { cors } from "hono/cors";
|
|
15
15
|
import { asyncLocalStorage, sessionStorage } from "../lib/asyncLocalStorage";
|
|
16
16
|
import { localSession } from "../lib/session";
|
|
17
|
+
import { crypt } from "../lib/crypto";
|
|
17
18
|
import {
|
|
18
19
|
cleanPath,
|
|
19
20
|
ContextError,
|
|
@@ -40,6 +41,7 @@ import { ipRestriction } from "hono/ip-restriction";
|
|
|
40
41
|
import { logger } from "hono/logger";
|
|
41
42
|
import { csrf } from "hono/csrf";
|
|
42
43
|
import { v4 } from "uuid";
|
|
44
|
+
import type { SearchParams } from "meilisearch";
|
|
43
45
|
const cache = bentoCache.namespace("DNAX_API");
|
|
44
46
|
const app = new Hono();
|
|
45
47
|
const API_PATH = "/api";
|
|
@@ -498,24 +500,66 @@ function HonoInstance(): typeof app {
|
|
|
498
500
|
if (action == "count") {
|
|
499
501
|
response = await rest.count(collection, body?.params || {});
|
|
500
502
|
}
|
|
503
|
+
|
|
504
|
+
if (action == "search") {
|
|
505
|
+
let searchOption = {
|
|
506
|
+
term: body?.params?.term as string,
|
|
507
|
+
options: body?.params?.searchOptions as SearchParams,
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
if (col?.hooks?.beforeSearch) {
|
|
511
|
+
await col?.hooks?.beforeSearch({
|
|
512
|
+
io: Cfg.io,
|
|
513
|
+
data: body,
|
|
514
|
+
c: c,
|
|
515
|
+
rest: rest,
|
|
516
|
+
action: "search",
|
|
517
|
+
session: sessionStorage(),
|
|
518
|
+
error: fn.error,
|
|
519
|
+
search: searchOption,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
response = await rest.search(
|
|
524
|
+
collection,
|
|
525
|
+
searchOption.term,
|
|
526
|
+
searchOption.options || {}
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
if (col?.hooks?.afterSearch) {
|
|
530
|
+
await col?.hooks?.afterSearch({
|
|
531
|
+
io: Cfg.io,
|
|
532
|
+
data: body,
|
|
533
|
+
c: c,
|
|
534
|
+
rest: rest,
|
|
535
|
+
action: "search",
|
|
536
|
+
session: sessionStorage(),
|
|
537
|
+
error: fn.error,
|
|
538
|
+
search: searchOption,
|
|
539
|
+
result: response,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
501
543
|
// find
|
|
502
544
|
if (action == "find") {
|
|
503
545
|
if (col?.cache?.enabled && useCache) {
|
|
504
|
-
let keyCache =
|
|
505
|
-
|
|
506
|
-
query: c
|
|
546
|
+
let keyCache = crypt.AES.generateKey({
|
|
547
|
+
body: body,
|
|
548
|
+
query: c?.req?.query,
|
|
507
549
|
});
|
|
508
550
|
|
|
509
551
|
// if customCache
|
|
510
|
-
if (col?.cache?.
|
|
511
|
-
let fetchFromCache = await col?.cache?.
|
|
552
|
+
if (col?.cache?.fetch) {
|
|
553
|
+
let fetchFromCache = await col?.cache?.fetch({
|
|
554
|
+
key: keyCache,
|
|
512
555
|
action: action,
|
|
556
|
+
params: body?.params,
|
|
513
557
|
rest: rest,
|
|
514
558
|
session: sessionStorage(),
|
|
559
|
+
state: col?.cache?.state || {},
|
|
515
560
|
c: c,
|
|
516
561
|
});
|
|
517
|
-
|
|
518
|
-
if (!fetchFromCache.isStale) {
|
|
562
|
+
if (!fetchFromCache?.isStale) {
|
|
519
563
|
response = fetchFromCache.data;
|
|
520
564
|
} else {
|
|
521
565
|
response = await rest.find(collection, body?.params || {}, {
|
|
@@ -523,28 +567,6 @@ function HonoInstance(): typeof app {
|
|
|
523
567
|
});
|
|
524
568
|
}
|
|
525
569
|
}
|
|
526
|
-
// if not customCache
|
|
527
|
-
if (!col?.cache?.customCache) {
|
|
528
|
-
let responseFromCache = await cache.get({
|
|
529
|
-
key: keyCache,
|
|
530
|
-
});
|
|
531
|
-
if (responseFromCache) {
|
|
532
|
-
response = responseFromCache;
|
|
533
|
-
} else {
|
|
534
|
-
response = await rest.find(collection, body?.params || {}, {
|
|
535
|
-
withMeta: body?.withMeta || false,
|
|
536
|
-
});
|
|
537
|
-
await cache.set({
|
|
538
|
-
key: keyCache,
|
|
539
|
-
value: response,
|
|
540
|
-
ttl: col?.cache?.ttl || "1m",
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
/* response = await rest.find(collection, body?.params || {}, {
|
|
545
|
-
withMeta: body?.withMeta || false,
|
|
546
|
-
}); */
|
|
547
|
-
}
|
|
548
570
|
} else {
|
|
549
571
|
response = await rest.find(collection, body?.params || {}, {
|
|
550
572
|
withMeta: body?.withMeta || false,
|
|
@@ -659,6 +681,9 @@ function HonoInstance(): typeof app {
|
|
|
659
681
|
} else {
|
|
660
682
|
response = omit(response, privateFields);
|
|
661
683
|
}
|
|
684
|
+
if (response.hits) {
|
|
685
|
+
response.hits = omit(response.hits, privateFields);
|
|
686
|
+
}
|
|
662
687
|
}
|
|
663
688
|
|
|
664
689
|
// hidden is function
|
|
@@ -680,6 +705,9 @@ function HonoInstance(): typeof app {
|
|
|
680
705
|
privateFields.push("password");
|
|
681
706
|
privateFields = [...new Set(privateFields)];
|
|
682
707
|
response.data = omit(response.data, privateFields);
|
|
708
|
+
if (response?.hits) {
|
|
709
|
+
response.hits = omit(response.hits, privateFields);
|
|
710
|
+
}
|
|
683
711
|
} catch (e) {}
|
|
684
712
|
} else {
|
|
685
713
|
privateFields = await col?.api?.fields?.hidden({
|
|
@@ -690,6 +718,10 @@ function HonoInstance(): typeof app {
|
|
|
690
718
|
});
|
|
691
719
|
privateFields.push("password");
|
|
692
720
|
privateFields = [...new Set(privateFields)];
|
|
721
|
+
|
|
722
|
+
if (response?.hits) {
|
|
723
|
+
response.hits = omit(response?.hits, privateFields);
|
|
724
|
+
}
|
|
693
725
|
response = omit(response, privateFields);
|
|
694
726
|
}
|
|
695
727
|
}
|
|
@@ -703,6 +735,14 @@ function HonoInstance(): typeof app {
|
|
|
703
735
|
response.meta = response.meta || {};
|
|
704
736
|
}
|
|
705
737
|
|
|
738
|
+
if (response?.hits && col?.privateFields?.length) {
|
|
739
|
+
response = omit(response, col?.privateFields);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (response?.data && col?.privateFields?.length) {
|
|
743
|
+
response = omit(response, col?.privateFields);
|
|
744
|
+
}
|
|
745
|
+
|
|
706
746
|
return c.json(response);
|
|
707
747
|
} catch (err: any) {
|
|
708
748
|
if (Cfg?.debug) console.log(err?.message | err);
|
package/driver/mongo/rest.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { ChangeStream, ClientSession, MongoClient, ObjectId } from "mongodb";
|
|
|
8
8
|
import { contextError, fn, toJson, dotJson, getEntryBykeys } from "../../utils";
|
|
9
9
|
import v, { type Schema } from "joi";
|
|
10
10
|
import type { Context } from "hono";
|
|
11
|
+
import { MeiliSearch } from "../../lib/meilisearch";
|
|
12
|
+
import type { SearchParams } from "meilisearch";
|
|
11
13
|
|
|
12
14
|
import type {
|
|
13
15
|
findOneParam,
|
|
@@ -93,6 +95,9 @@ class useRest {
|
|
|
93
95
|
$skip?: number;
|
|
94
96
|
}) => Promise<Array<any>>;
|
|
95
97
|
};
|
|
98
|
+
meilisearch: {
|
|
99
|
+
client: InstanceType<typeof MeiliSearch>;
|
|
100
|
+
};
|
|
96
101
|
|
|
97
102
|
constructor(options: options) {
|
|
98
103
|
this.#c = options?.c;
|
|
@@ -102,7 +107,9 @@ class useRest {
|
|
|
102
107
|
this.#tenant = getTenant(this.#tenant_id);
|
|
103
108
|
this.#useHook = options?.useHook ?? true;
|
|
104
109
|
this.db = this.#tenant.database.db;
|
|
105
|
-
|
|
110
|
+
this.meilisearch = {
|
|
111
|
+
client: this.#tenant.searchEngine?.meilisearch?.client!,
|
|
112
|
+
};
|
|
106
113
|
this.activity = {
|
|
107
114
|
save: async (activityInput) => {
|
|
108
115
|
return await restActivity.save(this.#tenant, activityInput);
|
|
@@ -383,6 +390,28 @@ class useRest {
|
|
|
383
390
|
sessionStorage()?.get()?.state?.by ||
|
|
384
391
|
null,
|
|
385
392
|
});
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
col?.cache?.watch({
|
|
396
|
+
action: "insertOne",
|
|
397
|
+
c: this.#c,
|
|
398
|
+
session: sessionStorage(),
|
|
399
|
+
rest: this,
|
|
400
|
+
state: col?.cache?.state || {},
|
|
401
|
+
result: toJson(data),
|
|
402
|
+
});
|
|
403
|
+
} catch (err) {}
|
|
404
|
+
|
|
405
|
+
// meilisearch engine
|
|
406
|
+
if (
|
|
407
|
+
this.#tenant?.searchEngine?.meilisearch?.enabled &&
|
|
408
|
+
col?.searchEngine?.meilisearch?.dispatchAction == "auto"
|
|
409
|
+
) {
|
|
410
|
+
this.meilisearch.client
|
|
411
|
+
.index(col?.searchEngine?.meilisearch?.index)
|
|
412
|
+
.addDocuments([toJson(data)])
|
|
413
|
+
.catch();
|
|
414
|
+
}
|
|
386
415
|
return resolve(toJson(data));
|
|
387
416
|
} catch (err) {
|
|
388
417
|
return reject(err);
|
|
@@ -503,7 +532,25 @@ class useRest {
|
|
|
503
532
|
sessionStorage()?.get().state?.by ||
|
|
504
533
|
null,
|
|
505
534
|
});
|
|
506
|
-
|
|
535
|
+
try {
|
|
536
|
+
col?.cache?.watch({
|
|
537
|
+
action: "insertMany",
|
|
538
|
+
c: this.#c,
|
|
539
|
+
session: sessionStorage(),
|
|
540
|
+
rest: this,
|
|
541
|
+
state: col?.cache?.state || {},
|
|
542
|
+
result: toJson(data),
|
|
543
|
+
});
|
|
544
|
+
} catch (err) {}
|
|
545
|
+
if (
|
|
546
|
+
this.#tenant?.searchEngine?.meilisearch?.enabled &&
|
|
547
|
+
col?.searchEngine?.meilisearch?.dispatchAction == "auto"
|
|
548
|
+
) {
|
|
549
|
+
this.meilisearch.client
|
|
550
|
+
.index(col?.searchEngine?.meilisearch?.index)
|
|
551
|
+
.addDocuments(toJson(data))
|
|
552
|
+
.catch();
|
|
553
|
+
}
|
|
507
554
|
return resolve(toJson(data));
|
|
508
555
|
} catch (err) {
|
|
509
556
|
reject(err);
|
|
@@ -1104,7 +1151,26 @@ class useRest {
|
|
|
1104
1151
|
sessionStorage().get().state?.by ||
|
|
1105
1152
|
null,
|
|
1106
1153
|
});
|
|
1107
|
-
|
|
1154
|
+
try {
|
|
1155
|
+
col?.cache?.watch({
|
|
1156
|
+
action: "insertMany",
|
|
1157
|
+
c: this.#c,
|
|
1158
|
+
session: sessionStorage(),
|
|
1159
|
+
rest: this,
|
|
1160
|
+
state: col?.cache?.state || {},
|
|
1161
|
+
result: toJson(result?.doc || {}),
|
|
1162
|
+
});
|
|
1163
|
+
} catch (err) {}
|
|
1164
|
+
if (
|
|
1165
|
+
this.#tenant?.searchEngine?.meilisearch?.enabled &&
|
|
1166
|
+
col?.searchEngine?.meilisearch?.dispatchAction == "auto"
|
|
1167
|
+
) {
|
|
1168
|
+
this.meilisearch.client
|
|
1169
|
+
.index(col?.searchEngine?.meilisearch?.index)
|
|
1170
|
+
.updateDocuments([{ ...toJson(result?.doc || {}) }])
|
|
1171
|
+
.catch();
|
|
1172
|
+
}
|
|
1173
|
+
return resolve(toJson(result?.doc || {}));
|
|
1108
1174
|
} catch (err) {
|
|
1109
1175
|
return reject(err);
|
|
1110
1176
|
}
|
|
@@ -1174,6 +1240,18 @@ class useRest {
|
|
|
1174
1240
|
allowDiskUse: true,
|
|
1175
1241
|
})
|
|
1176
1242
|
.toArray();
|
|
1243
|
+
|
|
1244
|
+
if (
|
|
1245
|
+
this.#tenant?.searchEngine?.meilisearch?.enabled &&
|
|
1246
|
+
col?.searchEngine?.meilisearch?.dispatchAction == "auto"
|
|
1247
|
+
) {
|
|
1248
|
+
this.meilisearch.client
|
|
1249
|
+
.index(col?.searchEngine?.meilisearch?.index)
|
|
1250
|
+
.updateDocuments(result.docs)
|
|
1251
|
+
.catch();
|
|
1252
|
+
}
|
|
1253
|
+
``;
|
|
1254
|
+
|
|
1177
1255
|
return resolve({
|
|
1178
1256
|
docs: result.docs,
|
|
1179
1257
|
matchedCount: up_?.matchedCount || 0,
|
|
@@ -1245,7 +1323,19 @@ class useRest {
|
|
|
1245
1323
|
session: this.#session ? this.#session : undefined,
|
|
1246
1324
|
}
|
|
1247
1325
|
);
|
|
1248
|
-
|
|
1326
|
+
if (
|
|
1327
|
+
this.#tenant?.searchEngine?.meilisearch?.enabled &&
|
|
1328
|
+
col?.searchEngine?.meilisearch?.dispatchAction == "auto"
|
|
1329
|
+
) {
|
|
1330
|
+
this.meilisearch.client
|
|
1331
|
+
.index(col?.searchEngine?.meilisearch?.index)
|
|
1332
|
+
.updateDocuments([
|
|
1333
|
+
{
|
|
1334
|
+
...toJson(result.doc || {}),
|
|
1335
|
+
},
|
|
1336
|
+
])
|
|
1337
|
+
.catch();
|
|
1338
|
+
}
|
|
1249
1339
|
return resolve(result.doc);
|
|
1250
1340
|
} catch (err) {
|
|
1251
1341
|
return reject(err);
|
|
@@ -1253,6 +1343,44 @@ class useRest {
|
|
|
1253
1343
|
});
|
|
1254
1344
|
}
|
|
1255
1345
|
|
|
1346
|
+
async search(
|
|
1347
|
+
collection: string,
|
|
1348
|
+
term: string,
|
|
1349
|
+
searchOptions: SearchParams
|
|
1350
|
+
): Promise<{
|
|
1351
|
+
hits: Array<object>;
|
|
1352
|
+
limit: number;
|
|
1353
|
+
offset: number;
|
|
1354
|
+
total: number;
|
|
1355
|
+
processingTimeMs: number;
|
|
1356
|
+
facetDistribution?: object;
|
|
1357
|
+
facetsStats?: object;
|
|
1358
|
+
estimatedTotalHits?: number;
|
|
1359
|
+
totalHits?: number;
|
|
1360
|
+
hitsPerPage?: number;
|
|
1361
|
+
page?: number;
|
|
1362
|
+
}> {
|
|
1363
|
+
return new Promise(async (resolve, reject) => {
|
|
1364
|
+
try {
|
|
1365
|
+
let index = getCollection(collection, this.#tenant_id)?.searchEngine
|
|
1366
|
+
?.meilisearch?.index!;
|
|
1367
|
+
|
|
1368
|
+
if (!index)
|
|
1369
|
+
return reject({
|
|
1370
|
+
code: 400,
|
|
1371
|
+
message: "Search engine Index required",
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
let result = this.meilisearch.client.index(index).search(term, {
|
|
1375
|
+
...searchOptions,
|
|
1376
|
+
});
|
|
1377
|
+
resolve(result);
|
|
1378
|
+
} catch (err) {
|
|
1379
|
+
return reject(err);
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1256
1384
|
async updateMany(
|
|
1257
1385
|
collection: string,
|
|
1258
1386
|
ids: Array<string>,
|
|
@@ -1418,6 +1546,25 @@ class useRest {
|
|
|
1418
1546
|
sessionStorage().get().state?.by ||
|
|
1419
1547
|
null,
|
|
1420
1548
|
});
|
|
1549
|
+
try {
|
|
1550
|
+
col?.cache?.watch({
|
|
1551
|
+
action: "updateMany",
|
|
1552
|
+
c: this.#c,
|
|
1553
|
+
session: sessionStorage(),
|
|
1554
|
+
rest: this,
|
|
1555
|
+
state: col?.cache?.state || {},
|
|
1556
|
+
result: toJson(result?.docs || []),
|
|
1557
|
+
});
|
|
1558
|
+
} catch (err) {}
|
|
1559
|
+
if (
|
|
1560
|
+
this.#tenant?.searchEngine?.meilisearch?.enabled &&
|
|
1561
|
+
col?.searchEngine?.meilisearch?.dispatchAction == "auto"
|
|
1562
|
+
) {
|
|
1563
|
+
this.meilisearch.client
|
|
1564
|
+
.index(col?.searchEngine?.meilisearch?.index)
|
|
1565
|
+
.updateDocuments(result.docs)
|
|
1566
|
+
.catch();
|
|
1567
|
+
}
|
|
1421
1568
|
return resolve(result.docs);
|
|
1422
1569
|
} else {
|
|
1423
1570
|
throw new contextError("List of id required", 400);
|
|
@@ -1533,6 +1680,25 @@ class useRest {
|
|
|
1533
1680
|
sessionStorage().get().state?.by ||
|
|
1534
1681
|
null,
|
|
1535
1682
|
});
|
|
1683
|
+
try {
|
|
1684
|
+
col?.cache?.watch({
|
|
1685
|
+
action: "deleteOne",
|
|
1686
|
+
c: this.#c,
|
|
1687
|
+
session: sessionStorage(),
|
|
1688
|
+
rest: this,
|
|
1689
|
+
state: col?.cache?.state || {},
|
|
1690
|
+
result: toJson(doc || {}),
|
|
1691
|
+
});
|
|
1692
|
+
} catch (err) {}
|
|
1693
|
+
if (
|
|
1694
|
+
this.#tenant.searchEngine.meilisearch.enabled &&
|
|
1695
|
+
col?.searchEngine?.meilisearch?.dispatchAction == "auto"
|
|
1696
|
+
) {
|
|
1697
|
+
this.meilisearch.client
|
|
1698
|
+
.index(col?.searchEngine?.meilisearch?.index)
|
|
1699
|
+
.deleteDocument(id)
|
|
1700
|
+
.catch();
|
|
1701
|
+
}
|
|
1536
1702
|
return resolve(doc);
|
|
1537
1703
|
} catch (err) {
|
|
1538
1704
|
return resolve(err);
|
|
@@ -1648,7 +1814,25 @@ class useRest {
|
|
|
1648
1814
|
sessionStorage().get().state?.by ||
|
|
1649
1815
|
null,
|
|
1650
1816
|
});
|
|
1651
|
-
|
|
1817
|
+
try {
|
|
1818
|
+
col?.cache?.watch({
|
|
1819
|
+
action: "deleteMany",
|
|
1820
|
+
c: this.#c,
|
|
1821
|
+
session: sessionStorage(),
|
|
1822
|
+
rest: this,
|
|
1823
|
+
state: col?.cache?.state || {},
|
|
1824
|
+
result: deletedIds || [],
|
|
1825
|
+
});
|
|
1826
|
+
} catch (err) {}
|
|
1827
|
+
if (
|
|
1828
|
+
this.#tenant.searchEngine.meilisearch.enabled &&
|
|
1829
|
+
col?.searchEngine?.meilisearch?.dispatchAction == "auto"
|
|
1830
|
+
) {
|
|
1831
|
+
this.meilisearch.client
|
|
1832
|
+
.index(col?.searchEngine?.meilisearch?.index)
|
|
1833
|
+
.deleteDocuments(deletedIds)
|
|
1834
|
+
.catch();
|
|
1835
|
+
}
|
|
1652
1836
|
return resolve(deletedIds);
|
|
1653
1837
|
} catch (err) {
|
|
1654
1838
|
return reject(err);
|
package/lib/collection.ts
CHANGED
|
@@ -70,20 +70,47 @@ async function syncCollectionDatabase() {
|
|
|
70
70
|
const collections: Collection[] | undefined = Cfg.collections?.filter(
|
|
71
71
|
(cn) => cn.tenant_id == t.id
|
|
72
72
|
);
|
|
73
|
-
|
|
74
73
|
if (collections?.length) {
|
|
74
|
+
// searchEngine Section
|
|
75
|
+
if (t?.searchEngine?.meilisearch?.enabled) {
|
|
76
|
+
for await (let c of collections) {
|
|
77
|
+
if (c?.searchEngine?.meilisearch?.index) {
|
|
78
|
+
if (c?.searchEngine?.meilisearch?.dispatchAction) {
|
|
79
|
+
c.searchEngine.meilisearch.dispatchAction = "auto";
|
|
80
|
+
}
|
|
81
|
+
await t.searchEngine.meilisearch.client
|
|
82
|
+
?.createIndex(c?.searchEngine?.meilisearch?.index, {
|
|
83
|
+
primaryKey:
|
|
84
|
+
c?.searchEngine?.meilisearch?.primaryKey || "_id",
|
|
85
|
+
})
|
|
86
|
+
.catch((err) => {
|
|
87
|
+
//consola.error(err?.message);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// DatabaseSection
|
|
75
93
|
for await (let c of collections) {
|
|
76
94
|
// boucle sur les collections
|
|
77
95
|
//console.log(c?.slug,c.init)
|
|
78
96
|
if (c?.init && typeof c?.init == "function") {
|
|
79
97
|
await c?.init({
|
|
80
98
|
rest: new useRest({
|
|
81
|
-
tenant_id: t
|
|
99
|
+
tenant_id: t?.id,
|
|
82
100
|
}),
|
|
83
101
|
collection: c?.slug,
|
|
84
102
|
});
|
|
85
103
|
}
|
|
86
104
|
|
|
105
|
+
if (c?.cache?.enabled && c?.cache?.init) {
|
|
106
|
+
c?.cache?.init({
|
|
107
|
+
rest: new useRest({
|
|
108
|
+
tenant_id: t.id,
|
|
109
|
+
}),
|
|
110
|
+
state: c?.cache?.state || {},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
87
114
|
let collectionExist = collectionsInDatabase?.find(
|
|
88
115
|
(col) => col?.name == c.slug
|
|
89
116
|
);
|
package/lib/crypto.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import cr from "node:crypto";
|
|
3
2
|
class AES {
|
|
4
3
|
#key;
|
|
5
4
|
constructor(
|
|
@@ -10,16 +9,21 @@ class AES {
|
|
|
10
9
|
}
|
|
11
10
|
) {
|
|
12
11
|
// Crée une clé de 256 bits (32 octets) en utilisant un hachage SHA-256 de la clé secrète fournie
|
|
13
|
-
this.#key =
|
|
12
|
+
this.#key = cr.createHash("sha256").update(options.secretKey).digest();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static generateKey(data: any) {
|
|
16
|
+
const stringData = typeof data === "string" ? data : JSON.stringify(data);
|
|
17
|
+
return cr.createHash("sha256").update(stringData).digest("hex");
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
// Fonction de chiffrement
|
|
17
21
|
encrypt(data: string | object) {
|
|
18
22
|
let dataEncoding = JSON.stringify(data);
|
|
19
|
-
const iv =
|
|
23
|
+
const iv = cr.randomBytes(12); // Génère un IV de 12 octets pour AES-GCM
|
|
20
24
|
|
|
21
25
|
// Crée un objet de chiffrement avec AES-256-GCM, la clé et l'IV
|
|
22
|
-
const cipher =
|
|
26
|
+
const cipher = cr.createCipheriv("aes-256-gcm", this.#key, iv);
|
|
23
27
|
|
|
24
28
|
// Chiffre le message et convertit le texte chiffré en hexadécimal
|
|
25
29
|
let encrypted = cipher.update(dataEncoding, "utf8", "hex");
|
|
@@ -42,7 +46,7 @@ class AES {
|
|
|
42
46
|
const ciphertext = encryptedData.slice(56); // Extrait les données chiffrées
|
|
43
47
|
*/
|
|
44
48
|
// Crée un objet de déchiffrement avec AES-256-GCM, la clé et l'IV
|
|
45
|
-
const decipher =
|
|
49
|
+
const decipher = cr.createDecipheriv(
|
|
46
50
|
"aes-256-gcm",
|
|
47
51
|
this.#key,
|
|
48
52
|
Buffer.from(iv, "hex")
|
|
@@ -65,4 +69,6 @@ const crypt = {
|
|
|
65
69
|
AES,
|
|
66
70
|
};
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
const crypto = crypt;
|
|
73
|
+
|
|
74
|
+
export { crypt, crypto };
|
package/lib/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { loadAllCollections, syncCollectionDatabase } from "./collection";
|
|
2
|
+
import { syncMeilisearchClientOnTenant } from "./meilisearch";
|
|
2
3
|
import { connectTenantsDatabase } from "../driver";
|
|
3
4
|
import { loadEndpoints } from "./endpoint";
|
|
4
5
|
import { loadServices } from "./service";
|
|
@@ -9,6 +10,7 @@ import { loadAutoRoutes } from "./routes";
|
|
|
9
10
|
import { initScript } from "../lib/scripts";
|
|
10
11
|
import { syncAdapterFileSystem } from "./media";
|
|
11
12
|
import { runGenTypes } from "../types/gen";
|
|
13
|
+
|
|
12
14
|
// load all ressource
|
|
13
15
|
async function init(cf = { app: null }) {
|
|
14
16
|
await loadSocket();
|
|
@@ -23,6 +25,9 @@ async function init(cf = { app: null }) {
|
|
|
23
25
|
// sync all collections database ( Indexes)
|
|
24
26
|
syncCollectionDatabase();
|
|
25
27
|
|
|
28
|
+
// sync
|
|
29
|
+
syncMeilisearchClientOnTenant();
|
|
30
|
+
|
|
26
31
|
// load Service
|
|
27
32
|
loadServices();
|
|
28
33
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { MeiliSearch } from "meilisearch";
|
|
2
|
+
import { Cfg } from "../../config";
|
|
3
|
+
function getClient(config: {
|
|
4
|
+
host: string;
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
}): InstanceType<typeof MeiliSearch> {
|
|
7
|
+
const client = new MeiliSearch({
|
|
8
|
+
host: config?.host,
|
|
9
|
+
apiKey: config?.apiKey,
|
|
10
|
+
});
|
|
11
|
+
return client;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function syncMeilisearchClientOnTenant() {
|
|
15
|
+
for await (let t of Cfg.tenants) {
|
|
16
|
+
if (t?.searchEngine?.meilisearch?.enabled) {
|
|
17
|
+
t.searchEngine.meilisearch.client = getClient({
|
|
18
|
+
host: t.searchEngine.meilisearch.host,
|
|
19
|
+
apiKey: t.searchEngine.meilisearch.apiKey,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { MeiliSearch, getClient, syncMeilisearchClientOnTenant };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dnax/core",
|
|
3
|
-
"version": "0.52.
|
|
3
|
+
"version": "0.52.3",
|
|
4
4
|
"module": "index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"joi": "17.13.3",
|
|
42
42
|
"json-joy": "16.8.0",
|
|
43
43
|
"jsonwebtoken": "^9.0.2",
|
|
44
|
+
"meilisearch": "^0.50.0",
|
|
44
45
|
"mime-types": "^2.1.35",
|
|
45
46
|
"mingo": "^6.5.0",
|
|
46
47
|
"moment": "^2.30.1",
|
package/types/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AnySchema } from "joi";
|
|
2
2
|
import { Cron } from "croner";
|
|
3
|
-
import { updateParams } from "./../driver/mongo/@types";
|
|
4
|
-
import * as v from "valibot";
|
|
3
|
+
//import type{ updateParams } from "./../driver/mongo/@types";
|
|
4
|
+
//import * as v from "valibot";
|
|
5
5
|
import type { Db, MongoClient } from "mongodb";
|
|
6
6
|
|
|
7
7
|
import { useRest } from "../driver/mongo/rest";
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
} from "../driver/mongo/@types";
|
|
20
20
|
import type { RouterRoute } from "hono/types";
|
|
21
21
|
import type { MongoClientOptions } from "mongodb";
|
|
22
|
+
import type { SearchParams } from "meilisearch";
|
|
22
23
|
export type Socket = {
|
|
23
24
|
enabled: boolean;
|
|
24
25
|
exec: (ctx: { rest: useRest; io: socketIoType }) => void;
|
|
@@ -29,8 +30,17 @@ export type Tenant = {
|
|
|
29
30
|
name?: string;
|
|
30
31
|
enabled?: boolean;
|
|
31
32
|
dir: string;
|
|
33
|
+
searchEngine?: {
|
|
34
|
+
meilisearch?: {
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
host: string;
|
|
37
|
+
apiKey: string;
|
|
38
|
+
|
|
39
|
+
client?: InstanceType<typeof import("meilisearch").MeiliSearch>;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
32
42
|
database: {
|
|
33
|
-
driver
|
|
43
|
+
driver?: "mongodb";
|
|
34
44
|
/**
|
|
35
45
|
* Connection URI string to connect
|
|
36
46
|
*/
|
|
@@ -54,7 +64,8 @@ export type Actions =
|
|
|
54
64
|
| "deleteMany"
|
|
55
65
|
| "authCollection"
|
|
56
66
|
| "aggregate"
|
|
57
|
-
| "upload"
|
|
67
|
+
| "upload"
|
|
68
|
+
| "search";
|
|
58
69
|
|
|
59
70
|
type omitWhenType = "insertOne" | "insertMany" | "updateOne" | "updateMany";
|
|
60
71
|
|
|
@@ -197,6 +208,10 @@ export type sessionCtx = {
|
|
|
197
208
|
};
|
|
198
209
|
|
|
199
210
|
export type hooksCtx = (ctx: {
|
|
211
|
+
search?: {
|
|
212
|
+
term: string;
|
|
213
|
+
options: SearchParams;
|
|
214
|
+
};
|
|
200
215
|
filter?: any;
|
|
201
216
|
result?: any | null | object | undefined;
|
|
202
217
|
driver?: "mongodb" | "postgres";
|
|
@@ -244,7 +259,7 @@ export type ctxApi = {
|
|
|
244
259
|
rest: useRest;
|
|
245
260
|
data?: any;
|
|
246
261
|
session?: sessionCtx;
|
|
247
|
-
io?:
|
|
262
|
+
io?: socketIoType;
|
|
248
263
|
params?: findParam;
|
|
249
264
|
id?: string;
|
|
250
265
|
ids?: string[];
|
|
@@ -288,20 +303,61 @@ export type Collection = {
|
|
|
288
303
|
findOne?: (ctx: ctxApi) => object;
|
|
289
304
|
count?: (ctx: ctxApi) => number;
|
|
290
305
|
};
|
|
306
|
+
searchEngine?: {
|
|
307
|
+
meilisearch: {
|
|
308
|
+
/**
|
|
309
|
+
* unique name of the index on meilisearch
|
|
310
|
+
*/
|
|
311
|
+
index: string;
|
|
312
|
+
primaryKey?: string;
|
|
313
|
+
/**
|
|
314
|
+
* default value :auto
|
|
315
|
+
*/
|
|
316
|
+
dispatchAction: "auto" | "manual";
|
|
317
|
+
};
|
|
318
|
+
};
|
|
291
319
|
/**
|
|
292
320
|
* Cache available for action 'find' | 'findOne'
|
|
293
321
|
*/
|
|
294
322
|
cache?: {
|
|
295
323
|
enabled?: boolean;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
324
|
+
state?: {
|
|
325
|
+
[key: string]: any;
|
|
326
|
+
};
|
|
327
|
+
init?: (ctx: {
|
|
328
|
+
rest: InstanceType<typeof useRest>;
|
|
329
|
+
state: {
|
|
330
|
+
[key: string]: any;
|
|
331
|
+
};
|
|
332
|
+
}) => void;
|
|
333
|
+
watch: (ctx: {
|
|
334
|
+
action:
|
|
335
|
+
| "insertOne"
|
|
336
|
+
| "insertMany"
|
|
337
|
+
| "updateOne"
|
|
338
|
+
| "updateMany"
|
|
339
|
+
| "deleteOne"
|
|
340
|
+
| "deleteMany"
|
|
341
|
+
| "findOneAndUpdate";
|
|
342
|
+
rest: InstanceType<typeof useRest>;
|
|
343
|
+
c?: Context;
|
|
344
|
+
session?: sessionCtx;
|
|
345
|
+
result: any;
|
|
346
|
+
state: {
|
|
347
|
+
[key: string]: any;
|
|
348
|
+
};
|
|
349
|
+
}) => void;
|
|
350
|
+
fetch?: (ctx: {
|
|
351
|
+
state: {
|
|
352
|
+
[key: string]: any;
|
|
353
|
+
};
|
|
354
|
+
body: {
|
|
355
|
+
params: findParam;
|
|
356
|
+
pipeline: Array<object>;
|
|
357
|
+
withMeta: boolean;
|
|
358
|
+
};
|
|
359
|
+
key: string;
|
|
360
|
+
action: "find" | "aggregate";
|
|
305
361
|
rest: InstanceType<typeof useRest>;
|
|
306
362
|
c: Context;
|
|
307
363
|
session: sessionCtx;
|
|
@@ -340,6 +396,8 @@ export type Collection = {
|
|
|
340
396
|
afterCount?: hooksCtx;
|
|
341
397
|
beforeUpload?: hooksCtx;
|
|
342
398
|
afterUpload?: hooksCtx;
|
|
399
|
+
beforeSearch?: hooksCtx;
|
|
400
|
+
afterSearch?: hooksCtx;
|
|
343
401
|
};
|
|
344
402
|
access?: {
|
|
345
403
|
"*"?: accessCtx;
|
|
@@ -357,9 +415,11 @@ export type Collection = {
|
|
|
357
415
|
aggregate?: accessCtx;
|
|
358
416
|
upload?: accessCtx;
|
|
359
417
|
count?: accessCtx;
|
|
418
|
+
search?: accessCtx;
|
|
360
419
|
};
|
|
361
420
|
tenant_id?: string;
|
|
362
421
|
slug: string;
|
|
422
|
+
privateFields?: string[];
|
|
363
423
|
timestamps?: boolean;
|
|
364
424
|
description?: string;
|
|
365
425
|
fields?: Field[];
|
|
@@ -542,7 +602,8 @@ export type Q = {
|
|
|
542
602
|
| "execService"
|
|
543
603
|
| "batch"
|
|
544
604
|
| "count"
|
|
545
|
-
| "execToolkit"
|
|
605
|
+
| "execToolkit"
|
|
606
|
+
| "search";
|
|
546
607
|
};
|
|
547
608
|
|
|
548
609
|
export type routeOption = {
|