@opengis/gis 0.2.168 → 0.2.170

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 (192) hide show
  1. package/dist/{CardIcon-DzERD2T8.js → CardIcon-DXaESte6.js} +1 -1
  2. package/dist/{EntityTablePage-DAVJRQLe.js → EntityTablePage-BwyMNhrm.js} +1 -1
  3. package/dist/{ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-B-6axJUY.js → ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-DrHke0pk.js} +1 -1
  4. package/dist/{HeaderActions.vue_vue_type_script_setup_true_lang-D1iwSeRN.js → HeaderActions.vue_vue_type_script_setup_true_lang-9Rm8Dhc4.js} +1 -1
  5. package/dist/{MapSettings-wVix8wMh.js → MapSettings-E1aNltts.js} +1 -1
  6. package/dist/{RastersTablePage-u3oN_h_n.js → RastersTablePage-CJblEi8a.js} +1 -1
  7. package/dist/{cartocss-BIveBlgY.js → cartocss-Dv2QILZ2.js} +5 -5
  8. package/dist/{import-utils-DkKPU_Nd.js → import-utils-CL-V2wSj.js} +1 -1
  9. package/dist/{index-C3bl1qBS.js → index-Bi60lcRm.js} +4768 -4718
  10. package/dist/index.css +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.umd.cjs +31 -31
  13. package/dist/{raster-CfTJZx5p.js → raster-nn-sOUb9.js} +3 -3
  14. package/dist/{register-dV5ipRhb.js → register-CjHXBGr5.js} +3 -3
  15. package/dist/{service-DWC5gq3L.js → service-l3Csdnxm.js} +8 -7
  16. package/dist/{vs-datatable-Cd2Gp4jo.js → vs-datatable-CJlE3-mz.js} +3 -2
  17. package/module/test/map/addr.json +303 -0
  18. package/module/test/map/{addr.yml → addr1.yml} +3 -1
  19. package/module/test/select/address.str_id.sql +3 -0
  20. package/package.json +10 -10
  21. package/plugin.js +21 -12
  22. package/server/plugins/mapnik/funcs/checkRasterFile.js +1 -3
  23. package/server/routes/cartocss/cartocss.route.js +31 -0
  24. package/server/routes/cartocss/cartocss.route.test.js +79 -0
  25. package/server/routes/cartocss/cartocss.schema.js +73 -0
  26. package/server/routes/{gis/cartocss → cartocss/functions}/add.cartocss.js +8 -11
  27. package/server/routes/{gis/cartocss → cartocss/functions}/get.cartocss.js +11 -13
  28. package/server/routes/gis/functions/checkXML.js +107 -0
  29. package/server/routes/gis/functions/dashboard.js +24 -0
  30. package/server/routes/gis/gis.route.js +26 -0
  31. package/server/routes/gis/gis.route.test.js +34 -0
  32. package/server/routes/gis/gis.schema.js +1 -0
  33. package/server/routes/map/{controllers → functions}/clearTiles.js +9 -8
  34. package/server/routes/map/{controllers → functions}/geojson.js +43 -43
  35. package/server/routes/map/{controllers → functions}/gis.convert.js +15 -23
  36. package/server/routes/map/{controllers → functions}/gis.icon.js +23 -10
  37. package/server/routes/map/{controllers → functions}/jsonData.js +72 -71
  38. package/server/routes/map/{controllers → functions}/layerList.js +17 -17
  39. package/server/routes/map/{controllers → functions}/map.js +8 -9
  40. package/server/routes/map/{controllers → functions}/mapCatalog.js +7 -7
  41. package/server/routes/map/functions/mapCatalogAttribute.js +54 -0
  42. package/server/routes/map/{controllers → functions}/mapFeatures.js +22 -18
  43. package/server/routes/map/{controllers → functions}/mapFormat.js +25 -28
  44. package/server/routes/map/{controllers → functions}/mapTiles.js +39 -30
  45. package/server/routes/map/{controllers → functions}/maps.js +3 -3
  46. package/server/routes/map/{controllers → functions}/marker_icon.js +1 -1
  47. package/server/routes/map/{controllers → functions}/parseGeoFile.js +10 -11
  48. package/server/routes/map/{controllers → functions}/pointHeight.js +3 -7
  49. package/server/routes/map/{controllers → functions}/vtile.js +5 -3
  50. package/server/routes/map/{vtile1.js → functions/vtile1.js} +39 -33
  51. package/server/routes/map/map.route.js +280 -0
  52. package/server/routes/map/map.route.test.js +503 -0
  53. package/server/routes/map/map.schema.js +356 -0
  54. package/server/routes/mapnik/{controllers → functions}/clearTiles.js +26 -29
  55. package/server/routes/mapnik/{controllers → functions}/createXml.js +17 -20
  56. package/server/routes/mapnik/{controllers → functions}/createXmlMulti.js +11 -15
  57. package/server/routes/mapnik/{controllers → functions}/fileSearch.js +6 -9
  58. package/server/routes/mapnik/functions/fileStat.js +28 -0
  59. package/server/routes/mapnik/{controllers → functions}/pretile.js +29 -35
  60. package/server/routes/mapnik/{controllers → functions}/rasterInfo.js +19 -10
  61. package/server/routes/mapnik/{controllers → functions}/readDir.js +4 -4
  62. package/server/routes/mapnik/functions/rtile.js +164 -0
  63. package/server/routes/mapnik/{controllers → functions}/uploadRaster.js +19 -24
  64. package/server/routes/mapnik/functions/uploadXML.js +27 -0
  65. package/server/routes/mapnik/mapnik.route.js +102 -0
  66. package/server/routes/mapnik/mapnik.route.test.js +371 -0
  67. package/server/routes/mapnik/mapnik.schema.mjs +190 -0
  68. package/server/routes/mapnik.prefixless/functions/mapnikLogger.js +31 -0
  69. package/server/routes/{mapnik/controllers → mapnik.prefixless/functions}/mapnikStat.js +7 -5
  70. package/server/routes/mapnik.prefixless/mapnik.prefixless.route.js +20 -0
  71. package/server/routes/mapnik.prefixless/mapnik.prefixless.route.test.js +91 -0
  72. package/server/routes/mapnik.prefixless/mapnik.prefixless.schema.mjs +27 -0
  73. package/server/routes/maps/functions/add.map.js +42 -0
  74. package/server/routes/{map/maps → maps/functions}/del.map.js +6 -7
  75. package/server/routes/{map/maps → maps/functions}/get.map.js +12 -11
  76. package/server/routes/maps/maps.route.js +58 -0
  77. package/server/routes/maps/maps.route.test.js +231 -0
  78. package/server/routes/maps/maps.schema.js +10 -0
  79. package/server/routes/metadata/functions/editMetadata.js +35 -0
  80. package/server/routes/metadata/functions/metadataXML.js +19 -0
  81. package/server/routes/metadata/metadata.route.js +26 -0
  82. package/server/routes/metadata/metadata.route.test.js +47 -0
  83. package/server/routes/metadata/metadata.schema.js +53 -0
  84. package/server/routes/{gis/ogc → ogc/functions}/map.info.point.js +22 -29
  85. package/server/routes/ogc/ogc.route.js +21 -0
  86. package/server/routes/ogc/ogc.route.test.js +54 -0
  87. package/server/routes/ogc/ogc.schema.js +16 -0
  88. package/server/routes/permissions/functions/catalog.permissions.edit.js +20 -0
  89. package/server/routes/permissions/functions/catalog.permissions.js +13 -0
  90. package/server/routes/permissions/{controllers → functions}/gis.catalog.js +10 -10
  91. package/server/routes/permissions/permissions.route.js +35 -0
  92. package/server/routes/permissions/permissions.route.test.js +35 -0
  93. package/server/routes/permissions/permissions.schema.js +44 -0
  94. package/server/routes/registers/functions/add.registry.js +37 -0
  95. package/server/routes/{gis/registers/funcs → registers/functions}/classifiers.js +2 -2
  96. package/server/routes/{gis/registers → registers/functions}/del.registry.js +5 -5
  97. package/server/routes/{gis/registers/funcs → registers/functions}/get.info.js +9 -5
  98. package/server/routes/{gis/registers → registers/functions}/gis.export.js +27 -21
  99. package/server/routes/{gis/registers → registers/functions}/gis.registry.js +22 -15
  100. package/server/routes/{gis/registers → registers/functions}/gis.registry.list.js +12 -10
  101. package/server/routes/{gis/registers/funcs → registers/functions}/handleRegistryRequest.js +12 -11
  102. package/server/routes/registers/registers.route.js +80 -0
  103. package/server/routes/registers/registers.route.test.js +145 -0
  104. package/server/routes/registers/registers.schema.js +246 -0
  105. package/server/routes/{gis/services → services/functions}/add.service.js +14 -16
  106. package/server/routes/services/functions/create.route.js +93 -0
  107. package/server/routes/{gis/services → services/functions}/del.service.js +5 -5
  108. package/server/routes/{gis/services → services/functions}/get.service.users.js +15 -13
  109. package/server/routes/services/functions/get.services.col.js +34 -0
  110. package/server/routes/{gis/services → services/functions}/get.services.js +21 -16
  111. package/server/routes/services/services.route.js +76 -0
  112. package/server/routes/services/services.route.test.js +168 -0
  113. package/server/routes/services/services.schema.js +278 -0
  114. package/server/routes/widgets/functions/add.widget.js +39 -0
  115. package/server/routes/widgets/functions/del.widget.js +18 -0
  116. package/server/routes/{map/widgets → widgets/functions}/get.widget.js +12 -11
  117. package/server/routes/widgets/widgets.route.js +61 -0
  118. package/server/routes/widgets/widgets.route.test.js +144 -0
  119. package/server/routes/widgets/widgets.schema.js +25 -0
  120. package/module/cls.json +0 -7
  121. package/module/gis/card/gis.metadata.table/main_info.hbs +0 -21
  122. package/module/gis/card/gis.metadata.table/metadata_info.hbs +0 -28
  123. package/module/gis/card/gis.metadata.table/other.hbs +0 -26
  124. package/module/gis/card/gis.rasters.table/main_info.hbs +0 -27
  125. package/module/gis/card/gis.registers.table/cls.hbs +0 -36
  126. package/module/gis/card/gis.registers.table/columns.hbs +0 -89
  127. package/module/gis/card/gis.registers.table/filters.hbs +0 -80
  128. package/module/gis/card/gis.registers.table/main_info.hbs +0 -35
  129. package/module/gis/card/gis.registers.table/source.hbs +0 -45
  130. package/module/gis/card/gis.services.table/attributes.hbs +0 -91
  131. package/module/gis/card/gis.services.table/filters.hbs +0 -83
  132. package/module/gis/card/gis.services.table/main_info.hbs +0 -27
  133. package/module/gis/card/gis.services.table/source.hbs +0 -26
  134. package/module/test/cls/bp_build_type.json +0 -37
  135. package/module/test/cls/doc_status.json +0 -32
  136. package/module/test/cls/ts.temp_status.json +0 -18
  137. package/module/test/cls/ts.temp_structure.ts_class.json +0 -50
  138. package/module/test/cls/ts.temp_type.json +0 -10
  139. package/module/test/layer/bp.json +0 -60
  140. package/module/test/layer/bp1.yml +0 -33
  141. package/module/test/layer/individual.yml +0 -54
  142. package/module/test/layer/ts.linking_passport.yml +0 -56
  143. package/module/test/layer/ts.temp_structure.yml +0 -51
  144. package/module/test/map/bp_myo.json +0 -44
  145. package/module/test/map/bpf.json +0 -44
  146. package/module/test/map/main.json +0 -39
  147. package/module/test/map/mbd.json +0 -92
  148. package/module/test/map/ts.json +0 -53
  149. package/module/test/select/address_id.json +0 -3
  150. package/module/test/select/address_id.sql +0 -8
  151. package/module/test/select/core.user_uid.sql +0 -1
  152. package/module/test/table/data_address.street.table.json +0 -70
  153. package/module/test/table/data_bp_myo.bp.table.json +0 -123
  154. package/server/routes/gis/checkXML.js +0 -117
  155. package/server/routes/gis/dashboard.js +0 -29
  156. package/server/routes/gis/index.mjs +0 -92
  157. package/server/routes/gis/metadata/editMetadata.js +0 -34
  158. package/server/routes/gis/metadata/metadataXML.js +0 -13
  159. package/server/routes/gis/registers/add.registry.js +0 -35
  160. package/server/routes/gis/registers/insert.columns.js +0 -107
  161. package/server/routes/gis/registers/insert.filters.js +0 -110
  162. package/server/routes/gis/services/create.route.js +0 -96
  163. package/server/routes/gis/services/get.services.col.js +0 -33
  164. package/server/routes/map/controllers/mapCatalogAttribute.js +0 -56
  165. package/server/routes/map/index.mjs +0 -259
  166. package/server/routes/map/maps/add.map.js +0 -43
  167. package/server/routes/map/widgets/add.widget.js +0 -39
  168. package/server/routes/map/widgets/del.widget.js +0 -23
  169. package/server/routes/mapnik/controllers/fileStat.js +0 -28
  170. package/server/routes/mapnik/controllers/mapnikLogger.js +0 -29
  171. package/server/routes/mapnik/controllers/rtile.js +0 -167
  172. package/server/routes/mapnik/index.js +0 -40
  173. package/server/routes/permissions/controllers/catalog.permissions.edit.js +0 -22
  174. package/server/routes/permissions/controllers/catalog.permissions.js +0 -8
  175. package/server/routes/permissions/index.mjs +0 -18
  176. /package/server/routes/map/{controllers/pin → pin}/circle1.svg +0 -0
  177. /package/server/routes/map/{controllers/pin → pin}/circle2.svg +0 -0
  178. /package/server/routes/map/{controllers/pin → pin}/none.svg +0 -0
  179. /package/server/routes/map/{controllers/pin → pin}/pin.svg +0 -0
  180. /package/server/routes/map/{controllers/pin → pin}/pin1.svg +0 -0
  181. /package/server/routes/map/{controllers/pin → pin}/pin2.svg +0 -0
  182. /package/server/routes/map/{controllers/pin → pin}/pin3.svg +0 -0
  183. /package/server/routes/map/{controllers/pin → pin}/pin4.svg +0 -0
  184. /package/server/routes/map/{controllers/pin → pin}/pin5.svg +0 -0
  185. /package/server/routes/map/{controllers/pin → pin}/pin6.svg +0 -0
  186. /package/server/routes/map/{controllers/pin → pin}/pin7.svg +0 -0
  187. /package/server/routes/permissions/{controllers/utils → functions}/get.permissions.js +0 -0
  188. /package/server/routes/{gis/registers/funcs → registers/functions}/columns.js +0 -0
  189. /package/server/routes/{gis/registers/funcs → registers/functions}/content.type.js +0 -0
  190. /package/server/routes/{gis/registers → registers/functions}/map.registry.js +0 -0
  191. /package/server/routes/{gis/services → services/functions}/get.layer.geom.js +0 -0
  192. /package/server/routes/{gis/services → services/functions}/legend.auto.js +0 -0
@@ -0,0 +1,73 @@
1
+ const geometrySchema = {
2
+
3
+ type: "object",
4
+ required: ["type", "coordinates"],
5
+ properties: {
6
+ type: {
7
+ enum: [
8
+ "Point",
9
+ "LineString",
10
+ "Polygon",
11
+ "MultiPoint",
12
+ "MultiLineString",
13
+ "MultiPolygon",
14
+ ],
15
+ },
16
+ coordinates: {
17
+ type: "array",
18
+ },
19
+ },
20
+
21
+ };
22
+
23
+ const cssBodySchema = {
24
+ type: "object",
25
+ additionalProperties: true,
26
+
27
+ properties: {
28
+ id: { type: "string", pattern: '^([\\d\\w]+)$' },
29
+ name: { type: "string" },
30
+ group_id: { type: "string" },
31
+ cartocss_key: { type: "string" },
32
+ source_path: { type: "string" },
33
+ description: { type: ["string", "null"] },
34
+ card_table: { type: "string" },
35
+ card_columns: { type: "string" },
36
+ card_html: { type: "string" },
37
+ is_public: { type: "boolean" },
38
+ is_static: { type: "boolean" },
39
+ enabled: { type: "boolean" },
40
+ geom: geometrySchema,
41
+ },
42
+ };
43
+
44
+ export const getCartocssSchema = {
45
+ params: {
46
+ type: 'object',
47
+ required: ['id'],
48
+ properties: {
49
+ id: { type: 'string', pattern: '^([\\d\\w]+)$' }
50
+ }
51
+ },
52
+ querystring: {
53
+ type: 'object',
54
+ additionalProperties: false,
55
+ properties: {
56
+ nocache: { type: 'number', enum: [1] },
57
+ sql: { type: 'number', enum: [1] },
58
+ }
59
+ }
60
+ };
61
+
62
+ export const addCartocssSchema = {
63
+ params: {
64
+ type: 'object',
65
+ required: ['id'],
66
+ properties: {
67
+ id: { type: 'string', pattern: '^([\\d\\w]+)$' }
68
+ }
69
+ },
70
+ body: cssBodySchema,
71
+ };
72
+
73
+ export default null;
@@ -1,16 +1,13 @@
1
1
  import { pgClients, dataUpdate } from '@opengis/fastify-table/utils.js';
