@opengis/fastify-table 2.5.44 → 2.5.47

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.
Files changed (48) hide show
  1. package/dist/functions.d.ts +1 -0
  2. package/dist/functions.d.ts.map +1 -1
  3. package/dist/functions.js +122 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +4 -1
  6. package/dist/server/migrations/dictionaries.sql +79 -0
  7. package/dist/server/plugins/crud/funcs/dataDelete.d.ts +2 -1
  8. package/dist/server/plugins/crud/funcs/dataDelete.d.ts.map +1 -1
  9. package/dist/server/plugins/crud/funcs/dataDelete.js +2 -2
  10. package/dist/server/plugins/file/utils/inspectContent.d.ts +1 -1
  11. package/dist/server/plugins/file/utils/inspectContent.d.ts.map +1 -1
  12. package/dist/server/plugins/file/utils/inspectContent.js +23 -7
  13. package/dist/server/plugins/policy/funcs/checkXSS.d.ts.map +1 -1
  14. package/dist/server/plugins/policy/funcs/checkXSS.js +1 -0
  15. package/dist/server/plugins/upload/s3.d.ts.map +1 -1
  16. package/dist/server/plugins/upload/s3.js +8 -0
  17. package/dist/server/routes/crud/controllers/insert.d.ts +1 -2
  18. package/dist/server/routes/crud/controllers/insert.d.ts.map +1 -1
  19. package/dist/server/routes/crud/controllers/insert.js +1 -1
  20. package/dist/server/routes/crud/controllers/update.js +1 -1
  21. package/dist/server/routes/dictionaries/dictionaries.route.d.ts +2 -0
  22. package/dist/server/routes/dictionaries/dictionaries.route.d.ts.map +1 -0
  23. package/dist/server/routes/dictionaries/dictionaries.route.js +134 -0
  24. package/dist/server/routes/dictionaries/dictionaries.schema.d.ts +190 -0
  25. package/dist/server/routes/dictionaries/dictionaries.schema.d.ts.map +1 -0
  26. package/dist/server/routes/dictionaries/dictionaries.schema.js +86 -0
  27. package/dist/server/routes/dictionaries/functions/create.dictionary.d.ts +10 -0
  28. package/dist/server/routes/dictionaries/functions/create.dictionary.d.ts.map +1 -0
  29. package/dist/server/routes/dictionaries/functions/create.dictionary.item.d.ts +13 -0
  30. package/dist/server/routes/dictionaries/functions/create.dictionary.item.d.ts.map +1 -0
  31. package/dist/server/routes/dictionaries/functions/create.dictionary.item.js +57 -0
  32. package/dist/server/routes/dictionaries/functions/create.dictionary.js +76 -0
  33. package/dist/server/routes/dictionaries/functions/delete.dictionary.d.ts +9 -0
  34. package/dist/server/routes/dictionaries/functions/delete.dictionary.d.ts.map +1 -0
  35. package/dist/server/routes/dictionaries/functions/delete.dictionary.item.d.ts +11 -0
  36. package/dist/server/routes/dictionaries/functions/delete.dictionary.item.d.ts.map +1 -0
  37. package/dist/server/routes/dictionaries/functions/delete.dictionary.item.js +32 -0
  38. package/dist/server/routes/dictionaries/functions/delete.dictionary.js +28 -0
  39. package/dist/server/routes/dictionaries/functions/read.dictionaries.d.ts +5 -0
  40. package/dist/server/routes/dictionaries/functions/read.dictionaries.d.ts.map +1 -0
  41. package/dist/server/routes/dictionaries/functions/read.dictionaries.js +27 -0
  42. package/dist/server/routes/dictionaries/functions/update.dictionary.d.ts +10 -0
  43. package/dist/server/routes/dictionaries/functions/update.dictionary.d.ts.map +1 -0
  44. package/dist/server/routes/dictionaries/functions/update.dictionary.item.d.ts +13 -0
  45. package/dist/server/routes/dictionaries/functions/update.dictionary.item.d.ts.map +1 -0
  46. package/dist/server/routes/dictionaries/functions/update.dictionary.item.js +48 -0
  47. package/dist/server/routes/dictionaries/functions/update.dictionary.js +51 -0
  48. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  export declare function onListen1(): Promise<void>;
2
2
  export declare function onListen2(): Promise<void>;
3
+ export declare function onListen3(): Promise<void>;
3
4
  export declare function preTemplate({ name, type, user, }: {
4
5
  name?: string;
5
6
  type?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../functions.ts"],"names":[],"mappings":"AAgCA,wBAAsB,SAAS,kBAkH9B;AAED,wBAAsB,SAAS,kBAiJ9B;AAED,wBAAsB,WAAW,CAAC,EAChC,IAAI,EACJ,IAAI,EACJ,IAAS,GACV,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B;;eAqBA;AAED,wBAAsB,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,gBASxE;AAED,wBAAsB,UAAU,CAAC,EAC/B,KAAK,EACL,GAAQ,EACR,OAAO,EAAE,IAAS,EAClB,IAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACzB,OAAO,EAAE,GAAG,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B,iBA+FA;AAED,wBAAsB,aAAa,CAAC,EAClC,IAAI,EACJ,IAAI,EACJ,OAAO,EAAE,IAAS,EAClB,IAAS,GACV,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B,6BAiEA;AAED,wBAAsB,WAAW,CAAC,EAChC,KAAK,EACL,IAAS,EACT,OAAO,EAAE,GAAQ,EACjB,IAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B,gBA6CA;AAED,wBAAsB,WAAW,CAAC,EAChC,KAAK,EACL,IAAI,EACJ,OAAO,EAAE,GAAQ,EACjB,IAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B,gBA2CA;AAED,wBAAsB,OAAO,kBAmC5B"}
1
+ {"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../functions.ts"],"names":[],"mappings":"AAoCA,wBAAsB,SAAS,kBAkH9B;AAED,wBAAsB,SAAS,kBAiJ9B;AAED,wBAAsB,SAAS,kBAkM9B;AAED,wBAAsB,WAAW,CAAC,EAChC,IAAI,EACJ,IAAI,EACJ,IAAS,GACV,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B;;eAqBA;AAED,wBAAsB,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,GAAG,CAAA;CAAE,gBASxE;AAED,wBAAsB,UAAU,CAAC,EAC/B,KAAK,EACL,GAAQ,EACR,OAAO,EAAE,IAAS,EAClB,IAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACzB,OAAO,EAAE,GAAG,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B,iBA+FA;AAED,wBAAsB,aAAa,CAAC,EAClC,IAAI,EACJ,IAAI,EACJ,OAAO,EAAE,IAAS,EAClB,IAAS,GACV,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC5B,6BAiEA;AAED,wBAAsB,WAAW,CAAC,EAChC,KAAK,EACL,IAAS,EACT,OAAO,EAAE,GAAQ,EACjB,IAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B,gBA6CA;AAED,wBAAsB,WAAW,CAAC,EAChC,KAAK,EACL,IAAI,EACJ,OAAO,EAAE,GAAQ,EACjB,IAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B,gBA2CA;AAED,wBAAsB,OAAO,kBAmC5B"}
package/dist/functions.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { existsSync, readdirSync, readFileSync, mkdirSync, writeFileSync, } from "node:fs";
3
- import { createHash } from "node:crypto";
3
+ import { createHash, randomUUID } from "node:crypto";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import config from "./config.js";
6
6
  import getTemplatePath from "./server/plugins/table/funcs/getTemplatePath.js";
@@ -12,11 +12,14 @@ import getMenu from "./server/routes/menu/controllers/getMenu.js";
12
12
  import getSelectVal from "./server/plugins/table/funcs/metaFormat/getSelectVal.js";
13
13
  import locales from "./server/routes/table/controllers/utils/locales.js";
14
14
  import pgClients from "./server/plugins/pg/pgClients.js";
15
+ import { readdir } from "node:fs/promises";
16
+ import { readFile } from "node:fs/promises";
15
17
  const { client } = pgClients;
16
18
  const rclient = getRedis();
17
19
  // core templates && cls
18
20
  const filename = fileURLToPath(import.meta.url);
19
21
  const cwd = path.dirname(filename);
22
+ const shortId = () => randomUUID().split("-").pop() || "";
20
23
  export async function onListen1() {
21
24
  const json = await getMenu({ user: { uid: "1" }, pg: client }, null).catch(); // prevent "too many clients already" from crashing the application
22
25
  // insert interface list to db (user access management)
@@ -178,6 +181,124 @@ export async function onListen2() {
178
181
  console.trace(err);
179
182
  }
180
183
  }
184
+ export async function onListen3() {
185
+ const clsQuery = [];
186
+ if (client && !client?.pk && client?.init) {
187
+ await client.init();
188
+ }
189
+ if (!client?.pk?.["admin.dictionaries"])
190
+ return;
191
+ try {
192
+ const dictionaries = existsSync("dictionaries")
193
+ ? (await readdir("dictionaries", { withFileTypes: true }))
194
+ .filter((el) => el.isFile() && path.extname(el.name) === ".json")
195
+ .map((el) => el.name)
196
+ : [];
197
+ if (!dictionaries?.length)
198
+ return;
199
+ const uniqueDictionaries = dictionaries.filter((el, idx, arr) => arr.indexOf(el) === idx);
200
+ const hashes = await rclient
201
+ .hgetall("dictionaries-insert-hashes")
202
+ .then((obj) => Object.keys(obj));
203
+ const dbdata = await client
204
+ .query(`select json_object_agg(code, hash) from admin.dictionaries where system is true`)
205
+ .then((el) => el.rows?.[0]?.json_object_agg || {});
206
+ const codes = Object.keys(dbdata);
207
+ console.log("admin/hook dictionaries promise start");
208
+ const qHashes = await Promise.all(uniqueDictionaries.map(async (dictFileName) => {
209
+ const { code, name, module: moduleName, editable, values: arr, } = JSON.parse(await readFile(`dictionaries/${dictFileName}`, "utf-8"));
210
+ const hash = createHash("md5")
211
+ .update(JSON.stringify(arr || ""))
212
+ .digest("hex");
213
+ const dbhash = dbdata[code];
214
+ // check for changes by redis hash / dropped from db / changed at git project
215
+ const update = code &&
216
+ (!hashes.includes(hash) || !codes.includes(code) || hash !== dbhash);
217
+ if (update) {
218
+ const id = shortId();
219
+ const mainArgs = [
220
+ id,
221
+ code,
222
+ name,
223
+ moduleName,
224
+ "git",
225
+ editable || false,
226
+ true,
227
+ hash,
228
+ ];
229
+ const mainQuery = `insert into admin.dictionaries(id,code,name,module,source,editable,system,hash) values($1::text,$2,$3,$4,$5,$6,$7,$8)`;
230
+ const itemsArgs = [];
231
+ const itemArgsLength = 8;
232
+ const itemsQuery = `insert into admin.dictionary_items(dictionary_id,code,name,icon,color,parent_id,system,sort_order) values ${arr.map((_, idx) => `(${Array.from({ length: itemArgsLength }, (_, i) => `$${itemsArgs.length + i + 1 + idx * itemArgsLength}${i <= 5 ? "::text" : ""}`).join(",")})`)}`;
233
+ arr.forEach((el) => {
234
+ itemsArgs.push(id);
235
+ itemsArgs.push(el.code);
236
+ itemsArgs.push(el.name);
237
+ itemsArgs.push(el.icon);
238
+ itemsArgs.push(el.color);
239
+ itemsArgs.push(el.parent_id);
240
+ itemsArgs.push(true);
241
+ itemsArgs.push(el.sort || 0);
242
+ });
243
+ if (config.trace) {
244
+ console.log(code, "insert fresh dictionary");
245
+ }
246
+ return {
247
+ code,
248
+ update,
249
+ hash,
250
+ mainQuery,
251
+ mainArgs,
252
+ itemsQuery,
253
+ itemsArgs,
254
+ };
255
+ }
256
+ else {
257
+ if (config.trace) {
258
+ console.log(code, codes.includes(code)
259
+ ? "skip equal hash dictionary"
260
+ : "insert missing dictionary");
261
+ }
262
+ return { code, update, hash, equalHash: hashes.includes(hash) };
263
+ }
264
+ }));
265
+ // debug
266
+ const logDir = path.join(cwd, "log/migration");
267
+ mkdirSync(logDir, { recursive: true });
268
+ writeFileSync(path.join(logDir, `${path.basename(cwd)}-${client.options?.database}-dictionaries.sql`), clsQuery.filter(Boolean).join(";"));
269
+ writeFileSync(path.join(logDir, `${path.basename(cwd)}-${client.options?.database}-dictionaries.json`), JSON.stringify(dictionaries));
270
+ const updateIds = qHashes
271
+ .filter((el) => el.mainQuery)
272
+ .map((el) => el.code);
273
+ const rowCountItems = await client
274
+ .query("delete from admin.dictionary_items where dictionary_id in (select id from admin.dictionaries where system is true and code=any($1::text[]))", [updateIds])
275
+ .then((r) => r.rowCount || 0);
276
+ const rowCount = await client
277
+ .query("delete from admin.dictionaries where system is true and code=any($1::text[])", [updateIds])
278
+ .then((r) => r.rowCount || 0);
279
+ console.log("fastify-table/hook old dictionaries deleted", rowCount, rowCountItems);
280
+ if (qHashes.find((el) => el && el.mainQuery)) {
281
+ await Promise.all(qHashes
282
+ .filter((el) => el && el.mainQuery)
283
+ .map(async ({ hash, mainQuery, mainArgs, itemsQuery, itemsArgs, }) => {
284
+ await client.query(mainQuery, mainArgs);
285
+ await client.query(itemsQuery, itemsArgs);
286
+ rclient.hset("dictionaries-insert-hashes", hash, 1);
287
+ }));
288
+ logger.file("migration/hash", {
289
+ list: qHashes.filter(Boolean),
290
+ dictionaries: true,
291
+ });
292
+ console.log("fastify-table/hook dictionaries sql finish");
293
+ }
294
+ else {
295
+ console.log("fastify-table/hook dictionaries sql insert skipped");
296
+ }
297
+ }
298
+ catch (err) {
299
+ console.error("fastify-table/hook dictionaries sql error", err.toString());
300
+ }
301
+ }
181
302
  export async function preTemplate({ name, type, user = {}, }) {
182
303
  if (!name || !type)
183
304
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AA8FA,iBAAe,MAAM,CAAC,OAAO,EAAE,GAAG,iBAmPjC;;AACD,wBAEG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAgGA,iBAAe,MAAM,CAAC,OAAO,EAAE,GAAG,iBAsPjC;;AACD,wBAEG"}
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ const cwd = path.dirname(filename);
9
9
  config.templates?.forEach((el) => addTemplateDir(el));
10
10
  addTemplateDir(path.join(cwd, "module/core"));
11
11
  import config from "./config.js";
12
- import { preForm, afterInsert, afterTable, afterTemplate, afterUpdate, preTemplate, onReady, onListen1, onListen2, } from "./functions.js";
12
+ import { preForm, afterInsert, afterTable, afterTemplate, afterUpdate, preTemplate, onReady, onListen1, onListen2, onListen3, } from "./functions.js";
13
13
  import addHook from "./server/plugins/hook/addHook.js";
14
14
  const { maxFileSize = 512 } = config;
15
15
  const { name: execName } = path.parse(process.argv0);
@@ -56,6 +56,7 @@ import uploadChunkRoutes from "./server/routes/upload/index.js";
56
56
  import grpcRoutes from "./server/routes/grpc/index.js";
57
57
  import notificationsRoutes from "./server/routes/notifications/index.js";
58
58
  import userClsRoutes from "./server/routes/usercls/index.js";
59
+ import dictionariesRoutes from "./server/routes/dictionaries/dictionaries.route.js";
59
60
  import apiListService from "./server/routes/util/controllers/api.list.js";
60
61
  addHook("preForm", preForm);
61
62
  addHook("afterTable", afterTable);
@@ -138,6 +139,7 @@ async function plugin(fastify) {
138
139
  });
139
140
  fastify.addHook("onListen", onListen1);
140
141
  fastify.addHook("onListen", onListen2);
142
+ fastify.addHook("onListen", onListen3);
141
143
  fastify.addHook("onReady", onReady);
142
144
  // core migrations (second argument for core only)
143
145
  execMigrations(path.join(cwd, "server/migrations"), pgClients.client, true).catch((err) => console.warn(err.toString()));
@@ -226,6 +228,7 @@ async function plugin(fastify) {
226
228
  // from admin
227
229
  fastify.register(notificationsRoutes, opt);
228
230
  fastify.register(userClsRoutes, opt);
231
+ fastify.register(dictionariesRoutes, opt);
229
232
  config.proxy?.forEach?.((el) => {
230
233
  if (execName === "bun") {
231
234
  fastify.all(`${el.source}/*`, async (req, reply) => {
@@ -0,0 +1,79 @@
1
+ create schema if not exists admin;
2
+
3
+ DO $$
4
+ BEGIN
5
+ IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'dictionary_source_enum') THEN
6
+ CREATE TYPE admin.dictionary_source_enum AS ENUM (
7
+ 'git',
8
+ 'custom'
9
+ );
10
+ END IF;
11
+ END $$;
12
+
13
+ CREATE TABLE if not exists admin.dictionaries (
14
+ id text PRIMARY KEY DEFAULT public.next_id(),
15
+
16
+ code VARCHAR(100) NOT NULL UNIQUE,
17
+ name VARCHAR(255) NOT NULL,
18
+ module VARCHAR(50),
19
+ description TEXT,
20
+
21
+ source admin.dictionary_source_enum NOT NULL DEFAULT 'custom',
22
+ editable BOOLEAN NOT NULL DEFAULT true,
23
+ system BOOLEAN NOT NULL DEFAULT false,
24
+
25
+ hash text,
26
+
27
+ created_at TIMESTAMP NOT NULL DEFAULT now(),
28
+ updated_at TIMESTAMP NOT NULL DEFAULT now()
29
+ );
30
+
31
+ CREATE TABLE if not exists admin.dictionary_items (
32
+ id text PRIMARY KEY DEFAULT public.next_id(),
33
+
34
+ dictionary_id text NOT NULL REFERENCES admin.dictionaries(id) ON DELETE CASCADE,
35
+
36
+ code VARCHAR(100) NOT NULL,
37
+ name VARCHAR(255) NOT NULL,
38
+ description TEXT,
39
+
40
+ color VARCHAR(20),
41
+ icon VARCHAR(50),
42
+
43
+ sort_order INTEGER NOT NULL DEFAULT 0,
44
+
45
+ parent_id text,
46
+
47
+ active BOOLEAN NOT NULL DEFAULT true,
48
+ system BOOLEAN NOT NULL DEFAULT false,
49
+
50
+ metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
51
+
52
+ created_at TIMESTAMP NOT NULL DEFAULT now(),
53
+ updated_at TIMESTAMP NOT NULL DEFAULT now(),
54
+
55
+ CONSTRAINT admin_dictionary_items_dictionary_code_unique
56
+ UNIQUE (code, dictionary_id)
57
+ );
58
+
59
+ ALTER TABLE admin.dictionary_items
60
+ DROP CONSTRAINT if exists admin_dictionary_items_dictionary_code_unique cascade;
61
+
62
+ ALTER TABLE admin.dictionary_items
63
+ ADD CONSTRAINT admin_dictionary_items_dictionary_code_unique UNIQUE (code, dictionary_id);
64
+
65
+ ALTER TABLE admin.dictionary_items
66
+ DROP CONSTRAINT if exists admin_dictionary_items_parent_same_dictionary_fk;
67
+
68
+ ALTER TABLE admin.dictionary_items
69
+ ADD CONSTRAINT admin_dictionary_items_parent_same_dictionary_fk FOREIGN KEY (parent_id, dictionary_id)
70
+ REFERENCES admin.dictionary_items (code, dictionary_id)
71
+ ON DELETE SET NULL;
72
+
73
+ CREATE INDEX if not exists admin_dictionary_items_dictionary_id_idx ON admin.dictionary_items (dictionary_id);
74
+
75
+ CREATE INDEX if not exists admin_dictionary_items_parent_id_idx ON admin.dictionary_items (parent_id);
76
+
77
+ CREATE INDEX if not exists admin_dictionary_items_sort_order_idx ON admin.dictionary_items (dictionary_id, sort_order);
78
+
79
+ CREATE INDEX if not exists admin_dictionary_items_active_idx ON admin.dictionary_items (dictionary_id, active);
@@ -1,5 +1,5 @@
1
1
  import type { ExtendedPG } from "../../../types/core.js";
2
- export default function dataDelete({ table: table1, tokenData, referer, id, pg: pg1, uid, query, }: {
2
+ export default function dataDelete({ table: table1, tokenData, referer, id, pg: pg1, uid, query, args, }: {
3
3
  table: string;
4
4
  tokenData?: Record<string, any>;
5
5
  referer?: string;
@@ -7,5 +7,6 @@ export default function dataDelete({ table: table1, tokenData, referer, id, pg:
7
7
  pg?: ExtendedPG;
8
8
  uid: string | number;
9
9
  query?: string;
10
+ args?: any[];
10
11
  }): Promise<any>;
11
12
  //# sourceMappingURL=dataDelete.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dataDelete.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/crud/funcs/dataDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAezD,wBAA8B,UAAU,CAAC,EACvC,KAAK,EAAE,MAAM,EACb,SAAS,EACT,OAAO,EACP,EAAE,EACF,EAAE,EAAE,GAAG,EACP,GAAG,EACH,KAAK,GACN,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,gBA+FA"}
1
+ {"version":3,"file":"dataDelete.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/crud/funcs/dataDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAezD,wBAA8B,UAAU,CAAC,EACvC,KAAK,EAAE,MAAM,EACb,SAAS,EACT,OAAO,EACP,EAAE,EACF,EAAE,EAAE,GAAG,EACP,GAAG,EACH,KAAK,EACL,IAAI,GACL,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,EAAE,CAAC,EAAE,UAAU,CAAC;IAChB,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;CACd,gBA+FA"}
@@ -7,7 +7,7 @@ import extraData from "../../extra/extraData.js";
7
7
  import logChanges from "./utils/logChanges.js";
8
8
  import logger from "../../logger/getLogger.js";
9
9
  const rclient = getRedis();
10
- export default async function dataDelete({ table: table1, tokenData, referer, id, pg: pg1, uid, query, }) {
10
+ export default async function dataDelete({ table: table1, tokenData, referer, id, pg: pg1, uid, query, args, }) {
11
11
  const pg = pg1 || getPG({ name: "client" });
12
12
  if (!pg)
13
13
  return null;
@@ -47,7 +47,7 @@ export default async function dataDelete({ table: table1, tokenData, referer, id
47
47
  row,
48
48
  }, client);
49
49
  const res = await client
50
- .query(delQuery, [id])
50
+ .query(delQuery, [id].concat(args || []))
51
51
  .then((el) => (el.rows?.[0] ? { rowCount: 1, ...el.rows[0] } : {}));
52
52
  await logChanges({
53
53
  pg: client,
@@ -1,4 +1,4 @@
1
- export default function inspectContent(buffer: Buffer, expectedExtension: string): Promise<{
1
+ export default function inspectContent(input: Buffer | ReadableStream, expectedExtension: string): Promise<{
2
2
  ext: any;
3
3
  mime: any;
4
4
  isValid: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"inspectContent.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/file/utils/inspectContent.ts"],"names":[],"mappings":"AA+CA,wBAA8B,cAAc,CAC1C,MAAM,EAAE,MAAM,EACd,iBAAiB,EAAE,MAAM;;;;GAgD1B"}
1
+ {"version":3,"file":"inspectContent.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/file/utils/inspectContent.ts"],"names":[],"mappings":"AAkDA,wBAA8B,cAAc,CAC1C,KAAK,EAAE,MAAM,GAAG,cAAc,EAC9B,iBAAiB,EAAE,MAAM;;;;GAoE1B"}
@@ -1,4 +1,4 @@
1
- import { fileTypeFromBuffer } from "file-type";
1
+ import { fileTypeFromBuffer, fileTypeFromStream } from "file-type";
2
2
  import { BadRequestError } from "../../../../errors.js";
3
3
  import applyHook from "../../hook/applyHook.js";
4
4
  // Office 97–2003 (.doc/.xls/.ppt) share CFB magic; file-type reports them as "cfb"
@@ -38,24 +38,40 @@ const handlers = {
38
38
  }
39
39
  },
40
40
  };
41
- export default async function inspectContent(buffer, expectedExtension) {
41
+ const isReadableStream = (stream) => stream && stream instanceof ReadableStream;
42
+ export default async function inspectContent(input, expectedExtension) {
42
43
  if (!expectedExtension) {
43
- throw new BadRequestError("not enough params: expectedExtension");
44
+ throw new Error("not enough params: expectedExtension");
44
45
  }
45
- if (!Buffer.isBuffer(buffer) || !buffer.length) {
46
- throw new BadRequestError("Invalid buffer");
46
+ const buffer = Buffer.isBuffer(input) ? input : undefined;
47
+ const stream = !buffer ? input : undefined;
48
+ if (buffer && !buffer.length) {
49
+ throw BadRequestError("Invalid buffer");
50
+ }
51
+ if (stream && !isReadableStream(stream)) {
52
+ throw BadRequestError("invalid params: input");
53
+ }
54
+ if (!buffer && !stream) {
55
+ throw new Error("not enough params: input");
47
56
  }
48
57
  // custom inspection of extensions like kml, svg etc
49
58
  const hookData = (await applyHook("onInspectContent", {
50
59
  buffer,
60
+ stream,
51
61
  expectedExtension,
52
62
  }));
53
63
  if (hookData) {
54
64
  return hookData;
55
65
  }
56
- const { ext, mime } = (await fileTypeFromBuffer(buffer)) || {};
66
+ const inspectResult1 = buffer
67
+ ? await fileTypeFromBuffer(buffer)
68
+ : stream
69
+ ? await fileTypeFromStream(stream)
70
+ : undefined;
71
+ const inspectResult2 = stream ? await fileTypeFromStream(stream) : undefined;
72
+ const { ext, mime } = inspectResult1 || inspectResult2 || {};
57
73
  // ? geojson, signed pdf, kml, svg etc - not recognized - inspecting the content
58
- if (!ext && handlers[expectedExtension]) {
74
+ if (!ext && handlers[expectedExtension] && buffer) {
59
75
  const { ext: ext2, mime: mime2 } = (await handlers[expectedExtension](buffer)) || {};
60
76
  return { ext: ext2, mime: mime2, isValid: ext2 === expectedExtension };
61
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"checkXSS.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/policy/funcs/checkXSS.ts"],"names":[],"mappings":"AAGA,iBAAS,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAW,EAAE,EAAE,GAAG;;;;;;EAyD3C;AAED,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"checkXSS.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/policy/funcs/checkXSS.ts"],"names":[],"mappings":"AAGA,iBAAS,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAW,EAAE,EAAE,GAAG;;;;;;EA0D3C;AAED,eAAe,QAAQ,CAAC"}
@@ -14,6 +14,7 @@ function checkXSS({ body, schema = {} }) {
14
14
  "Tiny",
15
15
  "Ace",
16
16
  "Texteditor",
17
+ "RichText",
17
18
  ];
18
19
  Object.keys(body)
19
20
  .filter((key) => ["<", ">"].find((el) => body[key]?.includes?.(el)) &&
@@ -1 +1 @@
1
- {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../../server/plugins/upload/s3.ts"],"names":[],"mappings":"AAiGA,wBAAsB,sBAAsB,CAAC,EAAE,MAAW,iBAwCzD;AAED,wBAAsB,cAAc,CAClC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,GAAG,EACT,GAAG,SAAqB,iBAazB;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,MAAM,EACV,GAAG,SAAqB,EACxB,EAAE,MAAW,iBAsBd;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,iBA2B9D;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,gBAQ7C;AAED,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,gBAMrD;AAED,wBAAsB,mBAAmB,CAAC,EAAE,MAAW,iBAyEtD;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,MAAW;;;;;;;;GAsCrE;AAKD,wBAAsB,WAAW,CAC/B,EACE,MAAM,EACN,GAAG,EACH,WAAW,EACX,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB,EACD,EAAE,MAAW;;;;;;;;;;;;;;;;GAsDd;AASD,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB,iBAwBF;AAED,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,kBAY9C;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,iBAQjD;AAED,wBAAsB,WAAW,CAC/B,EACE,EAAE,EACF,KAAK,EACL,UAAU,GACX,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,GAAG,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB,EACD,EAAE,MAAW;;;;;;;;GA+Bd;AAED,wBAAsB,YAAY,CAChC,EACE,EAAE,GACH,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;CACZ,EACD,EAAE,MAAW;;;;GA2Cd;AAED,wBAAsB,WAAW,CAC/B,EACE,EAAE,GACH,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;CACZ,EACD,EAAE,MAAW;;GAwBd;AAED,wBAAsB,eAAe,CACnC,EACE,EAAE,EACF,GAAG,EACH,MAAM,GACP,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,EACD,EAAE,MAAW;;;;;;;;;;;;;;GAqEd"}
1
+ {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../../server/plugins/upload/s3.ts"],"names":[],"mappings":"AAmGA,wBAAsB,sBAAsB,CAAC,EAAE,MAAW,iBAwCzD;AAED,wBAAsB,cAAc,CAClC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,GAAG,EACT,GAAG,SAAqB,iBAazB;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,MAAM,EACV,GAAG,SAAqB,EACxB,EAAE,MAAW,iBAsBd;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,iBA2B9D;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,gBAQ7C;AAED,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,gBAMrD;AAED,wBAAsB,mBAAmB,CAAC,EAAE,MAAW,iBAyEtD;AAED,wBAAsB,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,MAAW;;;;;;;;GAsCrE;AAKD,wBAAsB,WAAW,CAC/B,EACE,MAAM,EACN,GAAG,EACH,WAAW,EACX,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB,EACD,EAAE,MAAW;;;;;;;;;;;;;;;;GAsDd;AASD,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,MAAM,EACV,IAAI,EAAE;IACJ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB,iBAwBF;AAED,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,kBAY9C;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,iBAQjD;AAED,wBAAsB,WAAW,CAC/B,EACE,EAAE,EACF,KAAK,EACL,UAAU,GACX,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,GAAG,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB,EACD,EAAE,MAAW;;;;;;;;GAyCd;AAED,wBAAsB,YAAY,CAChC,EACE,EAAE,GACH,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;CACZ,EACD,EAAE,MAAW;;;;GA2Cd;AAED,wBAAsB,WAAW,CAC/B,EACE,EAAE,GACH,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;CACZ,EACD,EAAE,MAAW;;GAwBd;AAED,wBAAsB,eAAe,CACnC,EACE,EAAE,EACF,GAAG,EACH,MAAM,GACP,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,EACD,EAAE,MAAW;;;;;;;;;;;;;;GAqEd"}
@@ -4,6 +4,8 @@ import config from "../../../config.js";
4
4
  import s3Client from "../file/providers/s3/client.js";
5
5
  import rclient from "../redis/client.js";
6
6
  import logger from "../logger/getLogger.js";
7
+ import inspectContent from "../file/utils/inspectContent.js";
8
+ import { ConflictError } from "../../../errors.js";
7
9
  const uploads = new Map();
8
10
  const resumableUploads = new Map();
9
11
  // non-redis upload timeout handlers
@@ -339,6 +341,12 @@ export async function uploadChunk({ id, chunk, partNumber, }, s3 = s3Client) {
339
341
  if (upload.parts.some((p) => p.PartNumber === partNumber)) {
340
342
  throw new Error(`Part ${partNumber} already uploaded`);
341
343
  }
344
+ if (partNumber === 1) {
345
+ const { isValid } = await inspectContent(chunk, path.extname(upload.Key).substring(1));
346
+ if (!isValid) {
347
+ throw ConflictError("file type spoofing attempt detected");
348
+ }
349
+ }
342
350
  const res = await s3.send(new UploadPartCommand({
343
351
  Bucket: upload.Bucket,
344
352
  Key: upload.Key,
@@ -1,3 +1,2 @@
1
- import { type FastifyReply } from "fastify";
2
- export default function insert(req: any, reply: FastifyReply): Promise<any>;
1
+ export default function insert(req: any): Promise<any>;
3
2
  //# sourceMappingURL=insert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../../../../server/routes/crud/controllers/insert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAqB5C,wBAA8B,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,gBAgMjE"}
1
+ {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../../../../server/routes/crud/controllers/insert.ts"],"names":[],"mappings":"AAqBA,wBAA8B,MAAM,CAAC,GAAG,EAAE,GAAG,gBAgM5C"}
@@ -10,7 +10,7 @@ import getToken from "../../../plugins/crud/funcs/getToken.js";
10
10
  import getTemplate from "../../../plugins/table/funcs/getTemplate.js";
11
11
  import { resources } from "../../../plugins/access/funcs/getResources.js";
12
12
  import { BadRequestError, ConflictError, ForbiddenError, NotFoundError, } from "../../../../errors.js";
13
- export default async function insert(req, reply) {
13
+ export default async function insert(req) {
14
14
  const { pg = pgClients.client, user = {}, params = {}, body = {}, headers = {}, method, } = req || {};
15
15
  if (!user) {
16
16
  throw ForbiddenError("access restricted");
@@ -76,7 +76,7 @@ export default async function update(req, reply) {
76
76
  throw BadRequestError("table is required");
77
77
  }
78
78
  if (!id && tokenData?.table) {
79
- return insert(req, reply);
79
+ return insert(req);
80
80
  }
81
81
  if (!id) {
82
82
  throw BadRequestError("id is required");
@@ -0,0 +1,2 @@
1
+ export default function route(app: any): void;
2
+ //# sourceMappingURL=dictionaries.route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dictionaries.route.d.ts","sourceRoot":"","sources":["../../../../server/routes/dictionaries/dictionaries.route.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,GAAG,EAAE,GAAG,QAmJrC"}
@@ -0,0 +1,134 @@
1
+ import readDictionaries from "./functions/read.dictionaries.js";
2
+ import createDictionary from "./functions/create.dictionary.js";
3
+ import updateDictionary from "./functions/update.dictionary.js";
4
+ import deleteDictionary from "./functions/delete.dictionary.js";
5
+ import createDictionaryItem from "./functions/create.dictionary.item.js";
6
+ import updateDictionaryItem from "./functions/update.dictionary.item.js";
7
+ import deleteDictionaryItem from "./functions/delete.dictionary.item.js";
8
+ import { dictionaryParamsSchema, createDictionaryBodySchema, createDictionaryItemBodySchema, updateDictionaryBodySchema, updateDictionaryItemBodySchema, } from "./dictionaries.schema.js";
9
+ const tags = ["core", "dictionaries"];
10
+ const packageName = "fastify-table";
11
+ export default function route(app) {
12
+ app.route({
13
+ method: "GET",
14
+ url: "/dictionaries/:code?",
15
+ config: { tags, package: packageName, policy: "L0" },
16
+ schema: { params: dictionaryParamsSchema },
17
+ handler: async ({ params, pg }) => {
18
+ const result = await readDictionaries({
19
+ code: params.code,
20
+ }, pg);
21
+ return result;
22
+ },
23
+ });
24
+ app.route({
25
+ method: "GET",
26
+ url: "/dictionaries/:code/values",
27
+ config: { tags, package: packageName, policy: "L0" },
28
+ schema: { params: dictionaryParamsSchema },
29
+ handler: async ({ params, pg }) => {
30
+ const result = await readDictionaries({
31
+ code: params.code,
32
+ values: true,
33
+ }, pg);
34
+ return result;
35
+ },
36
+ });
37
+ app.route({
38
+ method: "POST",
39
+ url: "/dictionaries/:id?",
40
+ config: { tags, package: packageName, policy: "L1" },
41
+ schema: {
42
+ params: dictionaryParamsSchema,
43
+ body: createDictionaryBodySchema,
44
+ },
45
+ handler: async ({ params, body, user, pg }) => {
46
+ const result = await createDictionary({
47
+ id: params.id,
48
+ body,
49
+ uid: user?.uid,
50
+ }, pg);
51
+ return result;
52
+ },
53
+ });
54
+ app.route({
55
+ method: "PUT",
56
+ url: "/dictionaries/:id",
57
+ config: { tags, package: packageName, policy: "L1" },
58
+ schema: {
59
+ params: dictionaryParamsSchema,
60
+ body: updateDictionaryBodySchema,
61
+ },
62
+ handler: async ({ params, user, body, pg }) => {
63
+ const result = await updateDictionary({
64
+ id: params.id,
65
+ body,
66
+ uid: user?.uid,
67
+ }, pg);
68
+ return result;
69
+ },
70
+ });
71
+ app.route({
72
+ method: "DELETE",
73
+ url: "/dictionaries/:id",
74
+ config: { tags, package: packageName, policy: "L1" },
75
+ schema: { params: dictionaryParamsSchema },
76
+ handler: async ({ params, user, pg }) => {
77
+ const result = await deleteDictionary({
78
+ id: params.id,
79
+ uid: user?.uid,
80
+ }, pg);
81
+ return result;
82
+ },
83
+ });
84
+ app.route({
85
+ method: "POST",
86
+ url: "/dictionaries/:code/values/:id?",
87
+ config: { tags, package: packageName, policy: "L1" },
88
+ schema: {
89
+ params: dictionaryParamsSchema,
90
+ body: createDictionaryItemBodySchema,
91
+ },
92
+ handler: async ({ params, body, user, pg }) => {
93
+ const result = await createDictionaryItem({
94
+ id: params.id,
95
+ code: params.code,
96
+ body,
97
+ uid: user?.uid,
98
+ }, pg);
99
+ return result;
100
+ },
101
+ });
102
+ app.route({
103
+ method: "PUT",
104
+ url: "/dictionaries/:code/values/:id",
105
+ config: { tags, package: packageName, policy: "L1" },
106
+ schema: {
107
+ params: dictionaryParamsSchema,
108
+ body: updateDictionaryItemBodySchema,
109
+ },
110
+ handler: async ({ params, body, user, pg }) => {
111
+ const result = await updateDictionaryItem({
112
+ id: params.id,
113
+ code: params.code,
114
+ body,
115
+ uid: user?.uid,
116
+ }, pg);
117
+ return result;
118
+ },
119
+ });
120
+ app.route({
121
+ method: "DELETE",
122
+ url: "/dictionaries/:code/values/:id",
123
+ config: { tags, package: packageName, policy: "L1" },
124
+ schema: { params: dictionaryParamsSchema },
125
+ handler: async ({ params, user, pg }) => {
126
+ const result = await deleteDictionaryItem({
127
+ id: params.id,
128
+ code: params.code,
129
+ uid: user?.uid,
130
+ }, pg);
131
+ return result;
132
+ },
133
+ });
134
+ }