@opengis/fastify-table 2.0.148 → 2.0.150

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.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../config.ts"],"names":[],"mappings":"AAWA,QAAA,MAAM,MAAM,KAA8D,CAAC;AAsC3E,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../config.ts"],"names":[],"mappings":"AAWA,QAAA,MAAM,MAAM,KAA8D,CAAC;AAgD3E,eAAe,MAAM,CAAC"}
package/dist/config.js CHANGED
@@ -15,6 +15,13 @@ Object.assign(config, {
15
15
  if (existsSync(`.env.local`)) {
16
16
  dotenv.config({ path: `.env.local` }); // ! load .env.local
17
17
  }
18
+ // example: bun server kamianske
19
+ if (process.cwd().split("\\").pop() &&
20
+ process.cwd().split("\\").pop() === "fastify-table" &&
21
+ process.argv[2] &&
22
+ existsSync(`.env.${process.argv[2]}`)) {
23
+ dotenv.config({ path: `.env.${process.argv[2]}` }); // ! for debug only
24
+ }
18
25
  if (process.env?.NODE_ENV && existsSync(`.env.${process.env.NODE_ENV}`)) {
19
26
  dotenv.config({ path: `.env.${process.env.NODE_ENV}` }); // ! load .env.{{production}} etc.
20
27
  }
@@ -16,6 +16,7 @@ export default function logChanges({ pg, table: table1, tokenData, referer, id,
16
16
  change_type: string;
17
17
  old: any;
18
18
  new: any;
19
+ titles: Record<string, string>;
19
20
  error?: undefined;
20
21
  } | {
21
22
  error: any;
@@ -26,5 +27,6 @@ export default function logChanges({ pg, table: table1, tokenData, referer, id,
26
27
  change_id?: undefined;
27
28
  old?: undefined;
28
29
  new?: undefined;
30
+ titles?: undefined;
29
31
  } | null>;
30
32
  //# sourceMappingURL=logChanges.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logChanges.d.ts","sourceRoot":"","sources":["../../../../../../server/plugins/crud/funcs/utils/logChanges.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AA4B5D,wBAA8B,UAAU,CAAC,EACvC,EAAE,EACF,KAAK,EAAE,MAAM,EACb,SAAS,EACT,OAAO,EACP,EAAE,EACF,IAAI,EACJ,GAAO,EACP,IAAI,GACL,EAAE;IACD,EAAE,EAAE,UAAU,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;;;;;;;;;;;;;;;;;;UAuJA"}
1
+ {"version":3,"file":"logChanges.d.ts","sourceRoot":"","sources":["../../../../../../server/plugins/crud/funcs/utils/logChanges.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AA8C5D,wBAA8B,UAAU,CAAC,EACvC,EAAE,EACF,KAAK,EAAE,MAAM,EACb,SAAS,EACT,OAAO,EACP,EAAE,EACF,IAAI,EACJ,GAAO,EACP,IAAI,GACL,EAAE;IACD,EAAE,EAAE,UAAU,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;;;;;;;;;;;;;;;;;;;;UA8MA"}
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import getTemplate from "../../../table/funcs/getTemplate.js";
3
3
  import metaFormat from "../../../table/funcs/metaFormat/index.js";
4
+ import getMeta from "../../../pg/funcs/getMeta.js";
4
5
  const defaultTitles = {
5
6
  editor_date: "Дата оновлення",
6
7
  editor_id: "Редактор",
@@ -14,15 +15,26 @@ const defaultTitles = {
14
15
  size: "Розмір файлу",
15
16
  ext: "Розширення файлу",
16
17
  };
17
- function getValue(val, tableName) {
18
+ // skip log of system columns changes made by CRUD functions - log only changes of data
19
+ const systemColumns = [
20
+ "cdate",
21
+ "editor_date",
22
+ "created_at",
23
+ "updated_at",
24
+ "editor_id",
25
+ "uid",
26
+ "updated_by",
27
+ "created_by",
28
+ ];
29
+ function getValue(val, tableName, columnTypes) {
30
+ if (typeof val === "boolean")
31
+ return val;
18
32
  if (!val)
19
33
  return null;
20
34
  if (["crm.files"].includes(tableName)) {
21
35
  return typeof val === "object" ? JSON.stringify(val) : val;
22
36
  }
23
- return typeof val === "object"
24
- ? JSON.stringify(val)?.substring?.(0, 30)
25
- : val?.toString?.()?.substring?.(0, 30);
37
+ return typeof val === "object" ? null : val?.toString?.()?.substring?.(0, 30);
26
38
  }
27
39
  // extract titles and cls from form schema
28
40
  // alt: extract table template from referer -> form
@@ -49,10 +61,11 @@ export default async function logChanges({ pg, table: table1, tokenData, referer
49
61
  .query(`insert into log.table_changes(change_date,change_type,change_user_id,entity_type,entity_id)
50
62
  values(CURRENT_DATE, $1, $2, $3, $4) returning change_id`, [type, uid, table, id])
51
63
  .then((el) => el.rows?.[0] || {});
52
- const q = `select json_object_agg(entity_key, value_hash) from (
64
+ const q = `select json_object_agg(entity_key, json_build_object('hash',value_hash,'value',value_new)) from (
53
65
  select
54
66
  entity_key,
55
67
  value_hash,
68
+ value_new,
56
69
  ( rank() over (partition by entity_key order by cdate desc) = 1 ) as is_latest
57
70
  from log.table_changes_data
58
71
 
@@ -74,33 +87,70 @@ export default async function logChanges({ pg, table: table1, tokenData, referer
74
87
  const body = await getTemplate("form", tokenData?.form);
75
88
  const schema = body?.schema || body || {};
76
89
  const titles = Object.keys(schema).reduce((acc, curr) => Object.assign(acc, { [curr]: schema[curr].title || schema[curr].ua }), {});
90
+ Object.assign(titles, defaultTitles);
77
91
  const cls = Object.keys(schema)
78
92
  .filter((el) => schema[el]?.data)
79
93
  .reduce((acc, curr) => Object.assign(acc, { [curr]: schema[curr].data }), {});
80
- const data1 = data
81
- ? await metaFormat({
82
- rows: [data],
83
- cls,
84
- sufix: false,
85
- reassign: false,
86
- }, pg)
87
- : null;
88
- const newObj = Object.fromEntries(Object.entries(data1?.[0] || {}).map((el) => [
89
- [titles[el[0]] || defaultTitles[el[0]] || el[0]],
90
- el[1],
91
- ]));
92
- const changesData = Object.keys(newObj || {})
93
- .map((el) => ({
94
+ // const original = JSON.parse(JSON.stringify(data));
95
+ const row = await metaFormat({
96
+ rows: [data].filter(Boolean),
97
+ cls,
98
+ sufix: false,
99
+ reassign: false,
100
+ }, pg).then((el) => el[0]);
101
+ const newObj1 = {};
102
+ const meta = await getMeta({ pg, table });
103
+ const columnTypes = (meta?.columns || []).reduce((acc, curr) => ({
104
+ ...acc,
105
+ [curr.name]: pg?.pgType?.[curr.dataTypeID],
106
+ }), {});
107
+ if (type !== "DELETE") {
108
+ Object.keys(row || {})
109
+ .filter((key) => !systemColumns.includes(key))
110
+ .reduce((acc, curr) => {
111
+ Object.assign(newObj1, {
112
+ [titles[curr] || curr]: columnTypes[curr] === "geometry"
113
+ ? {}
114
+ : {
115
+ value: getValue(row[curr], table, columnTypes),
116
+ hash: typeof row[curr] === "boolean" || row[curr]
117
+ ? createHash("md5")
118
+ .update(JSON.stringify(row[curr]))
119
+ .digest("hex")
120
+ : null,
121
+ },
122
+ });
123
+ return acc;
124
+ });
125
+ }
126
+ const newGeoms = await Promise.all(Object.keys(newObj1)
127
+ .filter((key) => row[key] && columnTypes[key] === "geometry")
128
+ .map(async (key) => pg
129
+ .query(typeof row[key] === "string" // binary geometry as string via update?
130
+ ? "select st_asgeojson(st_pointonsurface($1))::json"
131
+ : "select st_asgeojson(st_pointonsurface(st_geomfromgeojson($1)))::json", [row[key]])
132
+ .then((el) => ({ key, geometry: el.rows[0].st_asgeojson })))).then((r) => r.reduce((acc, curr) => {
133
+ const value = curr.geometry.coordinates.join(",");
134
+ const hash = createHash("md5")
135
+ .update(JSON.stringify(value))
136
+ .digest("hex");
137
+ newObj1[curr.key] = { value, hash };
138
+ return { ...acc, [curr.key]: { value, hash } };
139
+ }, {}));
140
+ const changesData = Object.keys(newObj1 || {})
141
+ .filter((key) => typeof newObj1?.[key] === "boolean" || newObj1?.[key])
142
+ .filter((key) => old[key] ? old[key].hash !== newObj1[key]?.hash : newObj1[key]?.hash)
143
+ .map((key) => ({
94
144
  change_id: changeId,
95
- entity_key: el,
96
- value_old: getValue(old?.[el], table),
97
- value_new: type === "DELETE" ? null : getValue(newObj?.[el], table),
98
- value_hash: newObj?.[el]
99
- ? createHash("md5").update(JSON.stringify(newObj?.[el])).digest("hex")
100
- : null,
145
+ entity_key: key,
146
+ value_old: old?.[key]?.value,
147
+ value_new: newObj1?.[key]?.value,
148
+ value_hash: newObj1?.[key]?.hash,
101
149
  uid,
102
- }))
103
- .filter((el) => old?.[el.entity_key] !== el.value_hash);
150
+ }));
151
+ if (process.env.NODE_ENV === "test") {
152
+ console.log("changesData", changesData);
153
+ }
104
154
  const res = await Promise.all(changesData.map(async (el) => {
105
155
  const insertQuery = `insert into log.table_changes_data (${Object.entries(el)
106
156
  ?.map((key) => `"${key[0]}"`)
@@ -108,18 +158,18 @@ export default async function logChanges({ pg, table: table1, tokenData, referer
108
158
  values (${Object.entries(el)
109
159
  ?.map((key, i) => `$${i + 1}`)
110
160
  .join(",")}) returning *`;
111
- const { rows = [] } = (await pg.query(insertQuery, [
161
+ const row1 = await pg
162
+ .query(insertQuery, [
112
163
  ...Object.entries(el).map((el1) => el1[1] &&
113
164
  typeof el1[1] === "object" &&
114
165
  (!Array.isArray(el1[1]) || typeof el1[1]?.[0] === "object")
115
166
  ? JSON.stringify(el1[1])
116
167
  : el1[1]),
117
- ])) || {};
118
- return rows[0];
168
+ ])
169
+ .then((el) => el.rows?.[0] || {});
170
+ return row1;
119
171
  }));
120
- const newData = type === "DELETE"
121
- ? {}
122
- : (Array.isArray(res) ? res : [res]).reduce((acc, curr) => Object.assign(acc, { [curr.entity_key]: curr.value_new }), {});
172
+ const newData = (Array.isArray(res) ? res : [res]).reduce((acc, curr) => Object.assign(acc, { [curr.entity_key]: curr.value_new }), {});
123
173
  // console.log('logChanges OK', type);
124
174
  return {
125
175
  change_id: changeId,
@@ -129,6 +179,7 @@ export default async function logChanges({ pg, table: table1, tokenData, referer
129
179
  change_type: type,
130
180
  old,
131
181
  new: newData,
182
+ titles,
132
183
  };
133
184
  }
134
185
  catch (err) {
@@ -2,5 +2,5 @@
2
2
  * Дістає CRM для widget
3
3
  *
4
4
  */
5
- export default function widgetGet({ pg, user, params, query, unittest }: any, reply: any): Promise<any>;
5
+ export default function widgetGet({ pg, user, params, query }: any, reply: any): Promise<any>;
6
6
  //# sourceMappingURL=widget.get.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"widget.get.d.ts","sourceRoot":"","sources":["../../../../../server/routes/widget/controllers/widget.get.ts"],"names":[],"mappings":"AAyBA;;;GAGG;AAEH,wBAA8B,SAAS,CACrC,EAAE,EAAqB,EAAE,IAAS,EAAE,MAAW,EAAE,KAAU,EAAE,QAAQ,EAAE,EAAE,GAAG,EAC5E,KAAK,EAAE,GAAG,gBAkLX"}
1
+ {"version":3,"file":"widget.get.d.ts","sourceRoot":"","sources":["../../../../../server/routes/widget/controllers/widget.get.ts"],"names":[],"mappings":"AAyBA;;;GAGG;AAEH,wBAA8B,SAAS,CACrC,EAAE,EAAqB,EAAE,IAAS,EAAE,MAAW,EAAE,KAAU,EAAE,EAAE,GAAG,EAClE,KAAK,EAAE,GAAG,gBA+KX"}
@@ -23,7 +23,8 @@ const pkList = {
23
23
  * Дістає CRM для widget
24
24
  *
25
25
  */
26
- export default async function widgetGet({ pg = pgClients.client, user = {}, params = {}, query = {}, unittest }, reply) {
26
+ export default async function widgetGet({ pg = pgClients.client, user = {}, params = {}, query = {} }, reply) {
27
+ const time = [Date.now()];
27
28
  const param = user?.uid
28
29
  ? await getToken({
29
30
  token: params.objectid,
@@ -43,8 +44,7 @@ export default async function widgetGet({ pg = pgClients.client, user = {}, para
43
44
  left join admin.users u on u.uid=c.uid
44
45
  where entity_id=$1 order by cdate desc`
45
46
  : "select communication_id, entity_id, body, subject, cdate, uid from crm.communications where entity_id=$1 order by cdate desc",
46
- history: `select * from (
47
- SELECT change_id, entity_id, entity_type, change_type, change_date, a.change_user_id, a.uid, a.cdate, b.json_agg as changes,
47
+ history: `SELECT change_id, entity_id, entity_type, change_type, change_date, a.change_user_id, a.uid, a.cdate, b.json_agg as changes,
48
48
  ${username} as username, u.login, u.avatar
49
49
  FROM log.table_changes a
50
50
  left join admin.users u on a.change_user_id = u.uid
@@ -54,26 +54,12 @@ export default async function widgetGet({ pg = pgClients.client, user = {}, para
54
54
  where change_id=a.change_id
55
55
  )q
56
56
  )b on 1=1
57
- where b.json_agg is not null and (entity_id=$1 or entity_id in (
58
- select communication_id as comments from crm.communications where entity_id=$1
59
- union all select checklist_id from crm.checklists where entity_id=$1)
57
+ where a.change_type != 'INSERT' and b.json_agg is not null and (entity_id=$1 or entity_id in (
58
+ select communication_id from crm.communications where entity_id=$1
59
+ union all select checklist_id from crm.checklists where entity_id=$1
60
+ union all select file_id from crm.files where entity_id=$1)
60
61
  )
61
-
62
- union all
63
- select change_id, entity_id, entity_type, change_type, change_date, a.change_user_id, a.uid, a.cdate, b.json_agg as changes,
64
- ${username} as username, u.login, u.avatar
65
- FROM log.table_changes a
66
- left join admin.users u on a.change_user_id = u.uid
67
- left join lateral(
68
- select json_agg(o) from (
69
- select json_object_agg(entity_key, coalesce(value_new, value_old)) as o from log.table_changes_data
70
- where change_id=a.change_id and entity_key not in ('uid', 'file_status', 'editor_id', 'cdate', 'editor_date', 'entity_id')
71
- )q
72
- )b on 1=1
73
- where a.change_type in ('INSERT', 'DELETE') and a.entity_id in (select file_id from crm.files where entity_id=$1)
74
- limit 100
75
-
76
- )q order by cdate desc limit 100`,
62
+ order by cdate desc limit 100`,
77
63
  checklist: pg.pk["admin.users"]
78
64
  ? `SELECT checklist_id, entity_id, subject, is_done, done_date, c.uid, c.cdate, ${username} as username, u.login, u.avatar
79
65
  FROM crm.checklists c
@@ -102,9 +88,11 @@ export default async function widgetGet({ pg = pgClients.client, user = {}, para
102
88
  if (!q) {
103
89
  return reply.status(400).send("invalid widget type");
104
90
  }
105
- /* data */
106
- const t1 = Date.now();
107
- const { rows = [] } = await pg.query(q, [objectid, params.type === "gallery" ? galleryExtList : null].filter((el) => el));
91
+ /* rows */
92
+ const rows = await pg
93
+ .query(q, [objectid, params.type === "gallery" ? galleryExtList : null].filter(Boolean))
94
+ .then((el) => el.rows || []);
95
+ time.push(Date.now());
108
96
  rows.forEach((row) => Object.assign(row, { username: row.username?.trim?.() || row.login }));
109
97
  /* reactions */
110
98
  const widgetPkey = pkList[params.type];
@@ -121,26 +109,32 @@ export default async function widgetGet({ pg = pgClients.client, user = {}, para
121
109
  });
122
110
  }
123
111
  /* Object info */
124
- const { tableName } = pg.pk["log.table_changes"]
112
+ const tableName = pg.pk["log.table_changes"]
125
113
  ? await pg
126
- .query('select entity_type as "tableName" from log.table_changes where entity_id=$1 limit 1', [objectid])
127
- .then((el) => el.rows?.[0] || {})
114
+ .query("select entity_type from log.table_changes where entity_id=$1 limit 1", [objectid])
115
+ .then((el) => el.rows?.[0]?.entity_type)
116
+ : null;
117
+ const { pk, columns = [] } = tableName
118
+ ? await getMeta({ pg, table: tableName })
128
119
  : {};
129
- const { pk, columns = [] } = await getMeta({ pg, table: tableName });
130
120
  const authorIdColumn = columns.find((col) => ["uid", "created_by"].includes(col.name))?.name;
131
- if (!pk && params.type === "history" && !unittest) {
121
+ if (!pk &&
122
+ params.type === "history" &&
123
+ process.env.NODE_ENV !== "test" &&
124
+ !process.env.VITEST) {
132
125
  return reply.status(404).send("log table not found");
133
126
  }
134
127
  const q1 = `select ${username} as author, u.login, a.cdate, a.editor_date from ${tableName} a
135
128
  left join admin.users u on a.${authorIdColumn}=u.uid where a.${pk}=$1 limit 1`;
136
129
  const data = pg.pk["admin.users"] && pk && tableName
137
130
  ? await pg
138
- .query(q, [
131
+ .query(q1, [
139
132
  objectid,
140
133
  params.type === "gallery" ? galleryExtList : null,
141
- ].filter((el) => el))
134
+ ].filter(Boolean))
142
135
  .then((el) => el.rows?.[0] || {})
143
136
  : {};
137
+ time.push(Date.now());
144
138
  if (query.debug && user?.user_type === "admin") {
145
139
  return {
146
140
  q,
@@ -151,7 +145,7 @@ export default async function widgetGet({ pg = pgClients.client, user = {}, para
151
145
  };
152
146
  }
153
147
  return reply.status(200).send({
154
- time: { data: Date.now() - t1 },
148
+ time: { rows: time[1] - time[0], data: time[2] - time[1] },
155
149
  rows,
156
150
  user: { uid: user?.uid, name: user?.user_name },
157
151
  data: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "2.0.148",
3
+ "version": "2.0.150",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [