@opengis/gis 0.2.150 → 0.2.151

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 (197) hide show
  1. package/README.md +181 -181
  2. package/dist/{CardIcon-CwtOJb6Z.js → CardIcon-COYvGn3y.js} +1 -1
  3. package/dist/{EntityTablePage-BZcdLpRo.js → EntityTablePage-n5jN3Vg8.js} +2 -2
  4. package/dist/{ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-BkUQoTIz.js → ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-_bbOMBQM.js} +1 -1
  5. package/dist/{HeaderActions.vue_vue_type_script_setup_true_lang-ls8AXq2-.js → HeaderActions.vue_vue_type_script_setup_true_lang-X8W5T9FU.js} +1 -1
  6. package/dist/{MapSettings-B9MYgBIp.js → MapSettings-DFpaiasG.js} +2 -2
  7. package/dist/{RastersTablePage-BoVW5Fv8.js → RastersTablePage-BDPlRImd.js} +2 -2
  8. package/dist/{cartocss-DYZTBG6c.js → cartocss-DAww4B8T.js} +5 -5
  9. package/dist/{import-utils-DsfbRYWS.js → import-utils-BD6m47wz.js} +1 -1
  10. package/dist/{index-FZ4LFEYB.js → index-DgSwtpZG.js} +666 -666
  11. package/dist/index.css +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/index.umd.cjs +8 -8
  14. package/dist/{raster-BsoWgW3l.js → raster-Dj4pagIY.js} +3 -3
  15. package/dist/{register-BKQkDmho.js → register-lve1-eW0.js} +3 -3
  16. package/dist/{service-B3tj7wpL.js → service-DfMY6QRr.js} +6 -6
  17. package/dist/{vs-datatable-N1huoMSR.js → vs-datatable-CMGb14xz.js} +2 -2
  18. package/module/cls.json +6 -6
  19. package/module/gis/card/gis.metadata.table/index.yml +22 -22
  20. package/module/gis/card/gis.metadata.table/main_info.hbs +20 -20
  21. package/module/gis/card/gis.metadata.table/metadata_info.hbs +27 -27
  22. package/module/gis/card/gis.metadata.table/other.hbs +25 -25
  23. package/module/gis/card/gis.rasters.table/index.yml +11 -11
  24. package/module/gis/card/gis.rasters.table/main_info.hbs +27 -27
  25. package/module/gis/card/gis.registers.table/cls.hbs +36 -36
  26. package/module/gis/card/gis.registers.table/columns.hbs +89 -89
  27. package/module/gis/card/gis.registers.table/filters.hbs +80 -80
  28. package/module/gis/card/gis.registers.table/index.yml +23 -23
  29. package/module/gis/card/gis.registers.table/main_info.hbs +35 -35
  30. package/module/gis/card/gis.registers.table/source.hbs +45 -45
  31. package/module/gis/card/gis.services.table/attributes.hbs +91 -91
  32. package/module/gis/card/gis.services.table/filters.hbs +83 -83
  33. package/module/gis/card/gis.services.table/index.yml +25 -25
  34. package/module/gis/card/gis.services.table/main_info.hbs +27 -27
  35. package/module/gis/card/gis.services.table/source.hbs +25 -25
  36. package/module/gis/cls/bool.yes_no.json +12 -12
  37. package/module/gis/cls/encoding.json +14 -14
  38. package/module/gis/cls/geom_type.json +14 -14
  39. package/module/gis/cls/gis.column_type.json +34 -34
  40. package/module/gis/cls/gis.column_view_type.json +26 -26
  41. package/module/gis/cls/gis.filter_type.json +22 -22
  42. package/module/gis/cls/language.json +10 -10
  43. package/module/gis/cls/meta.service_type.json +42 -42
  44. package/module/gis/cls/ogc.service.json +21 -21
  45. package/module/gis/cls/service_type.json +42 -42
  46. package/module/gis/cls/source_type.json +10 -10
  47. package/module/gis/cls/standarts.json +6 -6
  48. package/module/gis/cls/topic_category.json +106 -106
  49. package/module/gis/cls/update_frequency.json +29 -29
  50. package/module/gis/cls/view.json +21 -21
  51. package/module/gis/form/feedback.answer.form.json +10 -10
  52. package/module/gis/form/gis.cartocss.form.json +64 -64
  53. package/module/gis/form/gis.group_list.form.json +17 -17
  54. package/module/gis/form/gis.maps.form.json +61 -61
  55. package/module/gis/form/gis.metadata.form.json +240 -240
  56. package/module/gis/form/gis.metadata_new.form.json +195 -195
  57. package/module/gis/form/gis.ogc_service.form.json +51 -51
  58. package/module/gis/form/gis.rasters.form.json +76 -76
  59. package/module/gis/form/gis.registers.form.json +124 -124
  60. package/module/gis/form/gis.registers_column.form.json +84 -84
  61. package/module/gis/form/gis.registers_filter.form.json +65 -65
  62. package/module/gis/form/gis.services.form.json +111 -111
  63. package/module/gis/form/gis.services_attributes.form.json +75 -75
  64. package/module/gis/form/gis.services_filter.form.json +65 -65
  65. package/module/gis/menu.json +43 -43
  66. package/module/gis/select/admin.cls.name.sql +9 -9
  67. package/module/gis/select/layer_list.sql +7 -7
  68. package/module/gis/select/layer_list_text.sql +7 -7
  69. package/module/gis/select/pg.column_name.geometry.sql +12 -12
  70. package/module/gis/select/pg.columns.parent.sql +6 -6
  71. package/module/gis/select/pg.table_name.sql +17 -17
  72. package/module/gis/select/service_id.sql +1 -1
  73. package/module/gis/table/gis.cartocss.table.json +74 -74
  74. package/module/gis/table/gis.group_list.table.json +58 -58
  75. package/module/gis/table/gis.maps.table.json +110 -110
  76. package/module/gis/table/gis.metadata.table.json +70 -70
  77. package/module/gis/table/gis.metadata_new.table.json +137 -137
  78. package/module/gis/table/gis.ogc_service.table.json +100 -100
  79. package/module/gis/table/gis.rasters.table.json +113 -113
  80. package/module/gis/table/gis.registers.table.json +143 -143
  81. package/module/gis/table/gis.services.table.json +120 -120
  82. package/module/gis/table/site.gis.registers.table.json +88 -88
  83. package/module/gis/table/site.gis.services.table.json +106 -106
  84. package/module/gis/templates/ISO19136_2017_gml_template.xml +330 -330
  85. package/module/gis/tokens.yml +5 -5
  86. package/module/permissions/form/permissions.users.form.json +151 -151
  87. package/module/permissions/table/gis.permissions.table.json +97 -97
  88. package/module/test/cls/bp_build_type.json +37 -37
  89. package/module/test/cls/doc_status.json +31 -31
  90. package/module/test/cls/ts.temp_status.json +18 -18
  91. package/module/test/cls/ts.temp_structure.ts_class.json +49 -49
  92. package/module/test/cls/ts.temp_type.json +9 -9
  93. package/module/test/layer/bp.json +59 -59
  94. package/module/test/layer/bp1.yml +33 -33
  95. package/module/test/layer/individual.yml +53 -53
  96. package/module/test/layer/ts.linking_passport.yml +55 -55
  97. package/module/test/layer/ts.temp_structure.yml +50 -50
  98. package/module/test/map/addr.yml +21 -21
  99. package/module/test/map/bp_myo.json +43 -43
  100. package/module/test/map/bpf.json +43 -43
  101. package/module/test/map/main.json +37 -37
  102. package/module/test/map/mbd.json +92 -92
  103. package/module/test/map/ts.json +53 -53
  104. package/module/test/select/address_id.json +2 -2
  105. package/module/test/select/address_id.sql +7 -7
  106. package/module/test/select/core.user_uid.sql +1 -1
  107. package/module/test/table/data_address.street.table.json +69 -69
  108. package/module/test/table/data_bp_myo.bp.table.json +122 -122
  109. package/package.json +75 -75
  110. package/plugin.js +55 -55
  111. package/server/migrations/array_intersect.sql +13 -13
  112. package/server/migrations/cartocss.sql +27 -27
  113. package/server/migrations/group_list.sql +74 -74
  114. package/server/migrations/maps.sql +30 -30
  115. package/server/migrations/metadata.sql +415 -415
  116. package/server/migrations/ogc.sql +106 -106
  117. package/server/migrations/rasters.sql +265 -265
  118. package/server/migrations/services.sql +247 -247
  119. package/server/migrations/services_users_rel.sql +22 -22
  120. package/server/migrations/widgets.sql +20 -20
  121. package/server/plugins/crons.js +21 -21
  122. package/server/plugins/mapnik/funcs/checkRasterFile.js +109 -109
  123. package/server/plugins/mapnik/funcs/mapnik.js +151 -151
  124. package/server/plugins/mapnik/funcs/rootFolder.mjs +8 -8
  125. package/server/plugins/mapnik/map.proto +230 -230
  126. package/server/plugins/vite.js +74 -74
  127. package/server/routes/gis/cartocss/add.cartocss.js +42 -42
  128. package/server/routes/gis/cartocss/get.cartocss.js +59 -59
  129. package/server/routes/gis/checkXML.js +117 -117
  130. package/server/routes/gis/dashboard.js +29 -29
  131. package/server/routes/gis/index.mjs +87 -87
  132. package/server/routes/gis/metadata/editMetadata.js +34 -34
  133. package/server/routes/gis/metadata/metadataXML.js +13 -13
  134. package/server/routes/gis/ogc/map.info.point.js +124 -124
  135. package/server/routes/gis/registers/add.registry.js +35 -35
  136. package/server/routes/gis/registers/del.registry.js +14 -14
  137. package/server/routes/gis/registers/funcs/classifiers.js +100 -100
  138. package/server/routes/gis/registers/funcs/columns.js +3 -3
  139. package/server/routes/gis/registers/funcs/content.type.js +9 -9
  140. package/server/routes/gis/registers/funcs/get.info.js +89 -89
  141. package/server/routes/gis/registers/funcs/handleRegistryRequest.js +146 -146
  142. package/server/routes/gis/registers/gis.export.js +153 -153
  143. package/server/routes/gis/registers/gis.registry.js +64 -64
  144. package/server/routes/gis/registers/gis.registry.list.js +59 -59
  145. package/server/routes/gis/registers/insert.columns.js +107 -107
  146. package/server/routes/gis/registers/insert.filters.js +110 -110
  147. package/server/routes/gis/registers/map.registry.js +79 -79
  148. package/server/routes/gis/services/add.service.js +64 -64
  149. package/server/routes/gis/services/del.service.js +12 -12
  150. package/server/routes/gis/services/get.layer.geom.js +27 -27
  151. package/server/routes/gis/services/get.services.col.js +33 -33
  152. package/server/routes/gis/services/get.services.js +84 -84
  153. package/server/routes/gis/services/legend.auto.js +77 -77
  154. package/server/routes/map/controllers/clearTiles.js +34 -34
  155. package/server/routes/map/controllers/geojson.js +187 -187
  156. package/server/routes/map/controllers/jsonData.js +205 -205
  157. package/server/routes/map/controllers/layerList.js +102 -102
  158. package/server/routes/map/controllers/map.js +123 -123
  159. package/server/routes/map/controllers/mapCatalog.js +72 -72
  160. package/server/routes/map/controllers/mapCatalogAttribute.js +55 -55
  161. package/server/routes/map/controllers/mapFeatures.js +128 -128
  162. package/server/routes/map/controllers/mapFormat.js +234 -234
  163. package/server/routes/map/controllers/mapTiles.js +152 -152
  164. package/server/routes/map/controllers/maps.js +15 -15
  165. package/server/routes/map/controllers/marker_icon.js +43 -43
  166. package/server/routes/map/controllers/vtile.js +172 -172
  167. package/server/routes/map/index.mjs +169 -169
  168. package/server/routes/map/maps/add.map.js +43 -43
  169. package/server/routes/map/maps/del.map.js +18 -18
  170. package/server/routes/map/maps/get.map.js +118 -118
  171. package/server/routes/map/vtile1.js +179 -179
  172. package/server/routes/map/widgets/add.widget.js +38 -38
  173. package/server/routes/map/widgets/del.widget.js +22 -22
  174. package/server/routes/map/widgets/get.widget.js +40 -40
  175. package/server/routes/mapnik/controllers/clearTiles.js +106 -106
  176. package/server/routes/mapnik/controllers/createXml.js +67 -67
  177. package/server/routes/mapnik/controllers/createXmlMulti.js +76 -76
  178. package/server/routes/mapnik/controllers/fileSearch.js +38 -38
  179. package/server/routes/mapnik/controllers/fileStat.js +28 -28
  180. package/server/routes/mapnik/controllers/mapnikLogger.js +29 -29
  181. package/server/routes/mapnik/controllers/mapnikStat.js +19 -19
  182. package/server/routes/mapnik/controllers/pretile.js +144 -144
  183. package/server/routes/mapnik/controllers/rasterInfo.js +57 -57
  184. package/server/routes/mapnik/controllers/readDir.js +22 -22
  185. package/server/routes/mapnik/controllers/rtile.js +150 -150
  186. package/server/routes/mapnik/controllers/uploadRaster.js +157 -157
  187. package/server/routes/mapnik/functions/cartoBounds.js +32 -32
  188. package/server/routes/mapnik/functions/convertBoundToTile.js +27 -27
  189. package/server/routes/mapnik/functions/uploadXML.js +113 -113
  190. package/server/routes/mapnik/index.js +40 -40
  191. package/server/routes/permissions/controllers/catalog.permissions.edit.js +22 -22
  192. package/server/routes/permissions/controllers/catalog.permissions.js +7 -7
  193. package/server/routes/permissions/controllers/gis.catalog.js +80 -80
  194. package/server/routes/permissions/controllers/utils/get.permissions.js +59 -59
  195. package/server/routes/permissions/index.mjs +18 -18
  196. package/server/routes/root.mjs +3 -3
  197. package/utils.js +13 -13
