@opengis/fastify-table 2.5.43 → 2.5.46

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 (89) 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/helper.d.ts +20 -0
  5. package/dist/helper.d.ts.map +1 -1
  6. package/dist/helper.js +26 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +5 -2
  9. package/dist/server/migrations/dictionaries.sql +79 -0
  10. package/dist/server/plugins/access/funcs/getResources.d.ts +2 -1
  11. package/dist/server/plugins/access/funcs/getResources.d.ts.map +1 -1
  12. package/dist/server/plugins/access/funcs/getResources.js +5 -1
  13. package/dist/server/plugins/crud/funcs/dataDelete.d.ts +2 -1
  14. package/dist/server/plugins/crud/funcs/dataDelete.d.ts.map +1 -1
  15. package/dist/server/plugins/crud/funcs/dataDelete.js +2 -2
  16. package/dist/server/plugins/crud/funcs/getOpt.d.ts +1 -1
  17. package/dist/server/plugins/crud/funcs/getOpt.d.ts.map +1 -1
  18. package/dist/server/plugins/crud/funcs/getOpt.js +1 -1
  19. package/dist/server/plugins/crud/funcs/setOpt.d.ts +1 -1
  20. package/dist/server/plugins/crud/funcs/setOpt.d.ts.map +1 -1
  21. package/dist/server/plugins/crud/funcs/setOpt.js +1 -1
  22. package/dist/server/plugins/crud/funcs/setToken.d.ts +1 -1
  23. package/dist/server/plugins/crud/funcs/setToken.d.ts.map +1 -1
  24. package/dist/server/plugins/table/funcs/getData.d.ts +32 -0
  25. package/dist/server/plugins/table/funcs/getData.d.ts.map +1 -0
  26. package/dist/server/plugins/table/funcs/getData.js +703 -0
  27. package/dist/server/plugins/table/funcs/getFilterSQLParametrized/index.d.ts +2 -1
  28. package/dist/server/plugins/table/funcs/getFilterSQLParametrized/index.d.ts.map +1 -1
  29. package/dist/server/plugins/table/funcs/getFilterSQLParametrized/index.js +2 -2
  30. package/dist/server/routes/crud/controllers/insert.d.ts +1 -2
  31. package/dist/server/routes/crud/controllers/insert.d.ts.map +1 -1
  32. package/dist/server/routes/crud/controllers/insert.js +1 -1
  33. package/dist/server/routes/crud/controllers/table.d.ts +1 -2
  34. package/dist/server/routes/crud/controllers/table.d.ts.map +1 -1
  35. package/dist/server/routes/crud/controllers/table.js +1 -1
  36. package/dist/server/routes/crud/controllers/update.js +1 -1
  37. package/dist/server/routes/crud/crud.route.d.ts +3 -0
  38. package/dist/server/routes/crud/crud.route.d.ts.map +1 -0
  39. package/dist/server/routes/crud/crud.route.js +161 -0
  40. package/dist/server/routes/crud/crud.schema.d.ts +114 -0
  41. package/dist/server/routes/crud/crud.schema.d.ts.map +1 -0
  42. package/dist/server/routes/crud/crud.schema.js +64 -0
  43. package/dist/server/routes/crud/functions/createTableRelation.d.ts +13 -0
  44. package/dist/server/routes/crud/functions/createTableRelation.d.ts.map +1 -0
  45. package/dist/server/routes/crud/functions/createTableRelation.js +93 -0
  46. package/dist/server/routes/crud/functions/deleteTableRelation.d.ts +11 -0
  47. package/dist/server/routes/crud/functions/deleteTableRelation.d.ts.map +1 -0
  48. package/dist/server/routes/crud/functions/deleteTableRelation.js +74 -0
  49. package/dist/server/routes/crud/functions/readTableRelations.d.ts +25 -0
  50. package/dist/server/routes/crud/functions/readTableRelations.d.ts.map +1 -0
  51. package/dist/server/routes/crud/functions/readTableRelations.js +76 -0
  52. package/dist/server/routes/crud/functions/updateTableRelation.d.ts +14 -0
  53. package/dist/server/routes/crud/functions/updateTableRelation.d.ts.map +1 -0
  54. package/dist/server/routes/crud/functions/updateTableRelation.js +117 -0
  55. package/dist/server/routes/dictionaries/dictionaries.route.d.ts +2 -0
  56. package/dist/server/routes/dictionaries/dictionaries.route.d.ts.map +1 -0
  57. package/dist/server/routes/dictionaries/dictionaries.route.js +134 -0
  58. package/dist/server/routes/dictionaries/dictionaries.schema.d.ts +190 -0
  59. package/dist/server/routes/dictionaries/dictionaries.schema.d.ts.map +1 -0
  60. package/dist/server/routes/dictionaries/dictionaries.schema.js +86 -0
  61. package/dist/server/routes/dictionaries/functions/create.dictionary.d.ts +10 -0
  62. package/dist/server/routes/dictionaries/functions/create.dictionary.d.ts.map +1 -0
  63. package/dist/server/routes/dictionaries/functions/create.dictionary.item.d.ts +13 -0
  64. package/dist/server/routes/dictionaries/functions/create.dictionary.item.d.ts.map +1 -0
  65. package/dist/server/routes/dictionaries/functions/create.dictionary.item.js +57 -0
  66. package/dist/server/routes/dictionaries/functions/create.dictionary.js +76 -0
  67. package/dist/server/routes/dictionaries/functions/delete.dictionary.d.ts +9 -0
  68. package/dist/server/routes/dictionaries/functions/delete.dictionary.d.ts.map +1 -0
  69. package/dist/server/routes/dictionaries/functions/delete.dictionary.item.d.ts +11 -0
  70. package/dist/server/routes/dictionaries/functions/delete.dictionary.item.d.ts.map +1 -0
  71. package/dist/server/routes/dictionaries/functions/delete.dictionary.item.js +32 -0
  72. package/dist/server/routes/dictionaries/functions/delete.dictionary.js +28 -0
  73. package/dist/server/routes/dictionaries/functions/read.dictionaries.d.ts +5 -0
  74. package/dist/server/routes/dictionaries/functions/read.dictionaries.d.ts.map +1 -0
  75. package/dist/server/routes/dictionaries/functions/read.dictionaries.js +27 -0
  76. package/dist/server/routes/dictionaries/functions/update.dictionary.d.ts +10 -0
  77. package/dist/server/routes/dictionaries/functions/update.dictionary.d.ts.map +1 -0
  78. package/dist/server/routes/dictionaries/functions/update.dictionary.item.d.ts +13 -0
  79. package/dist/server/routes/dictionaries/functions/update.dictionary.item.d.ts.map +1 -0
  80. package/dist/server/routes/dictionaries/functions/update.dictionary.item.js +48 -0
  81. package/dist/server/routes/dictionaries/functions/update.dictionary.js +51 -0
  82. package/dist/server/routes/file/schema.js +6 -6
  83. package/dist/server/routes/table/functions/getData.d.ts +3 -2
  84. package/dist/server/routes/table/functions/getData.d.ts.map +1 -1
  85. package/dist/server/routes/table/functions/getData.js +2 -14
  86. package/dist/server/routes/table/schema.d.ts +5 -4
  87. package/dist/server/routes/table/schema.d.ts.map +1 -1
  88. package/dist/server/routes/table/schema.js +13 -13
  89. package/package.json +1 -1