2
2
 
3
3
  import uploadXML from '../../mapnik/functions/uploadXML.js';
4
+ import { NotFoundError } from '@opengis/fastify-table/errors.js';
4
5
 
5
- export default async function addCartocss(req, reply) {
6
- const {
7
- pg = pgClients.client, body, params, user,
8
- } = req;
9
-
10
- const cartocss = await pg.query('select cartocss_id, source_path, config, style from gis.cartocss where cartocss_id=$1', [params.id]).then(el => el.rows?.[0]);
6
+ export default async function addCartocss({ id, body, uid }, pg = pgClients.client) {
7
+ const cartocss = await pg.query('select cartocss_id, source_path, config, style from gis.cartocss where cartocss_id=$1', [id]).then(el => el.rows?.[0]);
11
8
 
12
9
  if (!cartocss) {
13
- return reply.status(404).send({ error: 'cartocss not found', code: 404 });
10
+ throw NotFoundError('cartocss not found');
14
11
  }
15
12
 
16
13
  const sourcePath = body.source_path || cartocss.source_path;
@@ -22,21 +19,21 @@ export default async function addCartocss(req, reply) {
22
19
 
23
20
  await dataUpdate({
24
21
  pg,
25
- id: params.id,
22
+ id,
26
23
  table: 'gis.cartocss',
27
24
  data,
28
- uid: user?.uid,
25
+ uid,
29
26
  });
30
27
 
31
28
  // if not from static xml file
32
29
  if (!data.source_path) {
33
30
  await uploadXML({
34
- id: params.id,
31
+ id,
35
32
  dataset: config,
36
33
  style,
37
34
  source_path: sourcePath,
38
35
  }, pg);
39
36
  }
40
37
 
41
- return reply.status(200).send({ message: 'cartocss updated', code: 200 });
38
+ return { message: 'cartocss updated', code: 200 };
42
39
  }
@@ -2,6 +2,7 @@ import { config, pgClients } from '@opengis/fastify-table/utils.js';
2
2
 
3
3
  import mapnik from '../../../plugins/mapnik/funcs/mapnik.js';
4
4
  import cartoBounds from '../../mapnik/functions/cartoBounds.js';
5
+ import { NotFoundError, NotImplementedError } from '@opengis/fastify-table/errors.js';
5
6
 
6
7
  const { RenderTile } = mapnik();
7
8
 
@@ -10,39 +11,36 @@ const { prefix = '/api' } = config;
10
11
  const exclude = ['created_at', 'updated_at', 'created_by', 'updated_by'];
11
12
 
12
13
  export default async function checkCarto({
13
- pg = pgClients.client, params, query, user,
14
- }, reply) {
14
+ id, sql, nocache, isAdmin,
15
+ }, pg = pgClients.client) {
15
16
  const time = Date.now();
16
17
  if (!config.mapnik?.server) {
17
- return reply.status(400).send({ error: 'mapnik server address needed', code: 400 });
18
+ throw NotImplementedError('mapnik server address needed');
18
19
  }
19
20
 
20
- const { sql } = query || {};
21
-
22
21
  // extract all columns, migrations-agnostic
23
- const cartocss = await pg.query('select *, ARRAY[ST_XMin(geom), ST_YMin(geom), ST_XMax(geom), ST_YMax(geom)] as bounds, st_asgeojson(geom)::json as geom, source_path from gis.cartocss where cartocss_id=$1', [params.id]).then(el => el.rows?.[0]);
22
+ const cartocss = await pg.query('select *, ARRAY[ST_XMin(geom), ST_YMin(geom), ST_XMax(geom), ST_YMax(geom)] as bounds, st_asgeojson(geom)::json as geom, source_path from gis.cartocss where cartocss_id=$1', [id]).then(r => r.rows?.[0]);
24
23
 
25
24
  if (!cartocss) {
26
- return reply.status(404).send({ error: `cartocss not found: ${params.id}`, code: 404 });
25
+ throw NotFoundError(`cartocss not found: ${id}`);
27
26
  }
28
27
 
29
28
  // filter system columns
30
29
  Object.keys(cartocss).filter(key => exclude.includes(key)).forEach(key => delete cartocss[key]);
31
30
 
32
31
  const rtile = await RenderTile({
33
- name: cartocss.source_path ? `vector/${cartocss.source_path}` : params.id, // required for cartocss
32
+ name: cartocss.source_path ? `vector/${cartocss.source_path}` : id, // required for cartocss
34
33
  width: 256,
35
34
  bbox: [3713463.7081504324, 6088362.176970857, 3713616.5822070027, 6088515.051027427],
36
35
  ttl: '0',
37
- // debug: query.debug,
38
36
  }).catch(err => ({ err: err.toString() }));
39
37
 
40
38
  // skip empty bounds[null,null,null,null]
41
- const bounds = cartocss.bounds && cartocss.geom && !(query.nocache && user?.user_type?.includes?.('admin')) ? cartocss.bounds : await cartoBounds({
42
- id: params.id, dataset: cartocss.config, table: cartocss.card_table, sql,
39
+ const bounds = cartocss.bounds && cartocss.geom && !(nocache && isAdmin) ? cartocss.bounds : await cartoBounds({
40
+ id: id, dataset: cartocss.config, table: cartocss.card_table, sql,
43
41
  }, pg).catch(err => ({ err: err.toString() }));
44
42
 
45
- if (!(cartocss.bounds && cartocss.geom) && sql && user?.user_type?.includes?.('admin')) {
43
+ if (!(cartocss.bounds && cartocss.geom) && sql && isAdmin) {
46
44
  return bounds;
47
45
  }
48
46
 
@@ -50,7 +48,7 @@ export default async function checkCarto({
50
48
  time: Date.now() - time,
51
49
  ...cartocss,
52
50
  info: !!(cartocss.card_auto || (cartocss.card_html && cartocss.card_table)),
53
- url: `${prefix}/gis-rtile/${params.id}/{z}/{x}/{y}.png`,
51
+ url: `${prefix}/gis-rtile/${id}/{z}/{x}/{y}.png`,
54
52
  bounds,
55
53
  render: !!rtile.base64,
56
54
  renderError: rtile.err && config.local ? rtile.err : !!rtile.err,
@@ -0,0 +1,107 @@
1
+ import path from 'node:path';
2
+
3
+ import mapnik from "../../../plugins/mapnik/funcs/mapnik.js";
4
+
5
+ export default async function checkXML(pg, send = () => { }) {
6
+ if (!pg?.pk) {
7
+ throw new Error("empty pg");
8
+ }
9
+
10
+ const {
11
+ GetRasterStatus, GetRasterInfo, RenderTile,
12
+ } = mapnik();
13
+
14
+ const errors = { raster: 0, css: 0, service: 0 };
15
+
16
+ const rasters = pg.pk['gis.rasters'] ? await pg.query('select * from gis.rasters').then(r => r.rows || []) : [];
17
+
18
+ const css = pg.pk['gis.cartocss'] ? await pg.query('select * from gis.cartocss').then(r => r.rows || []) : [];
19
+
20
+ const dataset = pg.pk['gis.dataset'] && false ? await pg.query('select * from gis.dataset').then(r => r.rows || []) : [];
21
+
22
+ const total = rasters.length + css.length + dataset.length;
23
+ let successCount = 0;
24
+
25
+ send(`Всього растрів: ${rasters.length || '0'}`);
26
+
27
+ await rasters.reduce(
28
+ (promise, row, idx) => promise.then(async () => {
29
+ const uploadStatus = path.extname(row.source_path) ? await GetRasterStatus({ path: row.source_path }) : {};
30
+
31
+ if (uploadStatus.exists === false) {
32
+ errors.raster += 1;
33
+ send(`${idx}/${rasters.length} ERROR: GetRasterStatus: not uploaded (raster:${row.raster_id}/${row.name})`);
34
+ return null;
35
+ }
36
+
37
+ if (uploadStatus.finished === false) {
38
+ errors.raster += 1;
39
+ send(`${idx}/${rasters.length} ERROR: GetRasterStatus: upload not finished (raster:${row.raster_id}/${row.name})`);
40
+ return null;
41
+ }
42
+
43
+ const rasterInfo = await GetRasterInfo({
44
+ path: row.source_path,
45
+ }).catch(err => ({ err }));
46
+
47
+ if (rasterInfo.err) {
48
+ errors.raster += 1;
49
+ send(`${idx}/${rasters.length} ERROR: GetRasterInfo: ${rasterInfo.err} (raster:${row.raster_id}/${row.name})`);
50
+ return null;
51
+ }
52
+
53
+ const data = await RenderTile({
54
+ path: row.source_path,
55
+ width: 256,
56
+ bbox: [3713463.7081504324, 6088362.176970857, 3713616.5822070027, 6088515.051027427], // /:id/18/155363/91245.png
57
+ ttl: '0',
58
+ }).catch(err => ({ err: err.toString() }));
59
+
60
+ if (data.err) {
61
+ errors.raster += 1;
62
+ send(`${idx}/${rasters.length} ERROR: RenderTile: ${data.err} (raster:${row.raster_id}/${row.name})`);
63
+ return null;
64
+ }
65
+
66
+ send(`${idx}/${rasters.length} OK: (raster:${row.raster_id}/${row.name})`);
67
+ successCount += 1;
68
+ }),
69
+ Promise.resolve(),
70
+ );
71
+
72
+ send(`Всього CSS: ${css.length || '0'}`);
73
+
74
+ await css.reduce(
75
+ (promise, row, idx) => promise.then(async () => {
76
+ const data = await RenderTile({
77
+ path: row.source_path,
78
+ name: row.source_path ? undefined : row.cartocss_id,
79
+ width: 256,
80
+ bbox: [3713463.7081504324, 6088362.176970857, 3713616.5822070027, 6088515.051027427], // /:id/18/155363/91245.png
81
+ ttl: '0',
82
+ }).catch(err => ({ err: err.toString() }));
83
+
84
+ if (data.err) {
85
+ errors.css += 1;
86
+ send(`${idx}/${css.length} ERROR: RenderTile: ${data.err} (css:${row.cartocss_id}/${row.name})`);
87
+ return null;
88
+ }
89
+
90
+ send(`${idx}/${css.length} OK: (css:${row.cartocss_id}/${row.name})`);
91
+ successCount += 1;
92
+ }),
93
+ Promise.resolve(),
94
+ );
95
+
96
+ send(`Всього наборів: ${dataset.length || '0'}`);
97
+ // ? place dataset check logic here
98
+
99
+ const errorCount = Object.keys(errors).reduce((acc, curr) => acc + errors[curr], 0);
100
+ send(`Всього помилок: ${errorCount || '0'}`);
101
+ send(`Помилки растрів: ${errors.raster || '0'}`);
102
+ send(`Помилки CSS: ${errors.css || '0'}`);
103
+ send(`Помилки наборів: ${errors.dataset || '0'}`);
104
+
105
+ send(`Успіх: ${successCount || '0'}/${total || '0'}`);
106
+
107
+ }
@@ -0,0 +1,24 @@
1
+ import { pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function getDashboard({ pg = pgClients.client }) {
4
+
5
+ const statsQuery = `
6
+ SELECT
7
+ (SELECT COUNT(*)::int FROM gis.services) as services_count,
8
+ (SELECT COUNT(*)::int FROM gis.registers) as registers_count,
9
+ (SELECT COUNT(*)::int FROM gis.maps) as maps_count,
10
+ (SELECT COUNT(*)::int FROM gis.ogc_service) as ogc_count
11
+ `;
12
+
13
+ const stats = await pg.query(statsQuery).then(r => r.rows?.[0] || {});
14
+
15
+ return {
16
+ cards: {
17
+ services: stats.services_count || 0,
18
+ registers: stats.registers_count || 0,
19
+ maps: stats.maps_count || 0,
20
+ ogc: stats.ogc_count || 0,
21
+ },
22
+ };
23
+
24
+ }
@@ -0,0 +1,26 @@
1
+ import getDashboard from './functions/dashboard.js';
2
+ import checkXML from './functions/checkXML.js';
3
+
4
+ import { logger, eventStream, pgClients } from '@opengis/fastify-table/utils.js';
5
+
6
+ const privateParams = { config: { policy: 'L1', package: 'gis' } };
7
+ const publicParams = { config: { policy: 'L0', package: 'gis' } };
8
+
9
+ async function route(app) {
10
+ app.get('/gis-check', privateParams, async (req, reply) => {
11
+ const { pg = pgClients.client } = req;
12
+ const callback = process.env.NODE_ENV === 'test' || process.env.VITEST
13
+ ? console.log
14
+ : eventStream(reply);
15
+ try {
16
+ await checkXML(pg, callback);
17
+ } catch (err) {
18
+ logger.error(err);
19
+ callback(process.env.local === 'true' ? err.toString() : 'check error');
20
+ } finally {
21
+ callback('finish', true);
22
+ }
23
+ });
24
+ app.get('/dashboard', publicParams, getDashboard); // no params required
25
+ }
26
+ export default route;
@@ -0,0 +1,34 @@
1
+ import { beforeAll, describe, expect, test } from 'vitest';
2
+
3
+ import { build, injectWithHeaders } from '../../../test/helper.js';
4
+
5
+ const user = { id: '2', type: 'admin' };
6
+
7
+ beforeAll(async () => {
8
+ await build();
9
+ });
10
+
11
+ describe('GIS routes', () => {
12
+ test('GET /gis-check returns 200', async () => {
13
+ const res = await injectWithHeaders({
14
+ url: '/api/gis-check',
15
+ headers: { uid: user.id, user_type: user.type }
16
+ });
17
+
18
+ // event stream
19
+ expect(res.statusCode).toBe(200);
20
+ });
21
+
22
+ test('GET /dashboard returns 200', async () => {
23
+ const res = await injectWithHeaders({
24
+ url: '/api/dashboard',
25
+ });
26
+
27
+ expect(res.statusCode).toBe(200);
28
+ expect(res.body).toHaveProperty('cards');
29
+ expect(res.body.cards).toHaveProperty('services');
30
+ expect(res.body.cards).toHaveProperty('registers');
31
+ expect(res.body.cards).toHaveProperty('maps');
32
+ expect(res.body.cards).toHaveProperty('ogc');
33
+ });
34
+ });
@@ -0,0 +1 @@
1
+ export default null;
@@ -5,14 +5,15 @@ import {
5
5
  } from 'node:fs/promises';
6
6
 
7
7
  import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
8
+ import { BadRequestError, NotFoundError } from '@opengis/fastify-table/errors.js';
8
9
 
9
10
  export default async function clearTiles({
10
- params, query, user,
11
- }, reply) {
12
- const filepath = path.join(rootFolder, 'vtile', params.id);
11
+ id, debug,
12
+ }) {
13
+ const filepath = path.join(rootFolder, 'vtile', id);
13
14
 
14
- if (query.debug && user?.user_type?.includes?.('admin')) {
15
- return filepath;
15
+ if (debug) {
16
+ return { filepath };
16
17
  }
17
18
 
18
19
  const exists = existsSync(filepath);
@@ -21,14 +22,14 @@ export default async function clearTiles({
21
22
  const count = isDirectory ? await readdir(filepath).then(el => el.length) : 0;
22
23
 
23
24
  if (isDirectory && count === 0) {
24
- return reply.status(404).send({ error: 'Тайли не знайдено', code: 404 });
25
+ throw NotFoundError('Тайли не знайдено');
25
26
  }
26
27
 
27
28
  if (isDirectory && exists) {
28
29
  await rm(filepath, { force: true, recursive: true });
29
30
  await mkdir(filepath, { recursive: true });
30
- return reply.status(200).send({ message: 'Тайли очищено', code: 200 });
31
+ return { message: 'Тайли очищено', code: 200 };
31
32
  }
32
33
 
33
- return reply.status(400).send({ error: 'Некорректний параметр: id', code: 400 });
34
+ throw BadRequestError('Некорректний параметр: id');
34
35
  }
@@ -10,9 +10,12 @@ import {
10
10
  } from '@opengis/fastify-table/utils.js';
11
11
 
12
12
  import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
13
+ import { BadRequestError, NotFoundError } from '@opengis/fastify-table/errors.js';
13
14
 
14
15
  const systemColumns = ['uid', 'cdate', 'editor_id', 'editor_date', 'created_by', 'created_at', 'updated_by', 'updated_at'];
15
16
 
17
+ const unittest = process.env.NODE_ENV === 'test' || process.env.VITEST;
18
+
16
19
  /**
17
20
  * Повертає GeoJSON для сервісу на основі налаштувань з gis.services
18
21
  *
@@ -21,20 +24,18 @@ const systemColumns = ['uid', 'cdate', 'editor_id', 'editor_date', 'created_by',
21
24
  * @param {string} query.sql - повернути SQL запит замість даних
22
25
  * @param {boolean} query.nocache - не використовувати кеш
23
26
  */
24
- export default async function geojson({
25
- pg = pgClients.client, params = {}, query = {}, unittest,
26
- }, reply) {
27
- const { layer } = params;
28
- const { sql, nocache, srid, type, extent } = query;
29
27
 
28
+ export default async function geojson({
29
+ layer, sql, nocache, srid, type, extent,
30
+ }, pg = pgClients.client) {
30
31
  if (!layer) {
31
- return reply.status(400).send('not enough params: layer (service_id)');
32
+ throw BadRequestError('not enough params: layer (service_id)');
32
33
  }
33
34
 
34
35
  const serviceId = layer.replace('.json', '');
35
36
 
36
37
  if (!pg.pk?.['gis.services']) {
37
- return reply.status(404).send('services table not found');
38
+ throw NotFoundError('services table not found');
38
39
  }
39
40
 
40
41
  const service = await pg.query(
@@ -50,10 +51,10 @@ export default async function geojson({
50
51
  from gis.services
51
52
  where service_id = $1 and is_active = true and is_public = true`,
52
53
  [serviceId],
53
- ).then(el => el.rows?.[0]);
54
+ ).then(r => r.rows?.[0]);
54
55
 
55
56
  if (!service) {
56
- return reply.status(404).send('service not found or not available');
57
+ throw NotFoundError('service not found or not available');
57
58
  }
58
59
 
59
60
  const {
@@ -66,20 +67,17 @@ export default async function geojson({
66
67
  } = service;
67
68
 
68
69
  if (!source) {
69
- return reply.status(400).send('invalid service source path');
70
+ throw BadRequestError('invalid service source path');
70
71
  }
71
72
 
72
- const table = source;
73
-
74
- if (!pg.pk?.[table]?.length) {
75
- return reply.status(404).send('layer table not found');
73
+ if (!pg.pk?.[source]?.length) {
74
+ throw NotFoundError('layer table not found');
76
75
  }
77
76
 
78
- const meta = await getMeta({ pg, table });
79
- const { columns = [] } = meta || {};
77
+ const { columns = [], geom: metaGeom } = await getMeta({ pg, table: source }) || {};
80
78
 
81
- const geom = gc || meta?.geom || 'geom';
82
- const pk = pg.pk?.[table] || columns[0]?.name;
79
+ const geom = gc || metaGeom || 'geom';
80
+ const pk = pg.pk?.[source] || columns[0]?.name;
83
81
 
84
82
  let attributeColumns = [];
85
83
  if (attributes && Array.isArray(attributes)) {
@@ -113,11 +111,14 @@ export default async function geojson({
113
111
  const ageInMs = Date.now() - birthtimeMs;
114
112
  if (ageInMs < 86400000) {
115
113
  const content = await readFile(filepath, 'utf8');
116
- return reply
117
- .status(200)
118
- .header('Content-Type', 'application/json')
119
- .header('Cache-Control', 'public, max-age=86400')
120
- .send(content);
114
+
115
+ return {
116
+ headers: {
117
+ 'Content-Type': 'application/json',
118
+ 'Cache-Control': 'public, max-age=86400',
119
+ },
120
+ data: content
121
+ }
121
122
  }
122
123
  }
123
124
 
@@ -136,7 +137,7 @@ export default async function geojson({
136
137
  geomExpression = `st_transform(${geomExpression}, 4326)`;
137
138
  }
138
139
 
139
- const quotedTable = table.split('.').map(el => `"${el}"`).join('.');
140
+ const quotedTable = source.split('.').map(el => `"${el}"`).join('.');
140
141
  const quotedGeom = `"${geom}"`;
141
142
 
142
143
  const geomFinal = `st_force2d(${geomExpression.replace(new RegExp(`(\\b)${geom}(\\b)`, 'g'), `$1${quotedGeom}$2`)})`;
@@ -159,29 +160,28 @@ export default async function geojson({
159
160
  ) f
160
161
  `;
161
162
 
162
- if (sql === '1') return q;
163
- if (sql === '2') return filepath.replace(/\\/g, '/');
163
+ if (sql === 1) return q;
164
+ if (sql === 2) return filepath.replace(/\\/g, '/');
164
165
 
165
- try {
166
- const result = await pg.query(q);
167
- const geojsonData = result.rows[0] || { type: 'FeatureCollection', features: [] };
168
166
 
169
- geojsonData.service_id = serviceId;
170
- geojsonData.count = geojsonData.features?.length || 0;
167
+ const geojsonData = await pg.query(q)
168
+ .then(r => r.rows?.[0] || { type: 'FeatureCollection', features: [] });
171
169
 
172
- const jsonString = JSON.stringify(geojsonData);
170
+ geojsonData.service_id = serviceId;
171
+ geojsonData.count = geojsonData.features?.length || 0;
173
172
 
174
- if (!nocache && !unittest) {
175
- await mkdir(path.dirname(filepath), { recursive: true });
176
- await writeFile(filepath, jsonString, 'utf8');
177
- }
173
+ const jsonString = JSON.stringify(geojsonData);
174
+
175
+ if (!nocache && !unittest) {
176
+ await mkdir(path.dirname(filepath), { recursive: true });
177
+ await writeFile(filepath, jsonString, 'utf8');
178
+ }
178
179
 
179
- return reply
180
- .status(200)
181
- .header('Content-Type', 'application/json')
182
- .header('Cache-Control', nocache ? 'no-cache' : 'public, max-age=86400')
183
- .send(jsonString);
184
- } catch (err) {
185
- return reply.status(500).send({ error: err.message });
180
+ return {
181
+ headers: {
182
+ 'Content-Type': 'application/json',
183
+ 'Cache-Control': nocache ? 'no-cache' : 'public, max-age=86400',
184
+ },
185
+ data: jsonString
186
186
  }
187
187
  }
@@ -1,6 +1,8 @@
1
1
  import path from "node:path";
2
2
 
3
3
  import { downloadFile } from "@opengis/fastify-table/utils.js";
4
+ import { BadRequestError, ConflictError, NotFoundError } from "@opengis/fastify-table/errors.js";
5
+
4
6
  import mapnik from "../../../plugins/mapnik/funcs/mapnik.js";
5
7
 
6
8
  const { ConvertVector } = mapnik();
@@ -20,41 +22,35 @@ function resolveExtension(filename) {
20
22
  * @body {string} [filepath] - шлях до файлу на сервері (legacy)
21
23
  * @body {number} [srid=4326] - цільовий EPSG код
22
24
  */
23
- export default async function gisConvert({ body = {} }, reply) {
24
- const { file, filename, filepath, srid = 4326, encoding } = body;
25
-
25
+ export default async function gisConvert({ file, filename, filepath, encoding }) {
26
26
  let data;
27
27
  let sourceName;
28
28
 
29
29
  if (typeof file === "string" && file) {
30
30
  if (!filename || typeof filename !== "string") {
31
- return reply
32
- .status(400)
33
- .send({ error: "Потрібен параметр filename разом з file", code: 400 });
31
+ throw BadRequestError("Потрібен параметр filename разом з file");
34
32
  }
33
+
35
34
  data = Buffer.from(file, "base64");
36
35
  sourceName = path.basename(filename);
37
36
  } else if (typeof filepath === "string" && filepath) {
37
+ const filepath1 = filepath.replace(/%2e/g, ".").replace(/%2E/g, ".");
38
+ if (filepath1.includes('..')) {
39
+ throw ConflictError('path traversals are not allowed');
40
+ }
38
41
  data = await downloadFile(filepath, { buffer: true });
39
42
  if (!data) {
40
- return reply
41
- .status(404)
42
- .send({ error: `Файл не знайдено: ${filepath}`, code: 404 });
43
+ throw NotFoundError(`Файл не знайдено: ${filepath}`);
43
44
  }
44
45
  sourceName = path.basename(filepath);
45
46
  } else {
46
- return reply.status(400).send({
47
- error: "Потрібен параметр file (base64) або filepath",
48
- code: 400,
49
- });
47
+ throw BadRequestError("Потрібен параметр file (base64) або filepath");
50
48
  }
51
49
 
52
50
  const ext = resolveExtension(sourceName);
51
+
53
52
  if (!ALLOWED_FORMAT.has(ext)) {
54
- return reply.status(400).send({
55
- error: `Непідтримуване розширення: .${ext}. Підтримується: ${[...ALLOWED_FORMAT].join(", ")}`,
56
- code: 400,
57
- });
53
+ throw BadRequestError(`Непідтримуване розширення: .${ext}. Підтримується: ${[...ALLOWED_FORMAT].join(", ")}`);
58
54
  }
59
55
 
60
56
  const res = await ConvertVector({
@@ -63,11 +59,7 @@ export default async function gisConvert({ body = {} }, reply) {
63
59
  // srs: +srid, // target srs, 4326 by default
64
60
  encoding,
65
61
  filename: sourceName,
66
- }).catch((err) => ({ err }));
67
-
68
- if (res?.err) {
69
- return reply.code(500).send(res.err);
70
- }
62
+ });
71
63
 
72
- return reply.send(JSON.parse(res.geojson));
64
+ return JSON.parse(res.geojson);
73
65
  }