@@ -1,205 +1,205 @@
1
- import path from 'node:path';
2
- import { existsSync } from 'node:fs';
3
- import { createHash } from 'node:crypto';
4
- import {
5
- readFile, stat, mkdir, writeFile,
6
- } from 'node:fs/promises';
7
-
8
- import {
9
- pgClients, getMeta, metaFormat,
10
- } from '@opengis/fastify-table/utils.js';
11
-
12
- import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
13
-
14
- const systemColumns = ['uid', 'cdate', 'editor_id', 'editor_date', 'created_by', 'created_at', 'updated_by', 'updated_at', 'geom'];
15
-
16
- /**
17
- * Повертає JSON дані для реєстру на основі налаштувань з gis.registers
18
- *
19
- * @method GET
20
- * @param {string} params.layer - register_id
21
- * @param {string} query.page - номер сторінки (default: 1)
22
- * @param {string} query.limit - кількість записів на сторінку (default: 100)
23
- * @param {string} query.where - додаткова WHERE умова
24
- * @param {string} query.sql - повернути SQL запит замість даних
25
- * @param {boolean} query.nocache - не використовувати кеш
26
- */
27
- export default async function jsonData({
28
- pg = pgClients.client, params = {}, query = {}, unittest,
29
- }, reply) {
30
- const { layer } = params;
31
- const {
32
- page = 1,
33
- limit = 100,
34
- where,
35
- sql,
36
- nocache
37
- } = query;
38
-
39
- if (!layer) {
40
- return reply.status(400).send('not enough params: layer (register_id)');
41
- }
42
-
43
- const registerId = layer.replace('.json', '');
44
- const pageNum = parseInt(page, 10) || 1;
45
- const limitNum = Math.min(parseInt(limit, 10) || 100, 1000); // max 1000
46
- const offset = (pageNum - 1) * limitNum;
47
-
48
- if (!pg.pk?.['gis.registers']) {
49
- return reply.status(404).send('registers table not found');
50
- }
51
-
52
- const register = await pg.query(
53
- `select
54
- table_name,
55
- pk,
56
- query,
57
- columns,
58
- is_public,
59
- is_active,
60
- "order"
61
- from gis.registers
62
- where register_id = $1 and is_active = true and is_public = true`,
63
- [registerId],
64
- ).then(el => el.rows?.[0]);
65
-
66
- if (!register) {
67
- return reply.status(404).send('register not found or not available');
68
- }
69
-
70
- const {
71
- table_name: table,
72
- pk,
73
- query: registerQuery,
74
- columns,
75
- order: orderBy,
76
- } = register;
77
-
78
- if (!table) {
79
- return reply.status(400).send('invalid register table_name');
80
- }
81
-
82
- if (!pg.pk?.[table]?.length) {
83
- return reply.status(404).send('table not found');
84
- }
85
-
86
- const meta = await getMeta({ pg, table });
87
- const { columns: metaColumns = [] } = meta || {};
88
-
89
- let attributeColumns = [];
90
- if (columns && Array.isArray(columns)) {
91
- attributeColumns = columns
92
- .filter(col => col.name && !systemColumns.includes(col.name))
93
- .map(col => col.name);
94
- }
95
-
96
- const columnsFiltered = attributeColumns.length > 0
97
- ? metaColumns.filter(col => attributeColumns.includes(col.name) && !systemColumns.includes(col.name))
98
- : metaColumns.filter(col => !systemColumns.includes(col.name));
99
-
100
- const props = columnsFiltered.map(el =>
101
- `"${el.name}"${['int4'].includes(el.type) ? '::text' : ''}`
102
- ).join(', ');
103
-
104
- if (!props) {
105
- return reply.status(400).send('no columns to select');
106
- }
107
-
108
- const cacheHash = createHash('md5')
109
- .update([registerId, where, registerQuery, pageNum, limitNum].filter(el => el).join('-'))
110
- .digest('hex');
111
-
112
- const today = new Date().toISOString().split('T')[0];
113
- const filepath = path.join(
114
- rootFolder,
115
- `/json/${registerId}/${today}/${cacheHash}.json`,
116
- );
117
-
118
- if (existsSync(filepath) && !nocache && !sql && !unittest) {
119
- const { birthtimeMs = Date.now() } = await stat(filepath);
120
- const ageInMs = Date.now() - birthtimeMs;
121
- if (ageInMs < 86400000) {
122
- const content = await readFile(filepath, 'utf8');
123
- return reply
124
- .status(200)
125
- .header('Content-Type', 'application/json')
126
- .header('Cache-Control', 'public, max-age=86400')
127
- .send(content);
128
- }
129
- }
130
-
131
- const whereConditions = [
132
- registerQuery || '1=1',
133
- where || 'true',
134
- ].join(' AND ');
135
-
136
- const quotedTable = table.split('.').map(el => `"${el}"`).join('.');
137
-
138
- const countQuery = `
139
- SELECT COUNT(*) as total
140
- FROM ${quotedTable}
141
- WHERE ${whereConditions}
142
- `;
143
-
144
- const dataQuery = `
145
- SELECT "${pk}" as id ${props ? ', ' + props : ''}
146
- FROM ${quotedTable}
147
- WHERE ${whereConditions}
148
- ${orderBy ? `ORDER BY ${orderBy}` : ''}
149
- LIMIT ${limitNum}
150
- OFFSET ${offset}
151
- `;
152
-
153
- if (sql === '1') return { countQuery, dataQuery };
154
- if (sql === '2') return filepath.replace(/\\/g, '/');
155
-
156
- try {
157
- const [countResult, dataResult] = await Promise.all([
158
- pg.query(countQuery),
159
- pg.query(dataQuery),
160
- ]);
161
-
162
- const total = parseInt(countResult.rows[0]?.total || 0, 10);
163
- const rows = dataResult.rows || [];
164
-
165
- // Обробка класифікаторів
166
- if (columns && Array.isArray(columns)) {
167
- const classifiers = columns
168
- .filter(col => col.data && ['select', 'badge', 'tags', 'integer', 'text', 'text[]'].includes(col.format))
169
- .reduce((acc, curr) => ({ ...acc, [curr.name]: curr.data }), {});
170
-
171
- if (Object.keys(classifiers).length > 0) {
172
- await metaFormat({
173
- rows,
174
- table,
175
- cls: classifiers,
176
- suffix: true,
177
- });
178
- }
179
- }
180
-
181
- const result = {
182
- register_id: registerId,
183
- total,
184
- page: pageNum,
185
- limit: limitNum,
186
- pages: Math.ceil(total / limitNum),
187
- data: rows,
188
- };
189
-
190
- const jsonString = JSON.stringify(result);
191
-
192
- if (!nocache && !unittest) {
193
- await mkdir(path.dirname(filepath), { recursive: true });
194
- await writeFile(filepath, jsonString, 'utf8');
195
- }
196
-
197
- return reply
198
- .status(200)
199
- .header('Content-Type', 'application/json')
200
- .header('Cache-Control', nocache ? 'no-cache' : 'public, max-age=86400')
201
- .send(jsonString);
202
- } catch (err) {
203
- return reply.status(500).send({ error: err.message });
204
- }
205
- }
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { createHash } from 'node:crypto';
4
+ import {
5
+ readFile, stat, mkdir, writeFile,
6
+ } from 'node:fs/promises';
7
+
8
+ import {
9
+ pgClients, getMeta, metaFormat,
10
+ } from '@opengis/fastify-table/utils.js';
11
+
12
+ import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
13
+
14
+ const systemColumns = ['uid', 'cdate', 'editor_id', 'editor_date', 'created_by', 'created_at', 'updated_by', 'updated_at', 'geom'];
15
+
16
+ /**
17
+ * Повертає JSON дані для реєстру на основі налаштувань з gis.registers
18
+ *
19
+ * @method GET
20
+ * @param {string} params.layer - register_id
21
+ * @param {string} query.page - номер сторінки (default: 1)
22
+ * @param {string} query.limit - кількість записів на сторінку (default: 100)
23
+ * @param {string} query.where - додаткова WHERE умова
24
+ * @param {string} query.sql - повернути SQL запит замість даних
25
+ * @param {boolean} query.nocache - не використовувати кеш
26
+ */
27
+ export default async function jsonData({
28
+ pg = pgClients.client, params = {}, query = {}, unittest,
29
+ }, reply) {
30
+ const { layer } = params;
31
+ const {
32
+ page = 1,
33
+ limit = 100,
34
+ where,
35
+ sql,
36
+ nocache
37
+ } = query;
38
+
39
+ if (!layer) {
40
+ return reply.status(400).send('not enough params: layer (register_id)');
41
+ }
42
+
43
+ const registerId = layer.replace('.json', '');
44
+ const pageNum = parseInt(page, 10) || 1;
45
+ const limitNum = Math.min(parseInt(limit, 10) || 100, 1000); // max 1000
46
+ const offset = (pageNum - 1) * limitNum;
47
+
48
+ if (!pg.pk?.['gis.registers']) {
49
+ return reply.status(404).send('registers table not found');
50
+ }
51
+
52
+ const register = await pg.query(
53
+ `select
54
+ table_name,
55
+ pk,
56
+ query,
57
+ columns,
58
+ is_public,
59
+ is_active,
60
+ "order"
61
+ from gis.registers
62
+ where register_id = $1 and is_active = true and is_public = true`,
63
+ [registerId],
64
+ ).then(el => el.rows?.[0]);
65
+
66
+ if (!register) {
67
+ return reply.status(404).send('register not found or not available');
68
+ }
69
+
70
+ const {
71
+ table_name: table,
72
+ pk,
73
+ query: registerQuery,
74
+ columns,
75
+ order: orderBy,
76
+ } = register;
77
+
78
+ if (!table) {
79
+ return reply.status(400).send('invalid register table_name');
80
+ }
81
+
82
+ if (!pg.pk?.[table]?.length) {
83
+ return reply.status(404).send('table not found');
84
+ }
85
+
86
+ const meta = await getMeta({ pg, table });
87
+ const { columns: metaColumns = [] } = meta || {};
88
+
89
+ let attributeColumns = [];
90
+ if (columns && Array.isArray(columns)) {
91
+ attributeColumns = columns
92
+ .filter(col => col.name && !systemColumns.includes(col.name))
93
+ .map(col => col.name);
94
+ }
95
+
96
+ const columnsFiltered = attributeColumns.length > 0
97
+ ? metaColumns.filter(col => attributeColumns.includes(col.name) && !systemColumns.includes(col.name))
98
+ : metaColumns.filter(col => !systemColumns.includes(col.name));
99
+
100
+ const props = columnsFiltered.map(el =>
101
+ `"${el.name}"${['int4'].includes(el.type) ? '::text' : ''}`
102
+ ).join(', ');
103
+
104
+ if (!props) {
105
+ return reply.status(400).send('no columns to select');
106
+ }
107
+
108
+ const cacheHash = createHash('md5')
109
+ .update([registerId, where, registerQuery, pageNum, limitNum].filter(el => el).join('-'))
110
+ .digest('hex');
111
+
112
+ const today = new Date().toISOString().split('T')[0];
113
+ const filepath = path.join(
114
+ rootFolder,
115
+ `/json/${registerId}/${today}/${cacheHash}.json`,
116
+ );
117
+
118
+ if (existsSync(filepath) && !nocache && !sql && !unittest) {
119
+ const { birthtimeMs = Date.now() } = await stat(filepath);
120
+ const ageInMs = Date.now() - birthtimeMs;
121
+ if (ageInMs < 86400000) {
122
+ const content = await readFile(filepath, 'utf8');
123
+ return reply
124
+ .status(200)
125
+ .header('Content-Type', 'application/json')
126
+ .header('Cache-Control', 'public, max-age=86400')
127
+ .send(content);
128
+ }
129
+ }
130
+
131
+ const whereConditions = [
132
+ registerQuery || '1=1',
133
+ where || 'true',
134
+ ].join(' AND ');
135
+
136
+ const quotedTable = table.split('.').map(el => `"${el}"`).join('.');
137
+
138
+ const countQuery = `
139
+ SELECT COUNT(*) as total
140
+ FROM ${quotedTable}
141
+ WHERE ${whereConditions}
142
+ `;
143
+
144
+ const dataQuery = `
145
+ SELECT "${pk}" as id ${props ? ', ' + props : ''}
146
+ FROM ${quotedTable}
147
+ WHERE ${whereConditions}
148
+ ${orderBy ? `ORDER BY ${orderBy}` : ''}
149
+ LIMIT ${limitNum}
150
+ OFFSET ${offset}
151
+ `;
152
+
153
+ if (sql === '1') return { countQuery, dataQuery };
154
+ if (sql === '2') return filepath.replace(/\\/g, '/');
155
+
156
+ try {
157
+ const [countResult, dataResult] = await Promise.all([
158
+ pg.query(countQuery),
159
+ pg.query(dataQuery),
160
+ ]);
161
+
162
+ const total = parseInt(countResult.rows[0]?.total || 0, 10);
163
+ const rows = dataResult.rows || [];
164
+
165
+ // Обробка класифікаторів
166
+ if (columns && Array.isArray(columns)) {
167
+ const classifiers = columns
168
+ .filter(col => col.data && ['select', 'badge', 'tags', 'integer', 'text', 'text[]'].includes(col.format))
169
+ .reduce((acc, curr) => ({ ...acc, [curr.name]: curr.data }), {});
170
+
171
+ if (Object.keys(classifiers).length > 0) {
172
+ await metaFormat({
173
+ rows,
174
+ table,
175
+ cls: classifiers,
176
+ suffix: true,
177
+ });
178
+ }
179
+ }
180
+
181
+ const result = {
182
+ register_id: registerId,
183
+ total,
184
+ page: pageNum,
185
+ limit: limitNum,
186
+ pages: Math.ceil(total / limitNum),
187
+ data: rows,
188
+ };
189
+
190
+ const jsonString = JSON.stringify(result);
191
+
192
+ if (!nocache && !unittest) {
193
+ await mkdir(path.dirname(filepath), { recursive: true });
194
+ await writeFile(filepath, jsonString, 'utf8');
195
+ }
196
+
197
+ return reply
198
+ .status(200)
199
+ .header('Content-Type', 'application/json')
200
+ .header('Cache-Control', nocache ? 'no-cache' : 'public, max-age=86400')
201
+ .send(jsonString);
202
+ } catch (err) {
203
+ return reply.status(500).send({ error: err.message });
204
+ }
205
+ }
@@ -1,102 +1,102 @@
1
- import {
2
- config, pgClients, yml2json, getTemplates, getTemplate,
3
- } from '@opengis/fastify-table/utils.js';
4
- import getPermissions from '../../permissions/controllers/utils/get.permissions.js';
5
-
6
- export default async function layerList({
7
- pg = pgClients.client, query = {}, user = {},
8
- }, reply) {
9
- const { sql } = query;
10
- const layers = getTemplates('layer');
11
- const files = Promise.all(layers.map(async (el) => {
12
- const layer = await getTemplate('layer', el[0]);
13
- return layer;
14
- }));
15
-
16
- if (!pg.pk?.['gis.services'] && !pg.pk?.['gis.ogc_service'] && !pg.pk?.['gis.rasters'] && !pg.pk?.['gis.cartocss']) {
17
- return files;
18
- // return reply.status(404).send('services table not found');
19
- }
20
-
21
- const { all_layers, layer_list } = (user.uid) ? await getPermissions({ pg, uid: user.uid }) : { all_layers: false, layer_list: [] };
22
-
23
- const whereServices = (all_layers === true) ? '1=1'
24
- : (user.uid && !all_layers) ? '( s.is_public or s.service_id = any($1))'
25
- : 'is_public';
26
-
27
- const whereOgcServices = (all_layers === true) ? '1=1'
28
- : (user.uid && !all_layers) ? '( s.ispublic or s.ogc_service_id = any($1))'
29
- : 'ispublic';
30
-
31
- const whereRasters = (all_layers === true) ? '1=1'
32
- : (user.uid && !all_layers) ? '(s.is_public or s.raster_id = any($1))'
33
- : 'is_public';
34
-
35
- const whereCartocss = (all_layers === true) ? '1=1'
36
- : (user.uid && !all_layers) ? '(s.is_public or s.cartocss_id = any($1))'
37
- : 'is_public';
38
- const q = `
39
- select * from
40
- (
41
- select service_id as id, name, category, style, bbox::box2d as extent, st_asgeojson(bbox)::json as geom, coalesce('/api/vtile/'||service_id||'/ua/{z}/{x}/{y}.vmt',null) as url, coalesce(service_type, 'vtile') as service, group_id,
42
- popup, card, filters, source_path, legend
43
- from gis.services s where is_active and
44
- ${whereServices}
45
- union all
46
- select ogc_service_id as id, name, category, null as style, geom::box2d as extent, st_asgeojson(geom)::json as geom, url, 'ogc' as service, group_id,
47
- null as popup, null as card, null as filters, table_name as source_path, null as legend
48
- from gis.ogc_service s where enabled and
49
- ${whereOgcServices}
50
- union all
51
- select raster_id as id, name, null as category, null as style, geom::box2d as extent, st_asgeojson(geom)::json as geom,
52
- coalesce('/api/gis-rtile/'||rtrim(
53
- replace(
54
- replace(
55
- encode(source_path::bytea, 'base64'),
56
- '+', '-'
57
- ),
58
- '/', '_'
59
- ),
60
- '='
61
- )||'/{z}/{x}/{y}.png',null) as url, 'raster' as service, group_id,
62
- null as popup, null as card, null as filters, source_path, null as legend
63
- from gis.rasters s where is_active and
64
- ${whereRasters}
65
- union all
66
- select cartocss_id as id, name, null as category, style, geom::box2d as bbox, st_asgeojson(geom)::json as geom,
67
- coalesce('/api/gis-rtile/'||cartocss_id||'/{z}/{x}/{y}.png',null) as url, 'cartocss' as service, group_id,
68
- null as popup, null as card, null as filters, null as source_path, null as legend
69
- from gis.cartocss s where enabled and
70
- ${whereCartocss}
71
- )q order by q.name
72
- `;
73
-
74
- if (user.uid && sql) return q;
75
-
76
- const rows = await pg.query(q, (user.uid && !all_layers) ? [layer_list] : []).then(el => el.rows || []);
77
-
78
- const totals = pg.queryCache ? await pg.queryCache('select json_object_agg(oid::regclass, reltuples) from pg_class')
79
- .then(el => el.rows?.[0]?.json_object_agg || {}) : {};
80
- rows.filter(row => row.source_path).forEach(row => Object.assign(row, { count: totals[row.source_path] || 0 }));
81
-
82
- const groupIds = rows.map(el => el.group_id).filter(Boolean);
83
- const groupNames = groupIds.length ? await pg.query('select json_object_agg(group_id,group_name) from gis.group_list where group_id=any($1)', [groupIds]).then(el => el.rows?.[0]?.json_object_agg || {}) : {};
84
- rows.filter(el => el.group_id).forEach(row => Object.assign(row, { group_name: groupNames[row.group_id] }));
85
-
86
- if (!rows.length) {
87
- return reply.status(200).send([]);
88
- }
89
-
90
- // parse service style
91
- rows.forEach(row => Object.assign(row, { style: row.style && ['vtile', 'geojson'].includes(row.service) ? yml2json(row.style) : row.style }));
92
- // parse extent string to number[]
93
- rows.filter(row => row.extent).forEach(row => Object.assign(row, {
94
- extent: row.extent.match(/BOX\(([^)]+)\)/)?.[1]
95
- ?.replace?.(/ /g, ',')
96
- ?.split?.(','),
97
- }));
98
-
99
- // return files;
100
-
101
- return reply.status(200).send(rows.concat(files));
102
- }
1
+ import {
2
+ config, pgClients, yml2json, getTemplates, getTemplate,
3
+ } from '@opengis/fastify-table/utils.js';
4
+ import getPermissions from '../../permissions/controllers/utils/get.permissions.js';
5
+
6
+ export default async function layerList({
7
+ pg = pgClients.client, query = {}, user = {},
8
+ }, reply) {
9
+ const { sql } = query;
10
+ const layers = getTemplates('layer');
11
+ const files = Promise.all(layers.map(async (el) => {
12
+ const layer = await getTemplate('layer', el[0]);
13
+ return layer;
14
+ }));
15
+
16
+ if (!pg.pk?.['gis.services'] && !pg.pk?.['gis.ogc_service'] && !pg.pk?.['gis.rasters'] && !pg.pk?.['gis.cartocss']) {
17
+ return files;
18
+ // return reply.status(404).send('services table not found');
19
+ }
20
+
21
+ const { all_layers, layer_list } = (user.uid) ? await getPermissions({ pg, uid: user.uid }) : { all_layers: false, layer_list: [] };
22
+
23
+ const whereServices = (all_layers === true) ? '1=1'
24
+ : (user.uid && !all_layers) ? '( s.is_public or s.service_id = any($1))'
25
+ : 'is_public';
26
+
27
+ const whereOgcServices = (all_layers === true) ? '1=1'
28
+ : (user.uid && !all_layers) ? '( s.ispublic or s.ogc_service_id = any($1))'
29
+ : 'ispublic';
30
+
31
+ const whereRasters = (all_layers === true) ? '1=1'
32
+ : (user.uid && !all_layers) ? '(s.is_public or s.raster_id = any($1))'
33
+ : 'is_public';
34
+
35
+ const whereCartocss = (all_layers === true) ? '1=1'
36
+ : (user.uid && !all_layers) ? '(s.is_public or s.cartocss_id = any($1))'
37
+ : 'is_public';
38
+ const q = `
39
+ select * from
40
+ (
41
+ select service_id as id, name, category, style, bbox::box2d as extent, st_asgeojson(bbox)::json as geom, coalesce('/api/vtile/'||service_id||'/ua/{z}/{x}/{y}.vmt',null) as url, coalesce(service_type, 'vtile') as service, group_id,
42
+ popup, card, filters, source_path, legend
43
+ from gis.services s where is_active and
44
+ ${whereServices}
45
+ union all
46
+ select ogc_service_id as id, name, category, null as style, geom::box2d as extent, st_asgeojson(geom)::json as geom, url, 'ogc' as service, group_id,
47
+ null as popup, null as card, null as filters, table_name as source_path, null as legend
48
+ from gis.ogc_service s where enabled and
49
+ ${whereOgcServices}
50
+ union all
51
+ select raster_id as id, name, null as category, null as style, geom::box2d as extent, st_asgeojson(geom)::json as geom,
52
+ coalesce('/api/gis-rtile/'||rtrim(
53
+ replace(
54
+ replace(
55
+ encode(source_path::bytea, 'base64'),
56
+ '+', '-'
57
+ ),
58
+ '/', '_'
59
+ ),
60
+ '='
61
+ )||'/{z}/{x}/{y}.png',null) as url, 'raster' as service, group_id,
62
+ null as popup, null as card, null as filters, source_path, null as legend
63
+ from gis.rasters s where is_active and
64
+ ${whereRasters}
65
+ union all
66
+ select cartocss_id as id, name, null as category, style, geom::box2d as bbox, st_asgeojson(geom)::json as geom,
67
+ coalesce('/api/gis-rtile/'||cartocss_id||'/{z}/{x}/{y}.png',null) as url, 'cartocss' as service, group_id,
68
+ null as popup, null as card, null as filters, null as source_path, null as legend
69
+ from gis.cartocss s where enabled and
70
+ ${whereCartocss}
71
+ )q order by q.name
72
+ `;
73
+
74
+ if (user.uid && sql) return q;
75
+
76
+ const rows = await pg.query(q, (user.uid && !all_layers) ? [layer_list] : []).then(el => el.rows || []);
77
+
78
+ const totals = pg.queryCache ? await pg.queryCache('select json_object_agg(oid::regclass, reltuples) from pg_class')
79
+ .then(el => el.rows?.[0]?.json_object_agg || {}) : {};
80
+ rows.filter(row => row.source_path).forEach(row => Object.assign(row, { count: totals[row.source_path] || 0 }));
81
+
82
+ const groupIds = rows.map(el => el.group_id).filter(Boolean);
83
+ const groupNames = groupIds.length ? await pg.query('select json_object_agg(group_id,group_name) from gis.group_list where group_id=any($1)', [groupIds]).then(el => el.rows?.[0]?.json_object_agg || {}) : {};
84
+ rows.filter(el => el.group_id).forEach(row => Object.assign(row, { group_name: groupNames[row.group_id] }));
85
+
86
+ if (!rows.length) {
87
+ return reply.status(200).send([]);
88
+ }
89
+
90
+ // parse service style
91
+ rows.forEach(row => Object.assign(row, { style: row.style && ['vtile', 'geojson'].includes(row.service) ? yml2json(row.style) : row.style }));
92
+ // parse extent string to number[]
93
+ rows.filter(row => row.extent).forEach(row => Object.assign(row, {
94
+ extent: row.extent.match(/BOX\(([^)]+)\)/)?.[1]
95
+ ?.replace?.(/ /g, ',')
96
+ ?.split?.(','),
97
+ }));
98
+
99
+ // return files;
100
+
101
+ return reply.status(200).send(rows.concat(files));
102
+ }