@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.
- package/dist/functions.d.ts +1 -0
- package/dist/functions.d.ts.map +1 -1
- package/dist/functions.js +122 -1
- package/dist/helper.d.ts +20 -0
- package/dist/helper.d.ts.map +1 -1
- package/dist/helper.js +26 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/server/migrations/dictionaries.sql +79 -0
- package/dist/server/plugins/access/funcs/getResources.d.ts +2 -1
- package/dist/server/plugins/access/funcs/getResources.d.ts.map +1 -1
- package/dist/server/plugins/access/funcs/getResources.js +5 -1
- package/dist/server/plugins/crud/funcs/dataDelete.d.ts +2 -1
- package/dist/server/plugins/crud/funcs/dataDelete.d.ts.map +1 -1
- package/dist/server/plugins/crud/funcs/dataDelete.js +2 -2
- package/dist/server/plugins/crud/funcs/getOpt.d.ts +1 -1
- package/dist/server/plugins/crud/funcs/getOpt.d.ts.map +1 -1
- package/dist/server/plugins/crud/funcs/getOpt.js +1 -1
- package/dist/server/plugins/crud/funcs/setOpt.d.ts +1 -1
- package/dist/server/plugins/crud/funcs/setOpt.d.ts.map +1 -1
- package/dist/server/plugins/crud/funcs/setOpt.js +1 -1
- package/dist/server/plugins/crud/funcs/setToken.d.ts +1 -1
- package/dist/server/plugins/crud/funcs/setToken.d.ts.map +1 -1
- package/dist/server/plugins/table/funcs/getData.d.ts +32 -0
- package/dist/server/plugins/table/funcs/getData.d.ts.map +1 -0
- package/dist/server/plugins/table/funcs/getData.js +703 -0
- package/dist/server/plugins/table/funcs/getFilterSQLParametrized/index.d.ts +2 -1
- package/dist/server/plugins/table/funcs/getFilterSQLParametrized/index.d.ts.map +1 -1
- package/dist/server/plugins/table/funcs/getFilterSQLParametrized/index.js +2 -2
- package/dist/server/routes/crud/controllers/insert.d.ts +1 -2
- package/dist/server/routes/crud/controllers/insert.d.ts.map +1 -1
- package/dist/server/routes/crud/controllers/insert.js +1 -1
- package/dist/server/routes/crud/controllers/table.d.ts +1 -2
- package/dist/server/routes/crud/controllers/table.d.ts.map +1 -1
- package/dist/server/routes/crud/controllers/table.js +1 -1
- package/dist/server/routes/crud/controllers/update.js +1 -1
- package/dist/server/routes/crud/crud.route.d.ts +3 -0
- package/dist/server/routes/crud/crud.route.d.ts.map +1 -0
- package/dist/server/routes/crud/crud.route.js +161 -0
- package/dist/server/routes/crud/crud.schema.d.ts +114 -0
- package/dist/server/routes/crud/crud.schema.d.ts.map +1 -0
- package/dist/server/routes/crud/crud.schema.js +64 -0
- package/dist/server/routes/crud/functions/createTableRelation.d.ts +13 -0
- package/dist/server/routes/crud/functions/createTableRelation.d.ts.map +1 -0
- package/dist/server/routes/crud/functions/createTableRelation.js +93 -0
- package/dist/server/routes/crud/functions/deleteTableRelation.d.ts +11 -0
- package/dist/server/routes/crud/functions/deleteTableRelation.d.ts.map +1 -0
- package/dist/server/routes/crud/functions/deleteTableRelation.js +74 -0
- package/dist/server/routes/crud/functions/readTableRelations.d.ts +25 -0
- package/dist/server/routes/crud/functions/readTableRelations.d.ts.map +1 -0
- package/dist/server/routes/crud/functions/readTableRelations.js +76 -0
- package/dist/server/routes/crud/functions/updateTableRelation.d.ts +14 -0
- package/dist/server/routes/crud/functions/updateTableRelation.d.ts.map +1 -0
- package/dist/server/routes/crud/functions/updateTableRelation.js +117 -0
- package/dist/server/routes/dictionaries/dictionaries.route.d.ts +2 -0
- package/dist/server/routes/dictionaries/dictionaries.route.d.ts.map +1 -0
- package/dist/server/routes/dictionaries/dictionaries.route.js +134 -0
- package/dist/server/routes/dictionaries/dictionaries.schema.d.ts +190 -0
- package/dist/server/routes/dictionaries/dictionaries.schema.d.ts.map +1 -0
- package/dist/server/routes/dictionaries/dictionaries.schema.js +86 -0
- package/dist/server/routes/dictionaries/functions/create.dictionary.d.ts +10 -0
- package/dist/server/routes/dictionaries/functions/create.dictionary.d.ts.map +1 -0
- package/dist/server/routes/dictionaries/functions/create.dictionary.item.d.ts +13 -0
- package/dist/server/routes/dictionaries/functions/create.dictionary.item.d.ts.map +1 -0
- package/dist/server/routes/dictionaries/functions/create.dictionary.item.js +57 -0
- package/dist/server/routes/dictionaries/functions/create.dictionary.js +76 -0
- package/dist/server/routes/dictionaries/functions/delete.dictionary.d.ts +9 -0
- package/dist/server/routes/dictionaries/functions/delete.dictionary.d.ts.map +1 -0
- package/dist/server/routes/dictionaries/functions/delete.dictionary.item.d.ts +11 -0
- package/dist/server/routes/dictionaries/functions/delete.dictionary.item.d.ts.map +1 -0
- package/dist/server/routes/dictionaries/functions/delete.dictionary.item.js +32 -0
- package/dist/server/routes/dictionaries/functions/delete.dictionary.js +28 -0
- package/dist/server/routes/dictionaries/functions/read.dictionaries.d.ts +5 -0
- package/dist/server/routes/dictionaries/functions/read.dictionaries.d.ts.map +1 -0
- package/dist/server/routes/dictionaries/functions/read.dictionaries.js +27 -0
- package/dist/server/routes/dictionaries/functions/update.dictionary.d.ts +10 -0
- package/dist/server/routes/dictionaries/functions/update.dictionary.d.ts.map +1 -0
- package/dist/server/routes/dictionaries/functions/update.dictionary.item.d.ts +13 -0
- package/dist/server/routes/dictionaries/functions/update.dictionary.item.d.ts.map +1 -0
- package/dist/server/routes/dictionaries/functions/update.dictionary.item.js +48 -0
- package/dist/server/routes/dictionaries/functions/update.dictionary.js +51 -0
- package/dist/server/routes/file/schema.js +6 -6
- package/dist/server/routes/table/functions/getData.d.ts +3 -2
- package/dist/server/routes/table/functions/getData.d.ts.map +1 -1
- package/dist/server/routes/table/functions/getData.js +2 -14
- package/dist/server/routes/table/schema.d.ts +5 -4
- package/dist/server/routes/table/schema.d.ts.map +1 -1
- package/dist/server/routes/table/schema.js +13 -13
- 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
|
+
}
|