@@ -0,0 +1,703 @@
1
+ import config from "../../../../config.js";
2
+ import { handlebars, handlebarsSync } from "../../../helpers/index.js";
3
+ import getAccess from "../../crud/funcs/getAccess.js";
4
+ import setToken from "../../crud/funcs/setToken.js";
5
+ import getToken from "../../crud/funcs/getToken.js";
6
+ import gisIRColumn from "./gisIRColumn.js";
7
+ import applyHook from "../../hook/applyHook.js";
8
+ import getSelect from "./getSelect.js";
9
+ import setOpt from "../../crud/funcs/setOpt.js";
10
+ import getOpt from "../../crud/funcs/getOpt.js";
11
+ import getFilter from "./getFilter.js";
12
+ import logger from "../../logger/getLogger.js";
13
+ import getTemplate from "./getTemplate.js";
14
+ import getMeta from "../../pg/funcs/getMeta.js";
15
+ import pgClients from "../../pg/pgClients.js";
16
+ import metaFormat from "./metaFormat/index.js";
17
+ import getFilterSQLParametrized from "./getFilterSQLParametrized/index.js";
18
+ import extraDataGet from "../../extra/extraDataGet.js";
19
+ import locales from "../../../routes/table/controllers/utils/locales.js";
20
+ import conditions from "../../../routes/table/controllers/utils/conditions.js";
21
+ import prettify from "../../../routes/table/functions/prettify.js";
22
+ import { resources } from "../../access/funcs/getResources.js";
23
+ import { BadRequestError, ForbiddenError, NotFoundError, UnauthorizedError, } from "../../../../errors.js";
24
+ const components = {
25
+ "vs-widget-file": "select 'vs-widget-file' as component, count(*) from crm.files where entity_id=$1 and file_status<>3",
26
+ "vs-widget-comments": "select 'vs-widget-comments' as component, count(*) from crm.communications where entity_id=$1",
27
+ };
28
+ function getOrder({ queryOrder, queryDesc, defaultOrder, columnList, cols, sqlColumns, iscalled = false, }) {
29
+ if (iscalled && queryOrder) {
30
+ return `order by ${queryOrder}`;
31
+ }
32
+ if (!queryOrder) {
33
+ return `order by ${defaultOrder || "true::boolean"} nulls last`;
34
+ }
35
+ const orderArr = queryOrder.split(/[|]/g).map((el) => el.split(/[-]/));
36
+ const validOrder = orderArr
37
+ .filter((el) => el[0] &&
38
+ (columnList || "")
39
+ .concat(sqlColumns || "")
40
+ .concat(cols || "")
41
+ .includes(el[0]))
42
+ .map((el) => `${el[0]} ${queryDesc || el[1] === "desc" ? "desc" : ""}`)
43
+ .join(",");
44
+ return `order by ${validOrder || "true::boolean"} nulls last`;
45
+ }
46
+ const checkInline = {};
47
+ const maxLimit = 100;
48
+ const defaultLimit = 20;
49
+ export default async function dataAPI({ table, id, page, limit: queryLimit, sql, filter: queryFilter, search: querySearch, state, custom, order: queryOrder, desc: queryDesc, bbox: bboxParam, polyline: polylineParam, query: queryParam, omitGeometry: omitGeometryParam = false, uid, referer, public: ispublicParam, userType, contextQuery, searchColumn, sufix = true, filterList, actions: actionsParam, accessQuery: accessQueryParam, columns: columnsParam, isExport = false, called = false, args: paramsArgs = [], }, pg = pgClients.client) {
50
+ const time = Date.now();
51
+ const timeArr = [Date.now()];
52
+ if (!table) {
53
+ throw BadRequestError("not enough params: table");
54
+ }
55
+ const hookData = (await applyHook("preData", {
56
+ pg,
57
+ table,
58
+ id,
59
+ user: { uid, user_type: userType },
60
+ }));
61
+ // hooks should throw custom errors instead, this is temp solution to avoid breaking changes in hooks
62
+ if (hookData?.message && hookData?.status) {
63
+ if (hookData.status >= 400) {
64
+ throw new Error(hookData.message);
65
+ }
66
+ return hookData.message;
67
+ }
68
+ /* from tableData START */
69
+ const tokenData1 = await getOpt(table, uid);
70
+ const tokenData2 = await getToken({
71
+ uid,
72
+ token: table,
73
+ json: true,
74
+ });
75
+ const tokenData = tokenData1 || tokenData2;
76
+ const resource = resources[table] ? table : undefined;
77
+ const templateName = resources[table] || tokenData?.table || hookData?.table || table;
78
+ const loadTable = await getTemplate("table", templateName);
79
+ if (tokenData?.table && !loadTable) {
80
+ const { rows = [], fields = [] } = await pg.query(`select ${tokenData.columns || "*"} from ${tokenData.table} where ${tokenData.query || "true"} limit 10`);
81
+ const meta = await getMeta({ pg, table: tokenData.table });
82
+ const columns = (meta?.columns || fields).map((el) => ({
83
+ name: el.name,
84
+ type: pg.pgType?.[el.dataTypeID],
85
+ title: el.title,
86
+ }));
87
+ return { rows, columns };
88
+ }
89
+ /* from tableData END */
90
+ // check sql inline fields count
91
+ if (!checkInline[table] && loadTable?.sql?.length && loadTable.table) {
92
+ const filterSql = loadTable.sql.filter((el) => !el?.disabled && (el.inline ?? true));
93
+ const sqlTable = filterSql
94
+ .map((el, i) => ` left join lateral (${el.sql}) ${el.name || `t${i}`} on 1=1 `)
95
+ ?.join("") || "";
96
+ const d = pg
97
+ ? await Promise.all(filterSql.map((el, i) => pg
98
+ .query(`select ${el.name || `t${i}`}.* from(select * from ${loadTable.table})t ${sqlTable} limit 0`)
99
+ .then((item) => item.fields)))
100
+ : [];
101
+ d.forEach((el, i) => {
102
+ filterSql[i].inline = el.length === 1;
103
+ filterSql[i].fields = el.map((f) => f.name);
104
+ });
105
+ checkInline[table] = loadTable.sql;
106
+ }
107
+ else if (checkInline[table]) {
108
+ loadTable.sql = checkInline[table];
109
+ }
110
+ if (sql === 0 && process.platform === "win32")
111
+ return loadTable;
112
+ if (!pg) {
113
+ throw Error("empty pg");
114
+ }
115
+ const pkey = pg.pk?.[resources[table] || table] ||
116
+ pg.pk?.[resources[table] || table.replace(/"/g, "")] ||
117
+ (await getMeta({
118
+ pg,
119
+ table: resources[table] || loadTable?.table || table,
120
+ }))?.pk;
121
+ if (!loadTable &&
122
+ !(tokenData?.table && pg.pk?.[tokenData?.table]) &&
123
+ !(called && pkey)) {
124
+ throw NotFoundError("template not found");
125
+ }
126
+ const objectId = tokenData?.id || hookData?.id || id;
127
+ const { actions = [], query: accessQuery, message: accessMessage, } = actionsParam
128
+ ? {
129
+ actions: actionsParam,
130
+ query: accessQueryParam || "1=1",
131
+ message: undefined,
132
+ }
133
+ : (await getAccess({
134
+ resource,
135
+ table: templateName,
136
+ id: objectId,
137
+ user: { uid, userType },
138
+ method: "GET",
139
+ }, pg)) || {};
140
+ if (accessMessage) {
141
+ throw ForbiddenError(accessMessage);
142
+ }
143
+ const { table: table1, columns = [], sql: sqlQueryList, cardSql, form, meta, sqlColumns, editable = false, } = loadTable || hookData || tokenData || { table };
144
+ const ispublic = loadTable?.public ||
145
+ hookData?.public ||
146
+ tokenData?.public ||
147
+ ispublicParam ||
148
+ false;
149
+ if (!ispublic && !uid && !called && !accessQueryParam && !actionsParam) {
150
+ throw UnauthorizedError("unauthorized");
151
+ }
152
+ if (!ispublic && !actions.includes("view") && !config?.local && !called) {
153
+ throw ForbiddenError("access restricted");
154
+ }
155
+ const { list: filters = [] } = objectId
156
+ ? {}
157
+ : (await getFilter({
158
+ pg,
159
+ table: templateName,
160
+ user: { uid, user_type: userType },
161
+ }, null)) || {};
162
+ const tableMeta = await getMeta({ pg, table: table1 });
163
+ const viewSql = await getTemplate("view", table1);
164
+ timeArr.push(Date.now());
165
+ if (tableMeta?.view) {
166
+ if (!loadTable?.key && !tokenData?.key) {
167
+ throw NotFoundError(`key not found: ${table1}`);
168
+ }
169
+ Object.assign(tableMeta, { pk: loadTable?.key || tokenData?.key });
170
+ }
171
+ const { pk, columns: dbColumns = [] } = (viewSql
172
+ ? { pk: loadTable?.key, columns: loadTable?.columns }
173
+ : tableMeta) || {};
174
+ const columns1 = columns ||
175
+ dbColumns.map(({ name, title, dataTypeID }) => ({
176
+ name,
177
+ title,
178
+ type: pg.pgType?.[dataTypeID],
179
+ }));
180
+ columns1.forEach((col) => {
181
+ Object.assign(col, locales[`${table1 || table}.${col.name}`] || {});
182
+ });
183
+ if (!pk) {
184
+ throw NotFoundError(`table not found: ${table1}`);
185
+ }
186
+ const idx = {};
187
+ const columnList = dbColumns.map((el) => el.name || el).join(",");
188
+ const sqlTable = sqlQueryList
189
+ ?.filter?.((el) => !el?.disabled &&
190
+ !el.inline &&
191
+ el?.sql?.replace &&
192
+ !(isExport && el.export === false))
193
+ .map((el, i) => ` left join lateral (${el.sql}) ${el.name || `t${i}`} on 1=1 `)
194
+ ?.join("") || "";
195
+ const cardSqlFiltered = objectId
196
+ ? cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) || []
197
+ : [];
198
+ const cardSqlTable = cardSqlFiltered.length
199
+ ? cardSqlFiltered
200
+ .map((el, i) => ` left join lateral (${el.sql}) ct${i} on 1=1 `)
201
+ .join("\n") || ""
202
+ : "";
203
+ const sqlInline = loadTable?.sql
204
+ ?.filter?.((el) => el.inline && !(isExport && el.export === false))
205
+ ?.map((el) => `,(${el.sql})`)
206
+ ?.join("") || "";
207
+ const fields = !viewSql
208
+ ? await pg
209
+ .query(`select * ${sqlInline} from ${table1} t ${sqlTable} ${cardSqlTable} limit 0`)
210
+ .then((r) => r.fields || [])
211
+ : await pg
212
+ .query(`select * from (${viewSql})q limit 0`)
213
+ .then((r) => r.fields || []);
214
+ const dbColumnsTable = fields.map((el) => el.name);
215
+ const cols = columns
216
+ .filter((el) => el.name !== "geom" && dbColumnsTable.includes(el.name))
217
+ .map((el) => `"${el.name || el}"`)
218
+ .join(",");
219
+ const metaCols = Object.keys(loadTable?.meta?.cls || {}).filter((el) => !cols.includes(el)).length
220
+ ? `,${Object.keys(loadTable?.meta?.cls || {})
221
+ ?.filter((el) => !cols.includes(el))
222
+ .join(",")}`
223
+ : "";
224
+ if (objectId && columnList.includes(objectId)) {
225
+ return gisIRColumn({
226
+ pg,
227
+ layer: resources[table] || table,
228
+ column: objectId,
229
+ sql,
230
+ filter: queryFilter,
231
+ search: querySearch,
232
+ state,
233
+ custom,
234
+ });
235
+ }
236
+ const isdefault = !objectId ? filters.find((el) => el.default) : null;
237
+ const checkFilter = [
238
+ queryFilter,
239
+ querySearch,
240
+ state,
241
+ custom,
242
+ isdefault,
243
+ ].filter(Boolean).length;
244
+ const fData = checkFilter
245
+ ? await getFilterSQLParametrized({
246
+ pg,
247
+ table: loadTable
248
+ ? tokenData?.table || resources[table] || table
249
+ : table1,
250
+ filter: queryFilter,
251
+ search: querySearch,
252
+ searchColumn,
253
+ state,
254
+ custom,
255
+ uid,
256
+ objectId,
257
+ filterList,
258
+ args: paramsArgs,
259
+ })
260
+ : {
261
+ args: paramsArgs,
262
+ };
263
+ timeArr.push(Date.now());
264
+ const args = Array.from(fData.args || []);
265
+ if (objectId) {
266
+ args.push(objectId);
267
+ Object.assign(idx, { id: args.length });
268
+ }
269
+ const limit = (called
270
+ ? queryLimit || defaultLimit
271
+ : Math.min(maxLimit, +(queryLimit || defaultLimit))) || defaultLimit;
272
+ args.push(limit);
273
+ Object.assign(idx, { limit: args.length });
274
+ const offset = page && page > 0 && !objectId ? (page - 1) * limit : 0;
275
+ args.push(offset);
276
+ Object.assign(idx, { offset: args.length });
277
+ const order = getOrder({
278
+ queryOrder,
279
+ queryDesc,
280
+ defaultOrder: loadTable?.order,
281
+ columnList,
282
+ cols,
283
+ sqlColumns: loadTable?.sqlColumns,
284
+ iscalled: !!called,
285
+ });
286
+ const isSearchable = loadTable?.meta?.search &&
287
+ typeof loadTable.meta.search === "string" &&
288
+ querySearch &&
289
+ !idx.id;
290
+ if (isSearchable) {
291
+ args.push(`%${querySearch?.replace(/%/g, "\\%")}%`);
292
+ Object.assign(idx, { search: args.length });
293
+ }
294
+ if (meta?.bbox && polylineParam) {
295
+ args.push(polylineParam);
296
+ Object.assign(idx, {
297
+ polyline: args.length,
298
+ });
299
+ }
300
+ if (config.trace)
301
+ console.log(order);
302
+ const search = isSearchable
303
+ ? `(${loadTable.meta.search
304
+ .split(",")
305
+ .map((el) => `${el} ilike $${idx.search}`)
306
+ .join(" or ")})`
307
+ : null;
308
+ const queryBbox = bboxParam
309
+ ? bboxParam
310
+ .replace(/ /g, ",")
311
+ .split(",")
312
+ ?.map((el) => el - 0)
313
+ : [];
314
+ const bbox = meta?.bbox && queryBbox.filter((el) => !Number.isNaN(el))?.length === 4
315
+ ? `${meta.bbox} && 'box(${queryBbox[0]} ${queryBbox[1]},${queryBbox[2]} ${queryBbox[3]})'::box2d `
316
+ : undefined;
317
+ const queryPolyline = meta?.bbox && polylineParam
318
+ ? `ST_Contains(ST_MakePolygon(ST_LineFromEncodedPolyline($${idx.polyline})),${meta.bbox})`
319
+ : undefined;
320
+ const interfaceName = referer?.match?.(/.*\/([^?]+)/)?.[1];
321
+ const interfaceQuery1 = interfaceName && pg?.pk
322
+ ? await pg
323
+ .query("select query from admin.routes where route_id=$1", [
324
+ interfaceName,
325
+ ])
326
+ .then((r) => r.rows?.[0]?.query)
327
+ : null;
328
+ const interfaceQuery = queryParam
329
+ ? await handlebars.compile(queryParam)({ user: { uid }, uid })
330
+ : undefined;
331
+ const where = [
332
+ objectId ? ` "${pk}" = $${idx.id}` : null,
333
+ loadTable?.query,
334
+ tokenData?.query,
335
+ fData?.q,
336
+ search,
337
+ accessQuery || "1=1",
338
+ contextQuery,
339
+ bbox,
340
+ queryPolyline,
341
+ interfaceQuery,
342
+ interfaceQuery1,
343
+ ].filter(Boolean);
344
+ const omitGeometry = omitGeometryParam || (!objectId && loadTable?.showGeometryMode === "card");
345
+ const q1 = `select ${pk ? `"${pk}" as id,` : ""}
346
+ ${objectId ? "*" : columnsParam || sqlColumns || cols || "*"}
347
+ ${metaCols}
348
+ ${dbColumns.filter((el) => pg.pgType?.[el.dataTypeID] === "geometry")
349
+ .length &&
350
+ !columnsParam &&
351
+ !omitGeometry
352
+ ? `,${dbColumns
353
+ .filter((el) => pg.pgType?.[el.dataTypeID] === "geometry")
354
+ .map((el) => `st_asgeojson("${el.name.replace(/'/g, "''")}")::json as "${el.name.replace(/'/g, "''")}"`)
355
+ .join(",")}`
356
+ : ""}
357
+ from (select * ${sqlQueryList
358
+ ?.filter((el) => el.inline && !(isExport && el.export === false))
359
+ .map((el) => `,(${el.sql})`)
360
+ .join("") || ""} from ${viewSql ? `(${viewSql})` : table1} t ${sqlTable} ) t
361
+
362
+ ${objectId ? cardSqlTable : ""}
363
+ where ${where.join(" and ") || "true"}
364
+ ${order} limit $${idx.limit}::bigint offset $${idx.offset}::bigint`;
365
+ if (q1.includes("{{uid}}")) {
366
+ args.push(uid || "");
367
+ Object.assign(idx, { uid: args.length });
368
+ }
369
+ const q = q1.replace(/'{{uid}}'/g, `$${idx.uid}`);
370
+ if (config.trace)
371
+ console.log(q);
372
+ if (sql === 1 && process.platform === "win32") {
373
+ return prettify(q, args);
374
+ }
375
+ const rows = await pg.query(q, args).then((r) => r.rows || []);
376
+ if (!rows.length &&
377
+ referer?.includes?.("/card/") &&
378
+ referer?.includes?.(templateName)) {
379
+ throw ForbiddenError("access restricted: empty rows");
380
+ }
381
+ timeArr.push(Date.now());
382
+ if (uid &&
383
+ rows.length &&
384
+ // !config.security?.disableToken &&
385
+ (editable ||
386
+ (tokenData?.actions || actionsParam)?.includes("edit") ||
387
+ actions.includes("edit") ||
388
+ actions.includes("del"))) {
389
+ rows.forEach((row) => {
390
+ Object.assign(row, {
391
+ token: setToken({
392
+ ids: [
393
+ JSON.stringify({
394
+ id: row.id,
395
+ table: templateName,
396
+ form: loadTable?.form,
397
+ resource,
398
+ obj: tokenData?.obj || hookData?.obj,
399
+ }),
400
+ ],
401
+ uid,
402
+ array: 1,
403
+ })?.[0],
404
+ });
405
+ });
406
+ }
407
+ const idx1 = {};
408
+ const args1 = Array.from(fData.args || []);
409
+ if (idx.search) {
410
+ args1.push(args[idx.search - 1]);
411
+ Object.assign(idx1, { search: args1.length });
412
+ }
413
+ if (idx.uid) {
414
+ args1.push(args[idx.uid - 1]);
415
+ Object.assign(idx1, { uid: args1.length });
416
+ }
417
+ if (idx.polyline) {
418
+ args1.push(args[idx.polyline - 1]);
419
+ Object.assign(idx1, { polyline: args1.length });
420
+ }
421
+ const search1 = idx.search
422
+ ? `(${loadTable.meta.search
423
+ .split(",")
424
+ .map((el) => `${el} ilike $${idx1.search}`)
425
+ .join(" or ")})`
426
+ : null;
427
+ const queryPolyline1 = idx.polyline
428
+ ? `ST_Contains(ST_MakePolygon(ST_LineFromEncodedPolyline($${idx1.polyline})),${meta.bbox})`
429
+ : undefined;
430
+ const filterWhere = [
431
+ fData.q,
432
+ search1,
433
+ bbox,
434
+ queryPolyline1,
435
+ interfaceQuery,
436
+ loadTable?.query,
437
+ tokenData?.query,
438
+ ].filter(Boolean);
439
+ const aggColumns = columns
440
+ .filter((el) => el.agg)
441
+ .reduce((acc, curr) => Object.assign(acc, { [curr.name]: curr.agg }), {});
442
+ const aggregates = dbColumns
443
+ .map((el) => ({
444
+ name: el.name,
445
+ type: pg.pgType?.[el.dataTypeID],
446
+ }))
447
+ .filter((el) => ["numeric", "double precision"].includes(el.type) && aggColumns[el.name]);
448
+ const qCount = `select
449
+ count(*)::int as total,
450
+ count(*) FILTER(WHERE ${filterWhere.join(" and ") || "true"})::int as filtered
451
+ ${aggregates.length
452
+ ? `,${aggregates
453
+ .map((el) => `${aggColumns[el.name]}(${el.name}) FILTER(WHERE ${filterWhere.join(" and ") || "true"}) as ${el.name}`)
454
+ .join(",")}`
455
+ : ""}
456
+ from (select * ${sqlQueryList
457
+ ?.filter((el) => el.inline)
458
+ .map((el) => `,(${el.sql})`)
459
+ .join("") || ""} from ${viewSql ? `(${viewSql})` : table1} t ${sqlTable})t
460
+ where ${[loadTable?.query, tokenData?.query, accessQuery, contextQuery]
461
+ .filter(Boolean)
462
+ .join(" and ") || "true"} `.replace(/'{{uid}}'/g, `$${idx1.uid}`);
463
+ if (sql === 2 && process.platform === "win32") {
464
+ return prettify(qCount, args1);
465
+ }
466
+ const counts = objectId
467
+ ? { total: rows.length, filtered: rows.length }
468
+ : await pg
469
+ .queryCache?.(qCount, {
470
+ args: args1,
471
+ table: loadTable?.table || tokenData?.table || table,
472
+ time: 5,
473
+ })
474
+ .then((r) => r.rows[0] || {});
475
+ timeArr.push(Date.now());
476
+ const { total, filtered } = counts || {};
477
+ const agg = Object.keys(counts)
478
+ .filter((el) => !["total", "filtered"].includes(el))
479
+ .reduce((acc, el) => ({ ...acc, [el]: counts[el] }), {});
480
+ await extraDataGet({ rows, table: loadTable?.table, form }, pg);
481
+ await metaFormat({ rows, table: templateName, sufix }, pg);
482
+ timeArr.push(Date.now());
483
+ const status = [];
484
+ if (loadTable?.meta?.status) {
485
+ const statusColumn = loadTable.meta?.cls?.[loadTable.meta?.status]
486
+ ? {
487
+ name: loadTable.meta?.status,
488
+ data: loadTable.meta?.cls?.[loadTable.meta?.status],
489
+ }
490
+ : loadTable.columns.find((col) => col.name === loadTable.meta?.status) || {};
491
+ const statusCls = statusColumn.data || statusColumn.option;
492
+ const statusClsData = statusCls ? await getSelect(statusCls, pg) : {};
493
+ statusClsData?.arr?.forEach((el) => status.push({
494
+ ...el,
495
+ count: rows.filter((row) => el.id === row[statusColumn.name])
496
+ .length,
497
+ }));
498
+ }
499
+ const template = await getTemplate("card", templateName);
500
+ const index = template?.find((el) => el[0] === "index.yml")?.[1] || {};
501
+ const html = {};
502
+ const { panels = [] } = index;
503
+ const tokens = {};
504
+ if (template && objectId) {
505
+ // tokens result
506
+ if (index?.tokens &&
507
+ typeof index?.tokens === "object" &&
508
+ !Array.isArray(index?.tokens)) {
509
+ Object.keys(index.tokens || {})
510
+ .filter((key) => index?.tokens[key]?.public ||
511
+ actions?.includes?.("edit") ||
512
+ actions?.includes?.("add") ||
513
+ userType === "viewer" ||
514
+ !index?.tokens[key]?.table)
515
+ .forEach((key) => {
516
+ const item = index?.tokens[key];
517
+ Object.keys(item)
518
+ .filter((el) => item[el]?.includes?.("{{"))
519
+ .forEach((el) => {
520
+ item[el] = handlebarsSync.compile(item[el])({
521
+ user: { uid },
522
+ uid,
523
+ id: objectId,
524
+ data: rows[0],
525
+ });
526
+ });
527
+ const token = item.form && item.table
528
+ ? setToken({
529
+ ids: [
530
+ JSON.stringify({
531
+ ...item,
532
+ resource,
533
+ }),
534
+ ],
535
+ uid,
536
+ array: 1,
537
+ })[0]
538
+ : setOpt(item, uid);
539
+ tokens[key] = token;
540
+ });
541
+ }
542
+ // console.log('token');
543
+ panels?.forEach?.((panel) => panel?.items
544
+ ?.filter?.((item) => item.token)
545
+ ?.forEach?.((el1) => Object.assign(el1, { token: tokens[el1.token] })));
546
+ // conditions
547
+ panels
548
+ ?.filter((el) => el.items)
549
+ .forEach((el) => {
550
+ Object.assign(el, {
551
+ items: el.items?.filter((item) => conditions(item.conditions, rows[0])),
552
+ });
553
+ });
554
+ // title, count
555
+ await Promise.all(panels
556
+ .filter((el) => el.items)
557
+ .map(async (el) => {
558
+ const filtered1 = el.items.filter((item) => item.count?.toLowerCase?.().includes("select"));
559
+ const data = await Promise.all(filtered1.map(async (item) => pg
560
+ .query(item.count.replace(/{{id}}/g, objectId))
561
+ .then((item1) => item1.rows[0] || {})));
562
+ filtered1.forEach((el1, i) => {
563
+ Object.assign(el1, data[i] || {}, data[i].count ? {} : { count: undefined });
564
+ });
565
+ const q1 = el.items
566
+ .map((item) => item.component ? components[item.component] : null)
567
+ .filter((item) => item)
568
+ .join(" union all ");
569
+ const counts1 = q1 && objectId
570
+ ? await pg
571
+ .query(q1, [objectId])
572
+ .then((e) => e.rows.reduce((acc, curr) => Object.assign(acc, { [curr.component]: curr.count }), {}))
573
+ : {};
574
+ el.items
575
+ ?.filter?.((item) => item.component)
576
+ ?.forEach((item) => Object.assign(item, { count: counts1?.[item.component] }));
577
+ }));
578
+ // data result
579
+ const data = {};
580
+ if (index?.data && index?.data?.[0]?.name) {
581
+ await Promise.all(index.data
582
+ .filter((el) => el?.name && el?.sql)
583
+ .map(async (el) => {
584
+ const q2 = handlebarsSync.compile(el.sql)({
585
+ data: rows[0],
586
+ user: { uid },
587
+ uid,
588
+ id: objectId,
589
+ });
590
+ const { rows: sqlData } = await pg.query(q2);
591
+ data[el.name] = sqlData;
592
+ }));
593
+ }
594
+ // html
595
+ await Promise.all(template
596
+ .filter((el) => typeof el[1] === "string" && el[0].includes(".hbs"))
597
+ .map(async (el) => {
598
+ try {
599
+ const htmlContent = await handlebars.compile(el[1])({
600
+ ...rows[0],
601
+ user: { uid },
602
+ data,
603
+ tokens,
604
+ actions,
605
+ });
606
+ const name = el[0].substring(0, el[0].lastIndexOf("."));
607
+ html[name] = htmlContent;
608
+ }
609
+ catch (err) {
610
+ const name = el[0].substring(0, el[0].lastIndexOf("."));
611
+ logger.file("handlebars/error", {
612
+ table,
613
+ id: objectId,
614
+ error: err.toString(),
615
+ stack: err.stack,
616
+ });
617
+ html[name] = "handlebars compile error";
618
+ }
619
+ }));
620
+ panels?.forEach?.((panel) => {
621
+ panel?.items?.forEach?.((el) => {
622
+ if (html[el.name]) {
623
+ Object.assign(el, { html: html[el.name] });
624
+ }
625
+ el.items?.forEach?.((el1) => {
626
+ Object.assign(el1, { html: html[el1.name] });
627
+ });
628
+ });
629
+ });
630
+ }
631
+ const route = pg.tlist?.includes?.("admin.routes")
632
+ ? await pg
633
+ .query("select route_id as path, title from admin.routes where enabled and alias=$1 limit 1", [resources[table] || table])
634
+ .then((r) => r.rows?.[0] || {})
635
+ : {};
636
+ const card = !!(loadTable?.card ?? (await getTemplate("card", templateName)));
637
+ const res = {
638
+ time: {
639
+ total: Date.now() - time,
640
+ init: timeArr[1] - timeArr[0],
641
+ filter: timeArr[2] - timeArr[1],
642
+ data: timeArr[3] - timeArr[2],
643
+ count: timeArr[4] - timeArr[3],
644
+ format: timeArr[5] - timeArr[4],
645
+ },
646
+ route,
647
+ resource,
648
+ public: ispublic,
649
+ tokens,
650
+ card,
651
+ actions: tokenData?.actions || actionsParam || actions || ["view"],
652
+ total,
653
+ filtered,
654
+ count: rows.length,
655
+ pk,
656
+ form,
657
+ agg,
658
+ status,
659
+ panels,
660
+ html,
661
+ rows,
662
+ meta,
663
+ columns: columns1,
664
+ filters: filters?.map?.((el) => ({
665
+ ...el,
666
+ sql: undefined,
667
+ label: el.label || el.ua,
668
+ })),
669
+ };
670
+ if (!res.columns?.length && dbColumns?.length) {
671
+ Object.assign(res, {
672
+ columns: dbColumns.map(({ name, title, dataTypeID }) => ({
673
+ name,
674
+ title,
675
+ type: pg.pgType?.[dataTypeID],
676
+ })),
677
+ });
678
+ }
679
+ if (uid && actions.includes("add") && !objectId) {
680
+ const addTokens = setToken({
681
+ ids: [
682
+ JSON.stringify({
683
+ resource,
684
+ table: templateName,
685
+ form: loadTable?.form,
686
+ obj: tokenData?.obj || hookData?.obj,
687
+ }),
688
+ ],
689
+ uid,
690
+ array: 1,
691
+ });
692
+ Object.assign(res, { addToken: addTokens?.[0] });
693
+ }
694
+ const result = await applyHook("afterData", {
695
+ pg,
696
+ table: loadTable?.table || tokenData?.table,
697
+ id: objectId,
698
+ template: templateName,
699
+ payload: res,
700
+ user: { uid, user_type: userType },
701
+ });
702
+ return result || res;
703
+ }