@opengis/gis 0.2.161 → 0.2.162
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/README.md +181 -181
- package/dist/{CardIcon-CJb7yqV7.js → CardIcon-DcriKHRQ.js} +1 -1
- package/dist/{EntityTablePage-BDWJBRsX.js → EntityTablePage-44UmAQh0.js} +2 -2
- package/dist/{ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-BSfb8eHZ.js → ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-BAaobvwj.js} +1 -1
- package/dist/{HeaderActions.vue_vue_type_script_setup_true_lang-WHe_VrVa.js → HeaderActions.vue_vue_type_script_setup_true_lang-CLEpHbVL.js} +1 -1
- package/dist/{MapSettings-CfY1C4pP.js → MapSettings-C7qDO9gp.js} +2 -2
- package/dist/{RastersTablePage-DqNAAgwQ.js → RastersTablePage-DmOpbseA.js} +2 -2
- package/dist/{cartocss-BtW3JOe4.js → cartocss-DGvNrARb.js} +5 -5
- package/dist/{import-utils-omyzQAkY.js → import-utils-DYDa34GH.js} +1 -1
- package/dist/{index-C8Wi_h72.js → index-DCKH-TcG.js} +39 -39
- package/dist/index.css +1 -1
- package/dist/index.js +1 -1
- package/dist/index.umd.cjs +5 -5
- package/dist/{raster-C18YvUia.js → raster-CAi6K97q.js} +3 -3
- package/dist/{register-YfpjSpQO.js → register-C6IbQrEK.js} +3 -3
- package/dist/{service-CoVcnlm3.js → service-B_C_D5nN.js} +6 -6
- package/dist/{vs-datatable-D6oj_pBG.js → vs-datatable-aAXTRfDg.js} +2 -2
- package/module/cls.json +6 -6
- package/module/gis/card/gis.metadata.table/index.yml +22 -22
- package/module/gis/card/gis.metadata.table/main_info.hbs +20 -20
- package/module/gis/card/gis.metadata.table/metadata_info.hbs +27 -27
- package/module/gis/card/gis.metadata.table/other.hbs +25 -25
- package/module/gis/card/gis.rasters.table/index.yml +11 -11
- package/module/gis/card/gis.rasters.table/main_info.hbs +27 -27
- package/module/gis/card/gis.registers.table/cls.hbs +36 -36
- package/module/gis/card/gis.registers.table/columns.hbs +89 -89
- package/module/gis/card/gis.registers.table/filters.hbs +80 -80
- package/module/gis/card/gis.registers.table/index.yml +23 -23
- package/module/gis/card/gis.registers.table/main_info.hbs +35 -35
- package/module/gis/card/gis.registers.table/source.hbs +45 -45
- package/module/gis/card/gis.services.table/attributes.hbs +91 -91
- package/module/gis/card/gis.services.table/filters.hbs +83 -83
- package/module/gis/card/gis.services.table/index.yml +25 -25
- package/module/gis/card/gis.services.table/main_info.hbs +27 -27
- package/module/gis/card/gis.services.table/source.hbs +25 -25
- package/module/gis/cls/bool.yes_no.json +12 -12
- package/module/gis/cls/encoding.json +14 -14
- package/module/gis/cls/geom_type.json +14 -14
- package/module/gis/cls/gis.column_type.json +34 -34
- package/module/gis/cls/gis.column_view_type.json +26 -26
- package/module/gis/cls/gis.filter_type.json +22 -22
- package/module/gis/cls/language.json +10 -10
- package/module/gis/cls/meta.service_type.json +42 -42
- package/module/gis/cls/ogc.service.json +21 -21
- package/module/gis/cls/service_type.json +42 -42
- package/module/gis/cls/source_type.json +10 -10
- package/module/gis/cls/standarts.json +6 -6
- package/module/gis/cls/topic_category.json +106 -106
- package/module/gis/cls/update_frequency.json +29 -29
- package/module/gis/cls/view.json +21 -21
- package/module/gis/form/feedback.answer.form.json +10 -10
- package/module/gis/form/gis.cartocss.form.json +64 -64
- package/module/gis/form/gis.group_list.form.json +17 -17
- package/module/gis/form/gis.maps.form.json +61 -61
- package/module/gis/form/gis.metadata.form.json +240 -240
- package/module/gis/form/gis.metadata_new.form.json +195 -195
- package/module/gis/form/gis.ogc_service.form.json +51 -51
- package/module/gis/form/gis.rasters.form.json +76 -76
- package/module/gis/form/gis.registers.form.json +124 -124
- package/module/gis/form/gis.registers_column.form.json +84 -84
- package/module/gis/form/gis.registers_filter.form.json +65 -65
- package/module/gis/form/gis.services.form.json +111 -111
- package/module/gis/form/gis.services_attributes.form.json +75 -75
- package/module/gis/form/gis.services_filter.form.json +65 -65
- package/module/gis/menu.json +43 -43
- package/module/gis/select/admin.cls.name.sql +9 -9
- package/module/gis/select/layer_list.sql +7 -7
- package/module/gis/select/layer_list_text.sql +7 -7
- package/module/gis/select/pg.column_name.geometry.sql +12 -12
- package/module/gis/select/pg.columns.parent.sql +6 -6
- package/module/gis/select/pg.table_name.sql +17 -17
- package/module/gis/select/service_id.sql +1 -1
- package/module/gis/select/srid.sql +7 -7
- package/module/gis/table/gis.cartocss.table.json +74 -74
- package/module/gis/table/gis.group_list.table.json +58 -58
- package/module/gis/table/gis.maps.table.json +110 -110
- package/module/gis/table/gis.metadata.table.json +70 -70
- package/module/gis/table/gis.metadata_new.table.json +137 -137
- package/module/gis/table/gis.ogc_service.table.json +100 -100
- package/module/gis/table/gis.rasters.table.json +113 -113
- package/module/gis/table/gis.registers.table.json +143 -143
- package/module/gis/table/gis.services.table.json +120 -120
- package/module/gis/table/site.gis.registers.table.json +88 -88
- package/module/gis/table/site.gis.services.table.json +106 -106
- package/module/gis/templates/ISO19136_2017_gml_template.xml +330 -330
- package/module/gis/tokens.yml +5 -5
- package/module/permissions/form/permissions.users.form.json +151 -151
- package/module/permissions/table/gis.permissions.table.json +97 -97
- package/module/test/cls/bp_build_type.json +37 -37
- package/module/test/cls/doc_status.json +31 -31
- package/module/test/cls/ts.temp_status.json +18 -18
- package/module/test/cls/ts.temp_structure.ts_class.json +49 -49
- package/module/test/cls/ts.temp_type.json +9 -9
- package/module/test/layer/bp.json +59 -59
- package/module/test/layer/bp1.yml +33 -33
- package/module/test/layer/individual.yml +53 -53
- package/module/test/layer/ts.linking_passport.yml +55 -55
- package/module/test/layer/ts.temp_structure.yml +50 -50
- package/module/test/map/addr.yml +21 -21
- package/module/test/map/bp_myo.json +43 -43
- package/module/test/map/bpf.json +43 -43
- package/module/test/map/main.json +39 -39
- package/module/test/map/mbd.json +92 -92
- package/module/test/map/ts.json +53 -53
- package/module/test/select/address_id.json +2 -2
- package/module/test/select/address_id.sql +7 -7
- package/module/test/select/core.user_uid.sql +1 -1
- package/module/test/table/data_address.street.table.json +69 -69
- package/module/test/table/data_bp_myo.bp.table.json +122 -122
- package/package.json +75 -75
- package/plugin.js +55 -55
- package/server/migrations/array_intersect.sql +13 -13
- package/server/migrations/cartocss.sql +27 -27
- package/server/migrations/group_list.sql +74 -74
- package/server/migrations/maps.sql +30 -30
- package/server/migrations/metadata.sql +415 -415
- package/server/migrations/ogc.sql +106 -106
- package/server/migrations/rasters.sql +265 -265
- package/server/migrations/services.sql +247 -247
- package/server/migrations/services_users_rel.sql +22 -22
- package/server/migrations/widgets.sql +20 -20
- package/server/plugins/crons.js +21 -21
- package/server/plugins/mapnik/funcs/checkRasterFile.js +109 -109
- package/server/plugins/mapnik/funcs/mapnik.js +151 -151
- package/server/plugins/mapnik/funcs/rootFolder.mjs +8 -8
- package/server/plugins/mapnik/map.proto +437 -345
- package/server/plugins/vite.js +74 -74
- package/server/routes/gis/cartocss/add.cartocss.js +42 -42
- package/server/routes/gis/cartocss/get.cartocss.js +59 -59
- package/server/routes/gis/checkXML.js +117 -117
- package/server/routes/gis/dashboard.js +29 -29
- package/server/routes/gis/index.mjs +92 -92
- package/server/routes/gis/metadata/editMetadata.js +34 -34
- package/server/routes/gis/metadata/metadataXML.js +13 -13
- package/server/routes/gis/ogc/map.info.point.js +124 -124
- package/server/routes/gis/registers/add.registry.js +35 -35
- package/server/routes/gis/registers/del.registry.js +14 -14
- package/server/routes/gis/registers/funcs/classifiers.js +100 -100
- package/server/routes/gis/registers/funcs/columns.js +3 -3
- package/server/routes/gis/registers/funcs/content.type.js +9 -9
- package/server/routes/gis/registers/funcs/get.info.js +89 -89
- package/server/routes/gis/registers/funcs/handleRegistryRequest.js +146 -146
- package/server/routes/gis/registers/gis.export.js +153 -153
- package/server/routes/gis/registers/gis.registry.js +64 -64
- package/server/routes/gis/registers/gis.registry.list.js +59 -59
- package/server/routes/gis/registers/insert.columns.js +107 -107
- package/server/routes/gis/registers/insert.filters.js +110 -110
- package/server/routes/gis/registers/map.registry.js +79 -79
- package/server/routes/gis/services/add.service.js +64 -64
- package/server/routes/gis/services/create.route.js +96 -96
- package/server/routes/gis/services/del.service.js +12 -12
- package/server/routes/gis/services/get.layer.geom.js +27 -27
- package/server/routes/gis/services/get.service.users.js +74 -74
- package/server/routes/gis/services/get.services.col.js +33 -33
- package/server/routes/gis/services/get.services.js +84 -84
- package/server/routes/gis/services/legend.auto.js +77 -77
- package/server/routes/map/controllers/clearTiles.js +34 -34
- package/server/routes/map/controllers/geojson.js +187 -187
- package/server/routes/map/controllers/gis.convert.js +54 -53
- package/server/routes/map/controllers/jsonData.js +205 -205
- package/server/routes/map/controllers/layerList.js +102 -102
- package/server/routes/map/controllers/map.js +123 -123
- package/server/routes/map/controllers/mapCatalog.js +72 -72
- package/server/routes/map/controllers/mapCatalogAttribute.js +55 -55
- package/server/routes/map/controllers/mapFeatures.js +128 -128
- package/server/routes/map/controllers/mapFormat.js +234 -234
- package/server/routes/map/controllers/mapTiles.js +152 -152
- package/server/routes/map/controllers/maps.js +15 -15
- package/server/routes/map/controllers/marker_icon.js +43 -43
- package/server/routes/map/controllers/parseGeoFile.js +86 -86
- package/server/routes/map/controllers/pointHeight.js +55 -55
- package/server/routes/map/controllers/vtile.js +172 -172
- package/server/routes/map/index.mjs +249 -228
- package/server/routes/map/maps/add.map.js +43 -43
- package/server/routes/map/maps/del.map.js +18 -18
- package/server/routes/map/maps/get.map.js +118 -118
- package/server/routes/map/vtile1.js +179 -179
- package/server/routes/map/widgets/add.widget.js +38 -38
- package/server/routes/map/widgets/del.widget.js +22 -22
- package/server/routes/map/widgets/get.widget.js +40 -40
- package/server/routes/mapnik/controllers/clearTiles.js +106 -106
- package/server/routes/mapnik/controllers/createXml.js +67 -67
- package/server/routes/mapnik/controllers/createXmlMulti.js +76 -76
- package/server/routes/mapnik/controllers/fileSearch.js +38 -38
- package/server/routes/mapnik/controllers/fileStat.js +28 -28
- package/server/routes/mapnik/controllers/mapnikLogger.js +29 -29
- package/server/routes/mapnik/controllers/mapnikStat.js +19 -19
- package/server/routes/mapnik/controllers/pretile.js +144 -144
- package/server/routes/mapnik/controllers/rasterInfo.js +57 -57
- package/server/routes/mapnik/controllers/readDir.js +22 -22
- package/server/routes/mapnik/controllers/rtile.js +167 -167
- package/server/routes/mapnik/controllers/uploadRaster.js +157 -157
- package/server/routes/mapnik/functions/cartoBounds.js +32 -32
- package/server/routes/mapnik/functions/convertBoundToTile.js +27 -27
- package/server/routes/mapnik/functions/uploadXML.js +113 -113
- package/server/routes/mapnik/index.js +40 -40
- package/server/routes/permissions/controllers/catalog.permissions.edit.js +22 -22
- package/server/routes/permissions/controllers/catalog.permissions.js +7 -7
- package/server/routes/permissions/controllers/gis.catalog.js +80 -80
- package/server/routes/permissions/controllers/utils/get.permissions.js +59 -59
- package/server/routes/permissions/index.mjs +18 -18
- package/server/routes/root.mjs +3 -3
- package/utils.js +13 -13
|
@@ -1,53 +1,54 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
const { ConvertVector } = mapnik();
|
|
7
|
-
|
|
8
|
-
const ALLOWED_FORMAT = new Set(["zip", "shp", "kml", "gml", "geojson", "json"]);
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Конвертує векторний файл на сервері у GeoJSON через mapnik ConvertVector.
|
|
12
|
-
*
|
|
13
|
-
* @method POST /gis-convert
|
|
14
|
-
* @body {string} filepath - шлях до файлу на сервері
|
|
15
|
-
* @body {number} [srid=4326] - цільовий EPSG код
|
|
16
|
-
*/
|
|
17
|
-
export default async function gisConvert({ body = {} }, reply) {
|
|
18
|
-
const { filepath, srid = 4326 } = body;
|
|
19
|
-
|
|
20
|
-
if (!filepath || typeof filepath !== "string") {
|
|
21
|
-
return reply
|
|
22
|
-
.status(400)
|
|
23
|
-
.send({ error: "Потрібен параметр filepath", code: 400 });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const ext = path.extname(filepath).replace(".", "").toLowerCase();
|
|
27
|
-
if (!ALLOWED_FORMAT.has(ext)) {
|
|
28
|
-
return reply.status(400).send({
|
|
29
|
-
error: `Непідтримуване розширення: .${ext}. Підтримується: ${[...ALLOWED_FORMAT].join(", ")}`,
|
|
30
|
-
code: 400,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const data = await downloadFile(filepath, { buffer: true });
|
|
35
|
-
if (!data) {
|
|
36
|
-
return reply
|
|
37
|
-
.status(404)
|
|
38
|
-
.send({ error: `Файл не знайдено: ${filepath}`, code: 404 });
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const res = await ConvertVector({
|
|
42
|
-
data,
|
|
43
|
-
format: ext,
|
|
44
|
-
srs: +srid,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import { downloadFile } from "@opengis/fastify-table/utils.js";
|
|
4
|
+
import mapnik from "../../../plugins/mapnik/funcs/mapnik.js";
|
|
5
|
+
|
|
6
|
+
const { ConvertVector } = mapnik();
|
|
7
|
+
|
|
8
|
+
const ALLOWED_FORMAT = new Set(["zip", "shp", "kml", "gml", "geojson", "json"]);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Конвертує векторний файл на сервері у GeoJSON через mapnik ConvertVector.
|
|
12
|
+
*
|
|
13
|
+
* @method POST /gis-convert
|
|
14
|
+
* @body {string} filepath - шлях до файлу на сервері
|
|
15
|
+
* @body {number} [srid=4326] - цільовий EPSG код
|
|
16
|
+
*/
|
|
17
|
+
export default async function gisConvert({ body = {} }, reply) {
|
|
18
|
+
const { filepath, srid = 4326, encoding } = body;
|
|
19
|
+
|
|
20
|
+
if (!filepath || typeof filepath !== "string") {
|
|
21
|
+
return reply
|
|
22
|
+
.status(400)
|
|
23
|
+
.send({ error: "Потрібен параметр filepath", code: 400 });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ext = path.extname(filepath).replace(".", "").toLowerCase();
|
|
27
|
+
if (!ALLOWED_FORMAT.has(ext)) {
|
|
28
|
+
return reply.status(400).send({
|
|
29
|
+
error: `Непідтримуване розширення: .${ext}. Підтримується: ${[...ALLOWED_FORMAT].join(", ")}`,
|
|
30
|
+
code: 400,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const data = await downloadFile(filepath, { buffer: true });
|
|
35
|
+
if (!data) {
|
|
36
|
+
return reply
|
|
37
|
+
.status(404)
|
|
38
|
+
.send({ error: `Файл не знайдено: ${filepath}`, code: 404 });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const res = await ConvertVector({
|
|
42
|
+
data,
|
|
43
|
+
format: ext,
|
|
44
|
+
srs: +srid,
|
|
45
|
+
encoding,
|
|
46
|
+
filename: path.basename(filepath),
|
|
47
|
+
}).catch((err) => ({ err }));
|
|
48
|
+
|
|
49
|
+
if (res?.err) {
|
|
50
|
+
return reply.code(500).send(res.err);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return reply.send(JSON.parse(res.geojson));
|
|
54
|
+
}
|
|
@@ -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
|
+
}
|