ваше сообщение коммита
This commit is contained in:
@@ -17,6 +17,8 @@ const identitiesRoutes = require('./routes/identities');
|
||||
const chatRoutes = require('./routes/chat');
|
||||
const adminRoutes = require('./routes/admin');
|
||||
const tokensRouter = require('./routes/tokens');
|
||||
const isicRoutes = require('./routes/isic'); // Добавленный импорт
|
||||
const geocodingRoutes = require('./routes/geocoding'); // Добавленный импорт
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -114,6 +116,8 @@ app.use('/api/identities', identitiesRoutes);
|
||||
app.use('/api/chat', chatRoutes);
|
||||
app.use('/api/admin', adminRoutes);
|
||||
app.use('/api/tokens', tokensRouter);
|
||||
app.use('/api/isic', isicRoutes); // Добавленное использование роута
|
||||
app.use('/api/geocoding', geocodingRoutes); // Добавленное использование роута
|
||||
|
||||
const nonceStore = new Map(); // или любая другая реализация хранилища nonce
|
||||
|
||||
|
||||
5
backend/db/data/isic_level_names.csv
Normal file
5
backend/db/data/isic_level_names.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
CodeLevel,LevelName
|
||||
1,"Section"
|
||||
2,"Division"
|
||||
3,"Group"
|
||||
4,"Class"
|
||||
|
767
backend/db/data/isic_structure.csv
Normal file
767
backend/db/data/isic_structure.csv
Normal file
@@ -0,0 +1,767 @@
|
||||
Sortorder,Code,CodeLevel,Level1,Level2,Level3,Level4,Level5,Level6
|
||||
20,"A",1,"A",,,,,
|
||||
30,"01",2,"A","01",,,,
|
||||
40,"011",3,"A","01","011",,,
|
||||
50,"0111",4,"A","01","011","0111",,
|
||||
60,"0112",4,"A","01","011","0112",,
|
||||
70,"0113",4,"A","01","011","0113",,
|
||||
80,"0114",4,"A","01","011","0114",,
|
||||
90,"0115",4,"A","01","011","0115",,
|
||||
110,"0116",4,"A","01","011","0116",,
|
||||
130,"0119",4,"A","01","011","0119",,
|
||||
140,"012",3,"A","01","012",,,
|
||||
150,"0121",4,"A","01","012","0121",,
|
||||
160,"0122",4,"A","01","012","0122",,
|
||||
170,"0123",4,"A","01","012","0123",,
|
||||
180,"0124",4,"A","01","012","0124",,
|
||||
190,"0125",4,"A","01","012","0125",,
|
||||
200,"0126",4,"A","01","012","0126",,
|
||||
210,"0127",4,"A","01","012","0127",,
|
||||
220,"0128",4,"A","01","012","0128",,
|
||||
230,"0129",4,"A","01","012","0129",,
|
||||
240,"013",3,"A","01","013",,,
|
||||
250,"0130",4,"A","01","013","0130",,
|
||||
260,"014",3,"A","01","014",,,
|
||||
270,"0141",4,"A","01","014","0141",,
|
||||
280,"0142",4,"A","01","014","0142",,
|
||||
290,"0143",4,"A","01","014","0143",,
|
||||
300,"0144",4,"A","01","014","0144",,
|
||||
310,"0145",4,"A","01","014","0145",,
|
||||
320,"0146",4,"A","01","014","0146",,
|
||||
330,"0149",4,"A","01","014","0149",,
|
||||
340,"015",3,"A","01","015",,,
|
||||
350,"0150",4,"A","01","015","0150",,
|
||||
360,"016",3,"A","01","016",,,
|
||||
370,"0161",4,"A","01","016","0161",,
|
||||
380,"0162",4,"A","01","016","0162",,
|
||||
390,"0163",4,"A","01","016","0163",,
|
||||
400,"0164",4,"A","01","016","0164",,
|
||||
410,"017",3,"A","01","017",,,
|
||||
420,"0170",4,"A","01","017","0170",,
|
||||
430,"02",2,"A","02",,,,
|
||||
440,"021",3,"A","02","021",,,
|
||||
450,"0210",4,"A","02","021","0210",,
|
||||
460,"022",3,"A","02","022",,,
|
||||
470,"0220",4,"A","02","022","0220",,
|
||||
480,"023",3,"A","02","023",,,
|
||||
490,"0230",4,"A","02","023","0230",,
|
||||
500,"024",3,"A","02","024",,,
|
||||
510,"0240",4,"A","02","024","0240",,
|
||||
520,"03",2,"A","03",,,,
|
||||
530,"031",3,"A","03","031",,,
|
||||
540,"0311",4,"A","03","031","0311",,
|
||||
550,"0312",4,"A","03","031","0312",,
|
||||
560,"032",3,"A","03","032",,,
|
||||
570,"0321",4,"A","03","032","0321",,
|
||||
580,"0322",4,"A","03","032","0322",,
|
||||
590,"B",1,"B",,,,,
|
||||
600,"05",2,"B","05",,,,
|
||||
610,"051",3,"B","05","051",,,
|
||||
620,"0510",4,"B","05","051","0510",,
|
||||
630,"052",3,"B","05","052",,,
|
||||
640,"0520",4,"B","05","052","0520",,
|
||||
650,"06",2,"B","06",,,,
|
||||
660,"061",3,"B","06","061",,,
|
||||
670,"0610",4,"B","06","061","0610",,
|
||||
680,"062",3,"B","06","062",,,
|
||||
690,"0620",4,"B","06","062","0620",,
|
||||
700,"07",2,"B","07",,,,
|
||||
710,"071",3,"B","07","071",,,
|
||||
720,"0710",4,"B","07","071","0710",,
|
||||
730,"072",3,"B","07","072",,,
|
||||
740,"0721",4,"B","07","072","0721",,
|
||||
750,"0729",4,"B","07","072","0729",,
|
||||
760,"08",2,"B","08",,,,
|
||||
770,"081",3,"B","08","081",,,
|
||||
780,"0810",4,"B","08","081","0810",,
|
||||
790,"089",3,"B","08","089",,,
|
||||
800,"0891",4,"B","08","089","0891",,
|
||||
810,"0892",4,"B","08","089","0892",,
|
||||
820,"0893",4,"B","08","089","0893",,
|
||||
830,"0899",4,"B","08","089","0899",,
|
||||
840,"09",2,"B","09",,,,
|
||||
850,"091",3,"B","09","091",,,
|
||||
860,"0910",4,"B","09","091","0910",,
|
||||
870,"099",3,"B","09","099",,,
|
||||
880,"0990",4,"B","09","099","0990",,
|
||||
890,"C",1,"C",,,,,
|
||||
900,"10",2,"C","10",,,,
|
||||
910,"101",3,"C","10","101",,,
|
||||
920,"1010",4,"C","10","101","1010",,
|
||||
930,"102",3,"C","10","102",,,
|
||||
940,"1020",4,"C","10","102","1020",,
|
||||
950,"103",3,"C","10","103",,,
|
||||
960,"1030",4,"C","10","103","1030",,
|
||||
970,"104",3,"C","10","104",,,
|
||||
980,"1040",4,"C","10","104","1040",,
|
||||
990,"105",3,"C","10","105",,,
|
||||
1000,"1050",4,"C","10","105","1050",,
|
||||
1010,"106",3,"C","10","106",,,
|
||||
1020,"1061",4,"C","10","106","1061",,
|
||||
1030,"1062",4,"C","10","106","1062",,
|
||||
1040,"107",3,"C","10","107",,,
|
||||
1050,"1071",4,"C","10","107","1071",,
|
||||
1060,"1072",4,"C","10","107","1072",,
|
||||
1070,"1073",4,"C","10","107","1073",,
|
||||
1080,"1074",4,"C","10","107","1074",,
|
||||
1090,"1075",4,"C","10","107","1075",,
|
||||
1100,"1079",4,"C","10","107","1079",,
|
||||
1110,"108",3,"C","10","108",,,
|
||||
1120,"1080",4,"C","10","108","1080",,
|
||||
1130,"11",2,"C","11",,,,
|
||||
1140,"110",3,"C","11","110",,,
|
||||
1150,"1101",4,"C","11","110","1101",,
|
||||
1160,"1102",4,"C","11","110","1102",,
|
||||
1170,"1103",4,"C","11","110","1103",,
|
||||
1180,"1104",4,"C","11","110","1104",,
|
||||
1190,"12",2,"C","12",,,,
|
||||
1200,"120",3,"C","12","120",,,
|
||||
1210,"1200",4,"C","12","120","1200",,
|
||||
1220,"13",2,"C","13",,,,
|
||||
1230,"131",3,"C","13","131",,,
|
||||
1240,"1311",4,"C","13","131","1311",,
|
||||
1250,"1312",4,"C","13","131","1312",,
|
||||
1260,"1313",4,"C","13","131","1313",,
|
||||
1270,"139",3,"C","13","139",,,
|
||||
1280,"1391",4,"C","13","139","1391",,
|
||||
1290,"1392",4,"C","13","139","1392",,
|
||||
1300,"1393",4,"C","13","139","1393",,
|
||||
1310,"1394",4,"C","13","139","1394",,
|
||||
1320,"1399",4,"C","13","139","1399",,
|
||||
1330,"14",2,"C","14",,,,
|
||||
1340,"141",3,"C","14","141",,,
|
||||
1350,"1410",4,"C","14","141","1410",,
|
||||
1360,"142",3,"C","14","142",,,
|
||||
1370,"1420",4,"C","14","142","1420",,
|
||||
1380,"143",3,"C","14","143",,,
|
||||
1390,"1430",4,"C","14","143","1430",,
|
||||
1400,"15",2,"C","15",,,,
|
||||
1410,"151",3,"C","15","151",,,
|
||||
1420,"1511",4,"C","15","151","1511",,
|
||||
1430,"1512",4,"C","15","151","1512",,
|
||||
1440,"152",3,"C","15","152",,,
|
||||
1450,"1520",4,"C","15","152","1520",,
|
||||
1460,"16",2,"C","16",,,,
|
||||
1470,"161",3,"C","16","161",,,
|
||||
1480,"1610",4,"C","16","161","1610",,
|
||||
1490,"162",3,"C","16","162",,,
|
||||
1500,"1621",4,"C","16","162","1621",,
|
||||
1510,"1622",4,"C","16","162","1622",,
|
||||
1520,"1623",4,"C","16","162","1623",,
|
||||
1530,"1629",4,"C","16","162","1629",,
|
||||
1540,"17",2,"C","17",,,,
|
||||
1550,"170",3,"C","17","170",,,
|
||||
1560,"1701",4,"C","17","170","1701",,
|
||||
1570,"1702",4,"C","17","170","1702",,
|
||||
1580,"1709",4,"C","17","170","1709",,
|
||||
1590,"18",2,"C","18",,,,
|
||||
1600,"181",3,"C","18","181",,,
|
||||
1610,"1811",4,"C","18","181","1811",,
|
||||
1620,"1812",4,"C","18","181","1812",,
|
||||
1630,"182",3,"C","18","182",,,
|
||||
1640,"1820",4,"C","18","182","1820",,
|
||||
1650,"19",2,"C","19",,,,
|
||||
1660,"191",3,"C","19","191",,,
|
||||
1670,"1910",4,"C","19","191","1910",,
|
||||
1680,"192",3,"C","19","192",,,
|
||||
1690,"1920",4,"C","19","192","1920",,
|
||||
1700,"20",2,"C","20",,,,
|
||||
1710,"201",3,"C","20","201",,,
|
||||
1720,"2011",4,"C","20","201","2011",,
|
||||
1730,"2012",4,"C","20","201","2012",,
|
||||
1740,"2013",4,"C","20","201","2013",,
|
||||
1750,"202",3,"C","20","202",,,
|
||||
1760,"2021",4,"C","20","202","2021",,
|
||||
1770,"2022",4,"C","20","202","2022",,
|
||||
1780,"2023",4,"C","20","202","2023",,
|
||||
1790,"2029",4,"C","20","202","2029",,
|
||||
1800,"203",3,"C","20","203",,,
|
||||
1810,"2030",4,"C","20","203","2030",,
|
||||
1820,"21",2,"C","21",,,,
|
||||
1830,"210",3,"C","21","210",,,
|
||||
1840,"2100",4,"C","21","210","2100",,
|
||||
1850,"22",2,"C","22",,,,
|
||||
1860,"221",3,"C","22","221",,,
|
||||
1870,"2211",4,"C","22","221","2211",,
|
||||
1880,"2219",4,"C","22","221","2219",,
|
||||
1890,"222",3,"C","22","222",,,
|
||||
1900,"2220",4,"C","22","222","2220",,
|
||||
1910,"23",2,"C","23",,,,
|
||||
1920,"231",3,"C","23","231",,,
|
||||
1930,"2310",4,"C","23","231","2310",,
|
||||
1940,"239",3,"C","23","239",,,
|
||||
1950,"2391",4,"C","23","239","2391",,
|
||||
1960,"2392",4,"C","23","239","2392",,
|
||||
1970,"2393",4,"C","23","239","2393",,
|
||||
1980,"2394",4,"C","23","239","2394",,
|
||||
1990,"2395",4,"C","23","239","2395",,
|
||||
2000,"2396",4,"C","23","239","2396",,
|
||||
2010,"2399",4,"C","23","239","2399",,
|
||||
2020,"24",2,"C","24",,,,
|
||||
2030,"241",3,"C","24","241",,,
|
||||
2040,"2410",4,"C","24","241","2410",,
|
||||
2050,"242",3,"C","24","242",,,
|
||||
2060,"2420",4,"C","24","242","2420",,
|
||||
2070,"243",3,"C","24","243",,,
|
||||
2080,"2431",4,"C","24","243","2431",,
|
||||
2090,"2432",4,"C","24","243","2432",,
|
||||
2100,"25",2,"C","25",,,,
|
||||
2110,"251",3,"C","25","251",,,
|
||||
2120,"2511",4,"C","25","251","2511",,
|
||||
2130,"2512",4,"C","25","251","2512",,
|
||||
2140,"2513",4,"C","25","251","2513",,
|
||||
2150,"252",3,"C","25","252",,,
|
||||
2160,"2520",4,"C","25","252","2520",,
|
||||
2170,"259",3,"C","25","259",,,
|
||||
2180,"2591",4,"C","25","259","2591",,
|
||||
2190,"2592",4,"C","25","259","2592",,
|
||||
2200,"2593",4,"C","25","259","2593",,
|
||||
2210,"2599",4,"C","25","259","2599",,
|
||||
2220,"26",2,"C","26",,,,
|
||||
2230,"261",3,"C","26","261",,,
|
||||
2240,"2610",4,"C","26","261","2610",,
|
||||
2250,"262",3,"C","26","262",,,
|
||||
2260,"2620",4,"C","26","262","2620",,
|
||||
2270,"263",3,"C","26","263",,,
|
||||
2280,"2630",4,"C","26","263","2630",,
|
||||
2290,"264",3,"C","26","264",,,
|
||||
2300,"2640",4,"C","26","264","2640",,
|
||||
2310,"265",3,"C","26","265",,,
|
||||
2320,"2651",4,"C","26","265","2651",,
|
||||
2330,"2652",4,"C","26","265","2652",,
|
||||
2340,"266",3,"C","26","266",,,
|
||||
2350,"2660",4,"C","26","266","2660",,
|
||||
2360,"267",3,"C","26","267",,,
|
||||
2370,"2670",4,"C","26","267","2670",,
|
||||
2380,"268",3,"C","26","268",,,
|
||||
2390,"2680",4,"C","26","268","2680",,
|
||||
2400,"27",2,"C","27",,,,
|
||||
2410,"271",3,"C","27","271",,,
|
||||
2420,"2710",4,"C","27","271","2710",,
|
||||
2430,"272",3,"C","27","272",,,
|
||||
2440,"2720",4,"C","27","272","2720",,
|
||||
2450,"273",3,"C","27","273",,,
|
||||
2460,"2731",4,"C","27","273","2731",,
|
||||
2470,"2732",4,"C","27","273","2732",,
|
||||
2480,"2733",4,"C","27","273","2733",,
|
||||
2490,"274",3,"C","27","274",,,
|
||||
2500,"2740",4,"C","27","274","2740",,
|
||||
2510,"275",3,"C","27","275",,,
|
||||
2520,"2750",4,"C","27","275","2750",,
|
||||
2530,"279",3,"C","27","279",,,
|
||||
2540,"2790",4,"C","27","279","2790",,
|
||||
2550,"28",2,"C","28",,,,
|
||||
2560,"281",3,"C","28","281",,,
|
||||
2570,"2811",4,"C","28","281","2811",,
|
||||
2580,"2812",4,"C","28","281","2812",,
|
||||
2590,"2813",4,"C","28","281","2813",,
|
||||
2600,"2814",4,"C","28","281","2814",,
|
||||
2610,"2815",4,"C","28","281","2815",,
|
||||
2620,"2816",4,"C","28","281","2816",,
|
||||
2630,"2817",4,"C","28","281","2817",,
|
||||
2640,"2818",4,"C","28","281","2818",,
|
||||
2650,"2819",4,"C","28","281","2819",,
|
||||
2660,"282",3,"C","28","282",,,
|
||||
2670,"2821",4,"C","28","282","2821",,
|
||||
2680,"2822",4,"C","28","282","2822",,
|
||||
2690,"2823",4,"C","28","282","2823",,
|
||||
2700,"2824",4,"C","28","282","2824",,
|
||||
2710,"2825",4,"C","28","282","2825",,
|
||||
2720,"2826",4,"C","28","282","2826",,
|
||||
2730,"2829",4,"C","28","282","2829",,
|
||||
2740,"29",2,"C","29",,,,
|
||||
2750,"291",3,"C","29","291",,,
|
||||
2760,"2910",4,"C","29","291","2910",,
|
||||
2770,"292",3,"C","29","292",,,
|
||||
2780,"2920",4,"C","29","292","2920",,
|
||||
2790,"293",3,"C","29","293",,,
|
||||
2800,"2930",4,"C","29","293","2930",,
|
||||
2810,"30",2,"C","30",,,,
|
||||
2820,"301",3,"C","30","301",,,
|
||||
2830,"3011",4,"C","30","301","3011",,
|
||||
2840,"3012",4,"C","30","301","3012",,
|
||||
2850,"302",3,"C","30","302",,,
|
||||
2860,"3020",4,"C","30","302","3020",,
|
||||
2870,"303",3,"C","30","303",,,
|
||||
2880,"3030",4,"C","30","303","3030",,
|
||||
2890,"304",3,"C","30","304",,,
|
||||
2900,"3040",4,"C","30","304","3040",,
|
||||
2910,"309",3,"C","30","309",,,
|
||||
2920,"3091",4,"C","30","309","3091",,
|
||||
2930,"3092",4,"C","30","309","3092",,
|
||||
2940,"3099",4,"C","30","309","3099",,
|
||||
2950,"31",2,"C","31",,,,
|
||||
2960,"310",3,"C","31","310",,,
|
||||
2970,"3100",4,"C","31","310","3100",,
|
||||
2980,"32",2,"C","32",,,,
|
||||
2990,"321",3,"C","32","321",,,
|
||||
3000,"3211",4,"C","32","321","3211",,
|
||||
3010,"3212",4,"C","32","321","3212",,
|
||||
3020,"322",3,"C","32","322",,,
|
||||
3030,"3220",4,"C","32","322","3220",,
|
||||
3040,"323",3,"C","32","323",,,
|
||||
3050,"3230",4,"C","32","323","3230",,
|
||||
3060,"324",3,"C","32","324",,,
|
||||
3070,"3240",4,"C","32","324","3240",,
|
||||
3080,"325",3,"C","32","325",,,
|
||||
3090,"3250",4,"C","32","325","3250",,
|
||||
3100,"329",3,"C","32","329",,,
|
||||
3110,"3290",4,"C","32","329","3290",,
|
||||
3120,"33",2,"C","33",,,,
|
||||
3130,"331",3,"C","33","331",,,
|
||||
3140,"3311",4,"C","33","331","3311",,
|
||||
3150,"3312",4,"C","33","331","3312",,
|
||||
3160,"3313",4,"C","33","331","3313",,
|
||||
3170,"3314",4,"C","33","331","3314",,
|
||||
3180,"3315",4,"C","33","331","3315",,
|
||||
3190,"3319",4,"C","33","331","3319",,
|
||||
3200,"332",3,"C","33","332",,,
|
||||
3210,"3320",4,"C","33","332","3320",,
|
||||
3220,"D",1,"D",,,,,
|
||||
3230,"35",2,"D","35",,,,
|
||||
3240,"351",3,"D","35","351",,,
|
||||
3250,"3510",4,"D","35","351","3510",,
|
||||
3260,"352",3,"D","35","352",,,
|
||||
3270,"3520",4,"D","35","352","3520",,
|
||||
3280,"353",3,"D","35","353",,,
|
||||
3290,"3530",4,"D","35","353","3530",,
|
||||
3300,"E",1,"E",,,,,
|
||||
3310,"36",2,"E","36",,,,
|
||||
3320,"360",3,"E","36","360",,,
|
||||
3330,"3600",4,"E","36","360","3600",,
|
||||
3340,"37",2,"E","37",,,,
|
||||
3350,"370",3,"E","37","370",,,
|
||||
3360,"3700",4,"E","37","370","3700",,
|
||||
3370,"38",2,"E","38",,,,
|
||||
3380,"381",3,"E","38","381",,,
|
||||
3390,"3811",4,"E","38","381","3811",,
|
||||
3400,"3812",4,"E","38","381","3812",,
|
||||
3410,"382",3,"E","38","382",,,
|
||||
3420,"3821",4,"E","38","382","3821",,
|
||||
3430,"3822",4,"E","38","382","3822",,
|
||||
3440,"383",3,"E","38","383",,,
|
||||
3450,"3830",4,"E","38","383","3830",,
|
||||
3460,"39",2,"E","39",,,,
|
||||
3470,"390",3,"E","39","390",,,
|
||||
3480,"3900",4,"E","39","390","3900",,
|
||||
3490,"F",1,"F",,,,,
|
||||
3500,"41",2,"F","41",,,,
|
||||
3510,"410",3,"F","41","410",,,
|
||||
3520,"4100",4,"F","41","410","4100",,
|
||||
3530,"42",2,"F","42",,,,
|
||||
3540,"421",3,"F","42","421",,,
|
||||
3550,"4210",4,"F","42","421","4210",,
|
||||
3560,"422",3,"F","42","422",,,
|
||||
3570,"4220",4,"F","42","422","4220",,
|
||||
3580,"429",3,"F","42","429",,,
|
||||
3590,"4290",4,"F","42","429","4290",,
|
||||
3600,"43",2,"F","43",,,,
|
||||
3610,"431",3,"F","43","431",,,
|
||||
3620,"4311",4,"F","43","431","4311",,
|
||||
3630,"4312",4,"F","43","431","4312",,
|
||||
3640,"432",3,"F","43","432",,,
|
||||
3650,"4321",4,"F","43","432","4321",,
|
||||
3660,"4322",4,"F","43","432","4322",,
|
||||
3670,"4329",4,"F","43","432","4329",,
|
||||
3680,"433",3,"F","43","433",,,
|
||||
3690,"4330",4,"F","43","433","4330",,
|
||||
3700,"439",3,"F","43","439",,,
|
||||
3710,"4390",4,"F","43","439","4390",,
|
||||
3720,"G",1,"G",,,,,
|
||||
3730,"45",2,"G","45",,,,
|
||||
3740,"451",3,"G","45","451",,,
|
||||
3750,"4510",4,"G","45","451","4510",,
|
||||
3760,"452",3,"G","45","452",,,
|
||||
3770,"4520",4,"G","45","452","4520",,
|
||||
3780,"453",3,"G","45","453",,,
|
||||
3790,"4530",4,"G","45","453","4530",,
|
||||
3800,"454",3,"G","45","454",,,
|
||||
3810,"4540",4,"G","45","454","4540",,
|
||||
3820,"46",2,"G","46",,,,
|
||||
3830,"461",3,"G","46","461",,,
|
||||
3840,"4610",4,"G","46","461","4610",,
|
||||
3850,"462",3,"G","46","462",,,
|
||||
3860,"4620",4,"G","46","462","4620",,
|
||||
3870,"463",3,"G","46","463",,,
|
||||
3880,"4630",4,"G","46","463","4630",,
|
||||
3890,"464",3,"G","46","464",,,
|
||||
3900,"4641",4,"G","46","464","4641",,
|
||||
3910,"4649",4,"G","46","464","4649",,
|
||||
3920,"465",3,"G","46","465",,,
|
||||
3930,"4651",4,"G","46","465","4651",,
|
||||
3940,"4652",4,"G","46","465","4652",,
|
||||
3950,"4653",4,"G","46","465","4653",,
|
||||
3960,"4659",4,"G","46","465","4659",,
|
||||
3970,"466",3,"G","46","466",,,
|
||||
3980,"4661",4,"G","46","466","4661",,
|
||||
3990,"4662",4,"G","46","466","4662",,
|
||||
4000,"4663",4,"G","46","466","4663",,
|
||||
4010,"4669",4,"G","46","466","4669",,
|
||||
4020,"469",3,"G","46","469",,,
|
||||
4030,"4690",4,"G","46","469","4690",,
|
||||
4040,"47",2,"G","47",,,,
|
||||
4050,"471",3,"G","47","471",,,
|
||||
4060,"4711",4,"G","47","471","4711",,
|
||||
4070,"4719",4,"G","47","471","4719",,
|
||||
4080,"472",3,"G","47","472",,,
|
||||
4090,"4721",4,"G","47","472","4721",,
|
||||
4100,"4722",4,"G","47","472","4722",,
|
||||
4110,"4723",4,"G","47","472","4723",,
|
||||
4120,"473",3,"G","47","473",,,
|
||||
4130,"4730",4,"G","47","473","4730",,
|
||||
4140,"474",3,"G","47","474",,,
|
||||
4150,"4741",4,"G","47","474","4741",,
|
||||
4160,"4742",4,"G","47","474","4742",,
|
||||
4170,"475",3,"G","47","475",,,
|
||||
4180,"4751",4,"G","47","475","4751",,
|
||||
4190,"4752",4,"G","47","475","4752",,
|
||||
4200,"4753",4,"G","47","475","4753",,
|
||||
4210,"4759",4,"G","47","475","4759",,
|
||||
4220,"476",3,"G","47","476",,,
|
||||
4230,"4761",4,"G","47","476","4761",,
|
||||
4240,"4762",4,"G","47","476","4762",,
|
||||
4250,"4763",4,"G","47","476","4763",,
|
||||
4260,"4764",4,"G","47","476","4764",,
|
||||
4270,"477",3,"G","47","477",,,
|
||||
4280,"4771",4,"G","47","477","4771",,
|
||||
4290,"4772",4,"G","47","477","4772",,
|
||||
4300,"4773",4,"G","47","477","4773",,
|
||||
4310,"4774",4,"G","47","477","4774",,
|
||||
4320,"478",3,"G","47","478",,,
|
||||
4330,"4781",4,"G","47","478","4781",,
|
||||
4340,"4782",4,"G","47","478","4782",,
|
||||
4350,"4789",4,"G","47","478","4789",,
|
||||
4360,"479",3,"G","47","479",,,
|
||||
4370,"4791",4,"G","47","479","4791",,
|
||||
4380,"4799",4,"G","47","479","4799",,
|
||||
4390,"H",1,"H",,,,,
|
||||
4400,"49",2,"H","49",,,,
|
||||
4410,"491",3,"H","49","491",,,
|
||||
4420,"4911",4,"H","49","491","4911",,
|
||||
4430,"4912",4,"H","49","491","4912",,
|
||||
4440,"492",3,"H","49","492",,,
|
||||
4450,"4921",4,"H","49","492","4921",,
|
||||
4460,"4922",4,"H","49","492","4922",,
|
||||
4470,"4923",4,"H","49","492","4923",,
|
||||
4480,"493",3,"H","49","493",,,
|
||||
4490,"4930",4,"H","49","493","4930",,
|
||||
4500,"50",2,"H","50",,,,
|
||||
4510,"501",3,"H","50","501",,,
|
||||
4520,"5011",4,"H","50","501","5011",,
|
||||
4530,"5012",4,"H","50","501","5012",,
|
||||
4540,"502",3,"H","50","502",,,
|
||||
4550,"5021",4,"H","50","502","5021",,
|
||||
4560,"5022",4,"H","50","502","5022",,
|
||||
4570,"51",2,"H","51",,,,
|
||||
4580,"511",3,"H","51","511",,,
|
||||
4590,"5110",4,"H","51","511","5110",,
|
||||
4600,"512",3,"H","51","512",,,
|
||||
4610,"5120",4,"H","51","512","5120",,
|
||||
4620,"52",2,"H","52",,,,
|
||||
4630,"521",3,"H","52","521",,,
|
||||
4640,"5210",4,"H","52","521","5210",,
|
||||
4650,"522",3,"H","52","522",,,
|
||||
4660,"5221",4,"H","52","522","5221",,
|
||||
4670,"5222",4,"H","52","522","5222",,
|
||||
4680,"5223",4,"H","52","522","5223",,
|
||||
4690,"5224",4,"H","52","522","5224",,
|
||||
4700,"5229",4,"H","52","522","5229",,
|
||||
4710,"53",2,"H","53",,,,
|
||||
4720,"531",3,"H","53","531",,,
|
||||
4730,"5310",4,"H","53","531","5310",,
|
||||
4740,"532",3,"H","53","532",,,
|
||||
4750,"5320",4,"H","53","532","5320",,
|
||||
4760,"I",1,"I",,,,,
|
||||
4770,"55",2,"I","55",,,,
|
||||
4780,"551",3,"I","55","551",,,
|
||||
4790,"5510",4,"I","55","551","5510",,
|
||||
4800,"552",3,"I","55","552",,,
|
||||
4810,"5520",4,"I","55","552","5520",,
|
||||
4820,"559",3,"I","55","559",,,
|
||||
4830,"5590",4,"I","55","559","5590",,
|
||||
4840,"56",2,"I","56",,,,
|
||||
4850,"561",3,"I","56","561",,,
|
||||
4860,"5610",4,"I","56","561","5610",,
|
||||
4870,"562",3,"I","56","562",,,
|
||||
4880,"5621",4,"I","56","562","5621",,
|
||||
4890,"5629",4,"I","56","562","5629",,
|
||||
4930,"563",3,"I","56","563",,,
|
||||
4940,"5630",4,"I","56","563","5630",,
|
||||
4950,"J",1,"J",,,,,
|
||||
4960,"58",2,"J","58",,,,
|
||||
4970,"581",3,"J","58","581",,,
|
||||
4980,"5811",4,"J","58","581","5811",,
|
||||
4990,"5812",4,"J","58","581","5812",,
|
||||
5000,"5813",4,"J","58","581","5813",,
|
||||
5010,"5819",4,"J","58","581","5819",,
|
||||
5020,"582",3,"J","58","582",,,
|
||||
5030,"5820",4,"J","58","582","5820",,
|
||||
5040,"59",2,"J","59",,,,
|
||||
5050,"591",3,"J","59","591",,,
|
||||
5060,"5911",4,"J","59","591","5911",,
|
||||
5070,"5912",4,"J","59","591","5912",,
|
||||
5080,"5913",4,"J","59","591","5913",,
|
||||
5090,"5914",4,"J","59","591","5914",,
|
||||
5100,"592",3,"J","59","592",,,
|
||||
5110,"5920",4,"J","59","592","5920",,
|
||||
5120,"60",2,"J","60",,,,
|
||||
5130,"601",3,"J","60","601",,,
|
||||
5140,"6010",4,"J","60","601","6010",,
|
||||
5150,"602",3,"J","60","602",,,
|
||||
5160,"6020",4,"J","60","602","6020",,
|
||||
5180,"61",2,"J","61",,,,
|
||||
5190,"611",3,"J","61","611",,,
|
||||
5200,"6110",4,"J","61","611","6110",,
|
||||
5210,"612",3,"J","61","612",,,
|
||||
5220,"6120",4,"J","61","612","6120",,
|
||||
5230,"613",3,"J","61","613",,,
|
||||
5240,"6130",4,"J","61","613","6130",,
|
||||
5250,"619",3,"J","61","619",,,
|
||||
5260,"6190",4,"J","61","619","6190",,
|
||||
5270,"62",2,"J","62",,,,
|
||||
5280,"620",3,"J","62","620",,,
|
||||
5290,"6201",4,"J","62","620","6201",,
|
||||
5300,"6202",4,"J","62","620","6202",,
|
||||
5310,"6209",4,"J","62","620","6209",,
|
||||
5320,"63",2,"J","63",,,,
|
||||
5330,"631",3,"J","63","631",,,
|
||||
5340,"6311",4,"J","63","631","6311",,
|
||||
5350,"6312",4,"J","63","631","6312",,
|
||||
5360,"639",3,"J","63","639",,,
|
||||
5370,"6391",4,"J","63","639","6391",,
|
||||
5380,"6399",4,"J","63","639","6399",,
|
||||
5390,"K",1,"K",,,,,
|
||||
5400,"64",2,"K","64",,,,
|
||||
5410,"641",3,"K","64","641",,,
|
||||
5420,"6411",4,"K","64","641","6411",,
|
||||
5430,"6419",4,"K","64","641","6419",,
|
||||
5440,"642",3,"K","64","642",,,
|
||||
5450,"6420",4,"K","64","642","6420",,
|
||||
5460,"643",3,"K","64","643",,,
|
||||
5470,"6430",4,"K","64","643","6430",,
|
||||
5480,"649",3,"K","64","649",,,
|
||||
5490,"6491",4,"K","64","649","6491",,
|
||||
5500,"6492",4,"K","64","649","6492",,
|
||||
5510,"6499",4,"K","64","649","6499",,
|
||||
5520,"65",2,"K","65",,,,
|
||||
5530,"651",3,"K","65","651",,,
|
||||
5540,"6511",4,"K","65","651","6511",,
|
||||
5550,"6512",4,"K","65","651","6512",,
|
||||
5560,"652",3,"K","65","652",,,
|
||||
5570,"6520",4,"K","65","652","6520",,
|
||||
5580,"653",3,"K","65","653",,,
|
||||
5590,"6530",4,"K","65","653","6530",,
|
||||
5600,"66",2,"K","66",,,,
|
||||
5610,"661",3,"K","66","661",,,
|
||||
5620,"6611",4,"K","66","661","6611",,
|
||||
5630,"6612",4,"K","66","661","6612",,
|
||||
5640,"6619",4,"K","66","661","6619",,
|
||||
5650,"662",3,"K","66","662",,,
|
||||
5660,"6621",4,"K","66","662","6621",,
|
||||
5670,"6622",4,"K","66","662","6622",,
|
||||
5680,"6629",4,"K","66","662","6629",,
|
||||
5690,"663",3,"K","66","663",,,
|
||||
5700,"6630",4,"K","66","663","6630",,
|
||||
5710,"L",1,"L",,,,,
|
||||
5720,"68",2,"L","68",,,,
|
||||
5730,"681",3,"L","68","681",,,
|
||||
5740,"6810",4,"L","68","681","6810",,
|
||||
5750,"682",3,"L","68","682",,,
|
||||
5760,"6820",4,"L","68","682","6820",,
|
||||
5770,"M",1,"M",,,,,
|
||||
5780,"69",2,"M","69",,,,
|
||||
5790,"691",3,"M","69","691",,,
|
||||
5800,"6910",4,"M","69","691","6910",,
|
||||
5810,"692",3,"M","69","692",,,
|
||||
5820,"6920",4,"M","69","692","6920",,
|
||||
5830,"70",2,"M","70",,,,
|
||||
5840,"701",3,"M","70","701",,,
|
||||
5850,"7010",4,"M","70","701","7010",,
|
||||
5860,"702",3,"M","70","702",,,
|
||||
5870,"7020",4,"M","70","702","7020",,
|
||||
5880,"71",2,"M","71",,,,
|
||||
5890,"711",3,"M","71","711",,,
|
||||
5900,"7110",4,"M","71","711","7110",,
|
||||
5910,"712",3,"M","71","712",,,
|
||||
5920,"7120",4,"M","71","712","7120",,
|
||||
5930,"72",2,"M","72",,,,
|
||||
5940,"721",3,"M","72","721",,,
|
||||
5960,"7210",4,"M","72","721","7210",,
|
||||
5970,"722",3,"M","72","722",,,
|
||||
5980,"7220",4,"M","72","722","7220",,
|
||||
5990,"73",2,"M","73",,,,
|
||||
6000,"731",3,"M","73","731",,,
|
||||
6010,"7310",4,"M","73","731","7310",,
|
||||
6020,"732",3,"M","73","732",,,
|
||||
6030,"7320",4,"M","73","732","7320",,
|
||||
6040,"74",2,"M","74",,,,
|
||||
6050,"741",3,"M","74","741",,,
|
||||
6060,"7410",4,"M","74","741","7410",,
|
||||
6070,"742",3,"M","74","742",,,
|
||||
6080,"7420",4,"M","74","742","7420",,
|
||||
6090,"749",3,"M","74","749",,,
|
||||
6100,"7490",4,"M","74","749","7490",,
|
||||
6110,"75",2,"M","75",,,,
|
||||
6120,"750",3,"M","75","750",,,
|
||||
6130,"7500",4,"M","75","750","7500",,
|
||||
6140,"N",1,"N",,,,,
|
||||
6150,"77",2,"N","77",,,,
|
||||
6160,"771",3,"N","77","771",,,
|
||||
6170,"7710",4,"N","77","771","7710",,
|
||||
6180,"772",3,"N","77","772",,,
|
||||
6190,"7721",4,"N","77","772","7721",,
|
||||
6200,"7722",4,"N","77","772","7722",,
|
||||
6210,"7729",4,"N","77","772","7729",,
|
||||
6220,"773",3,"N","77","773",,,
|
||||
6230,"7730",4,"N","77","773","7730",,
|
||||
6240,"774",3,"N","77","774",,,
|
||||
6250,"7740",4,"N","77","774","7740",,
|
||||
6260,"78",2,"N","78",,,,
|
||||
6270,"781",3,"N","78","781",,,
|
||||
6280,"7810",4,"N","78","781","7810",,
|
||||
6290,"782",3,"N","78","782",,,
|
||||
6300,"7820",4,"N","78","782","7820",,
|
||||
6310,"783",3,"N","78","783",,,
|
||||
6320,"7830",4,"N","78","783","7830",,
|
||||
6330,"79",2,"N","79",,,,
|
||||
6340,"791",3,"N","79","791",,,
|
||||
6350,"7911",4,"N","79","791","7911",,
|
||||
6360,"7912",4,"N","79","791","7912",,
|
||||
6370,"799",3,"N","79","799",,,
|
||||
6380,"7990",4,"N","79","799","7990",,
|
||||
6390,"80",2,"N","80",,,,
|
||||
6400,"801",3,"N","80","801",,,
|
||||
6410,"8010",4,"N","80","801","8010",,
|
||||
6420,"802",3,"N","80","802",,,
|
||||
6430,"8020",4,"N","80","802","8020",,
|
||||
6440,"803",3,"N","80","803",,,
|
||||
6450,"8030",4,"N","80","803","8030",,
|
||||
6460,"81",2,"N","81",,,,
|
||||
6470,"811",3,"N","81","811",,,
|
||||
6480,"8110",4,"N","81","811","8110",,
|
||||
6490,"812",3,"N","81","812",,,
|
||||
6500,"8121",4,"N","81","812","8121",,
|
||||
6510,"8129",4,"N","81","812","8129",,
|
||||
6520,"813",3,"N","81","813",,,
|
||||
6530,"8130",4,"N","81","813","8130",,
|
||||
6540,"82",2,"N","82",,,,
|
||||
6550,"821",3,"N","82","821",,,
|
||||
6560,"8211",4,"N","82","821","8211",,
|
||||
6570,"8219",4,"N","82","821","8219",,
|
||||
6580,"822",3,"N","82","822",,,
|
||||
6590,"8220",4,"N","82","822","8220",,
|
||||
6600,"823",3,"N","82","823",,,
|
||||
6610,"8230",4,"N","82","823","8230",,
|
||||
6620,"829",3,"N","82","829",,,
|
||||
6630,"8291",4,"N","82","829","8291",,
|
||||
6640,"8292",4,"N","82","829","8292",,
|
||||
6650,"8299",4,"N","82","829","8299",,
|
||||
6660,"O",1,"O",,,,,
|
||||
6670,"84",2,"O","84",,,,
|
||||
6680,"841",3,"O","84","841",,,
|
||||
6690,"8411",4,"O","84","841","8411",,
|
||||
6700,"8412",4,"O","84","841","8412",,
|
||||
6710,"8413",4,"O","84","841","8413",,
|
||||
6730,"842",3,"O","84","842",,,
|
||||
6740,"8421",4,"O","84","842","8421",,
|
||||
6750,"8422",4,"O","84","842","8422",,
|
||||
6760,"8423",4,"O","84","842","8423",,
|
||||
6770,"843",3,"O","84","843",,,
|
||||
6780,"8430",4,"O","84","843","8430",,
|
||||
6790,"P",1,"P",,,,,
|
||||
6800,"85",2,"P","85",,,,
|
||||
6810,"851",3,"P","85","851",,,
|
||||
6820,"8510",4,"P","85","851","8510",,
|
||||
6830,"852",3,"P","85","852",,,
|
||||
6840,"8521",4,"P","85","852","8521",,
|
||||
6850,"8522",4,"P","85","852","8522",,
|
||||
6860,"853",3,"P","85","853",,,
|
||||
6870,"8530",4,"P","85","853","8530",,
|
||||
6880,"854",3,"P","85","854",,,
|
||||
6890,"8541",4,"P","85","854","8541",,
|
||||
6900,"8542",4,"P","85","854","8542",,
|
||||
6910,"8549",4,"P","85","854","8549",,
|
||||
6920,"855",3,"P","85","855",,,
|
||||
6930,"8550",4,"P","85","855","8550",,
|
||||
6940,"Q",1,"Q",,,,,
|
||||
6950,"86",2,"Q","86",,,,
|
||||
6960,"861",3,"Q","86","861",,,
|
||||
6970,"8610",4,"Q","86","861","8610",,
|
||||
6980,"862",3,"Q","86","862",,,
|
||||
6990,"8620",4,"Q","86","862","8620",,
|
||||
7000,"869",3,"Q","86","869",,,
|
||||
7010,"8690",4,"Q","86","869","8690",,
|
||||
7020,"87",2,"Q","87",,,,
|
||||
7030,"871",3,"Q","87","871",,,
|
||||
7040,"8710",4,"Q","87","871","8710",,
|
||||
7050,"872",3,"Q","87","872",,,
|
||||
7060,"8720",4,"Q","87","872","8720",,
|
||||
7070,"873",3,"Q","87","873",,,
|
||||
7080,"8730",4,"Q","87","873","8730",,
|
||||
7090,"879",3,"Q","87","879",,,
|
||||
7100,"8790",4,"Q","87","879","8790",,
|
||||
7110,"88",2,"Q","88",,,,
|
||||
7120,"881",3,"Q","88","881",,,
|
||||
7130,"8810",4,"Q","88","881","8810",,
|
||||
7140,"889",3,"Q","88","889",,,
|
||||
7150,"8890",4,"Q","88","889","8890",,
|
||||
7160,"R",1,"R",,,,,
|
||||
7170,"90",2,"R","90",,,,
|
||||
7180,"900",3,"R","90","900",,,
|
||||
7190,"9000",4,"R","90","900","9000",,
|
||||
7200,"91",2,"R","91",,,,
|
||||
7210,"910",3,"R","91","910",,,
|
||||
7215,"9101",4,"R","91","910","9101",,
|
||||
7220,"9102",4,"R","91","910","9102",,
|
||||
7230,"9103",4,"R","91","910","9103",,
|
||||
7240,"92",2,"R","92",,,,
|
||||
7250,"920",3,"R","92","920",,,
|
||||
7260,"9200",4,"R","92","920","9200",,
|
||||
7270,"93",2,"R","93",,,,
|
||||
7280,"931",3,"R","93","931",,,
|
||||
7290,"9311",4,"R","93","931","9311",,
|
||||
7300,"9312",4,"R","93","931","9312",,
|
||||
7310,"9319",4,"R","93","931","9319",,
|
||||
7320,"932",3,"R","93","932",,,
|
||||
7330,"9321",4,"R","93","932","9321",,
|
||||
7340,"9329",4,"R","93","932","9329",,
|
||||
7350,"S",1,"S",,,,,
|
||||
7360,"94",2,"S","94",,,,
|
||||
7370,"941",3,"S","94","941",,,
|
||||
7380,"9411",4,"S","94","941","9411",,
|
||||
7390,"9412",4,"S","94","941","9412",,
|
||||
7400,"942",3,"S","94","942",,,
|
||||
7410,"9420",4,"S","94","942","9420",,
|
||||
7420,"949",3,"S","94","949",,,
|
||||
7430,"9491",4,"S","94","949","9491",,
|
||||
7440,"9492",4,"S","94","949","9492",,
|
||||
7450,"9499",4,"S","94","949","9499",,
|
||||
7460,"95",2,"S","95",,,,
|
||||
7470,"951",3,"S","95","951",,,
|
||||
7480,"9511",4,"S","95","951","9511",,
|
||||
7490,"9512",4,"S","95","951","9512",,
|
||||
7500,"952",3,"S","95","952",,,
|
||||
7510,"9521",4,"S","95","952","9521",,
|
||||
7520,"9522",4,"S","95","952","9522",,
|
||||
7530,"9523",4,"S","95","952","9523",,
|
||||
7540,"9524",4,"S","95","952","9524",,
|
||||
7550,"9529",4,"S","95","952","9529",,
|
||||
7560,"96",2,"S","96",,,,
|
||||
7570,"960",3,"S","96","960",,,
|
||||
7580,"9601",4,"S","96","960","9601",,
|
||||
7590,"9602",4,"S","96","960","9602",,
|
||||
7600,"9603",4,"S","96","960","9603",,
|
||||
7610,"9609",4,"S","96","960","9609",,
|
||||
7620,"T",1,"T",,,,,
|
||||
7630,"97",2,"T","97",,,,
|
||||
7640,"970",3,"T","97","970",,,
|
||||
7650,"9700",4,"T","97","970","9700",,
|
||||
7660,"98",2,"T","98",,,,
|
||||
7670,"981",3,"T","98","981",,,
|
||||
7680,"9810",4,"T","98","981","9810",,
|
||||
7690,"982",3,"T","98","982",,,
|
||||
7700,"9820",4,"T","98","982","9820",,
|
||||
7710,"U",1,"U",,,,,
|
||||
7720,"99",2,"U","99",,,,
|
||||
7730,"990",3,"U","99","990",,,
|
||||
7740,"9900",4,"U","99","990","9900",,
|
||||
|
5833
backend/db/data/isic_titles.csv
Normal file
5833
backend/db/data/isic_titles.csv
Normal file
File diff suppressed because it is too large
Load Diff
112
backend/db/migrations/018_create_and_seed_isic_tables.sql
Normal file
112
backend/db/migrations/018_create_and_seed_isic_tables.sql
Normal file
@@ -0,0 +1,112 @@
|
||||
-- UP Migration
|
||||
-- Этот SQL-код будет выполнен вашим скриптом run-migrations.js
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 1. Создаем таблицу для названий уровней ISIC
|
||||
CREATE TABLE IF NOT EXISTS isic_rev4_level_names (
|
||||
code_level INTEGER PRIMARY KEY,
|
||||
level_name_en TEXT
|
||||
);
|
||||
|
||||
-- 2. Создаем основную таблицу для кодов ISIC
|
||||
CREATE TABLE IF NOT EXISTS isic_rev4_codes (
|
||||
sort_order INTEGER,
|
||||
code VARCHAR(10) PRIMARY KEY,
|
||||
description TEXT,
|
||||
explanatory_note_inclusion TEXT,
|
||||
explanatory_note_exclusion TEXT,
|
||||
code_level INTEGER,
|
||||
level1 VARCHAR(10),
|
||||
level2 VARCHAR(10),
|
||||
level3 VARCHAR(10),
|
||||
level4 VARCHAR(10),
|
||||
level5 VARCHAR(10),
|
||||
level6 VARCHAR(10),
|
||||
CONSTRAINT fk_code_level FOREIGN KEY (code_level) REFERENCES isic_rev4_level_names (code_level) -- Добавляем внешний ключ
|
||||
);
|
||||
|
||||
-- 3. Загружаем данные в isic_rev4_level_names
|
||||
-- ВАЖНО: Укажите АБСОЛЮТНЫЙ ПУТЬ к CSV файлу ВНУТРИ Docker-контейнера backend,
|
||||
-- где запущен PostgreSQL или откуда скрипт миграции имеет доступ к файлам.
|
||||
-- Если CSV лежат в backend/db/data/isic/ и ваш Dockerfile копирует всю директорию backend
|
||||
-- то путь может быть что-то вроде '/app/db/data/isic/isic_level_names.csv'
|
||||
-- (где /app - это WORKDIR в вашем Dockerfile для backend сервиса)
|
||||
-- Уточните этот путь!
|
||||
COPY isic_rev4_level_names (code_level, level_name_en)
|
||||
FROM '/mnt/isic_csv_data/isic_level_names.csv' -- <--- ПУТЬ СООТВЕТСТВУЕТ ТОМУ, ЧТО В volumes
|
||||
WITH (FORMAT CSV, HEADER TRUE, DELIMITER ',', QUOTE '"');
|
||||
|
||||
-- 4. Создаем временные таблицы для импорта основных данных ISIC
|
||||
CREATE TEMP TABLE tmp_isic_titles (
|
||||
sort_order_tmp INTEGER,
|
||||
code_tmp VARCHAR(10),
|
||||
description_tmp TEXT,
|
||||
inclusion_tmp TEXT,
|
||||
exclusion_tmp TEXT
|
||||
) ON COMMIT DROP; -- Временная таблица удалится после коммита
|
||||
|
||||
CREATE TEMP TABLE tmp_isic_structure (
|
||||
sort_order_tmp INTEGER,
|
||||
code_tmp VARCHAR(10),
|
||||
code_level_tmp INTEGER,
|
||||
level1_tmp VARCHAR(10),
|
||||
level2_tmp VARCHAR(10),
|
||||
level3_tmp VARCHAR(10),
|
||||
level4_tmp VARCHAR(10),
|
||||
level5_tmp VARCHAR(10),
|
||||
level6_tmp VARCHAR(10)
|
||||
) ON COMMIT DROP; -- Временная таблица удалится после коммита
|
||||
|
||||
-- 5. Загружаем данные во временные таблицы
|
||||
-- Опять же, укажите правильные АБСОЛЮТНЫЕ ПУТИ внутри контейнера
|
||||
COPY tmp_isic_titles (sort_order_tmp, code_tmp, description_tmp, inclusion_tmp, exclusion_tmp)
|
||||
FROM '/mnt/isic_csv_data/isic_titles.csv' -- <--- ПУТЬ СООТВЕТСТВУЕТ ТОМУ, ЧТО В volumes
|
||||
WITH (FORMAT CSV, HEADER TRUE, DELIMITER ',', QUOTE '"');
|
||||
|
||||
COPY tmp_isic_structure (sort_order_tmp, code_tmp, code_level_tmp, level1_tmp, level2_tmp, level3_tmp, level4_tmp, level5_tmp, level6_tmp)
|
||||
FROM '/mnt/isic_csv_data/isic_structure.csv' -- <--- ПУТЬ СООТВЕТСТВУЕТ ТОМУ, ЧТО В volumes
|
||||
WITH (FORMAT CSV, HEADER TRUE, DELIMITER ',', QUOTE '"');
|
||||
|
||||
-- 6. Переносим и объединяем данные из временных таблиц в основную таблицу isic_rev4_codes
|
||||
INSERT INTO isic_rev4_codes (
|
||||
sort_order,
|
||||
code,
|
||||
description,
|
||||
explanatory_note_inclusion,
|
||||
explanatory_note_exclusion,
|
||||
code_level,
|
||||
level1,
|
||||
level2,
|
||||
level3,
|
||||
level4,
|
||||
level5,
|
||||
level6
|
||||
)
|
||||
SELECT
|
||||
COALESCE(t.sort_order_tmp, s.sort_order_tmp),
|
||||
s.code_tmp,
|
||||
t.description_tmp,
|
||||
t.inclusion_tmp,
|
||||
t.exclusion_tmp,
|
||||
s.code_level_tmp,
|
||||
s.level1_tmp,
|
||||
s.level2_tmp,
|
||||
s.level3_tmp,
|
||||
s.level4_tmp,
|
||||
s.level5_tmp,
|
||||
s.level6_tmp
|
||||
FROM
|
||||
tmp_isic_structure s
|
||||
LEFT JOIN
|
||||
tmp_isic_titles t ON s.code_tmp = t.code_tmp;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- DOWN Migration
|
||||
-- Этот SQL-код НЕ будет выполнен вашим текущим скриптом run-migrations.js для отката,
|
||||
-- но его полезно иметь для ручного отката или если вы доработаете скрипт.
|
||||
-- BEGIN;
|
||||
-- DROP TABLE IF EXISTS isic_rev4_codes;
|
||||
-- DROP TABLE IF EXISTS isic_rev4_level_names;
|
||||
-- COMMIT;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,61 +1,24 @@
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:05:54.596Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:06:35.410Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:06:37.053Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:06:38.995Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:07:42.117Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:07:43.846Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:07:45.684Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:08:39.639Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:08:41.262Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:08:42.827Z"}
|
||||
{"level":"error","message":"Provider for bsc is not available: getaddrinfo ENOTFOUND bsc-dataseed.binance.org","timestamp":"2025-05-05T17:11:24.532Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:11:25.234Z"}
|
||||
{"level":"error","message":"Provider for bsc is not available: getaddrinfo ENOTFOUND bsc-dataseed.binance.org","timestamp":"2025-05-05T17:11:25.560Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:11:25.706Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:12:31.716Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:12:33.387Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:12:50.836Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:12:51.254Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:13:44.046Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:13:45.699Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:15:33.209Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:15:51.418Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:17:13.719Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:17:14.659Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:17:46.272Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:20:03.425Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:20:46.591Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:20:46.777Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:20:49.072Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:21:17.895Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:21:48.807Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:21:49.895Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:21:52.406Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:23:06.807Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:24:24.638Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:24:24.912Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:25:15.386Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:27:03.532Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:27:03.717Z"}
|
||||
{"level":"error","message":"Provider for arbitrum is not available: ","timestamp":"2025-05-05T17:28:22.073Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:28:23.981Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:29:03.703Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:29:03.920Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:29:06.150Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:29:07.606Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:29:43.268Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:29:43.845Z"}
|
||||
{"level":"error","message":"Error: undefined","timestamp":"2025-05-05T17:29:44.496Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:30:09.731Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:30:12.159Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:30:12.734Z"}
|
||||
{"level":"error","message":"Error: undefined","timestamp":"2025-05-05T17:30:13.475Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:31:06.593Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:32:22.498Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:32:22.672Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:32:24.941Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:32:26.560Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:32:39.406Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:32:39.596Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:32:42.499Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-05T17:32:45.767Z"}
|
||||
{"level":"error","message":"Provider for arbitrum is not available: ","timestamp":"2025-05-06T15:18:44.320Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T15:18:46.202Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T15:23:24.784Z"}
|
||||
{"level":"error","message":"Provider for arbitrum is not available: ","timestamp":"2025-05-06T16:39:15.891Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:39:17.824Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:48:50.487Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:53:25.301Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:53:25.491Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:53:27.539Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:53:28.910Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:55:59.199Z"}
|
||||
{"level":"error","message":"Provider for arbitrum is not available: ","timestamp":"2025-05-06T16:55:59.573Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:55:59.753Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:56:02.002Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T16:56:03.323Z"}
|
||||
{"details":{"error":{"code":400,"message":"Structured query parameters(amenity, street, city, county, state, postalcode, country) cannot be used together with 'q' parameter."}},"level":"error","message":"[Geocoding] Error proxying to Nominatim: Bad Request","query":{"addressdetails":"1","format":"jsonv2","limit":"1","postalcode":"357524","q":"357524"},"status":400,"timestamp":"2025-05-06T17:28:05.374Z"}
|
||||
{"details":{"error":{"code":400,"message":"Structured query parameters(amenity, street, city, county, state, postalcode, country) cannot be used together with 'q' parameter."}},"level":"error","message":"[Geocoding] Error proxying to Nominatim: Bad Request","query":{"addressdetails":"1","format":"jsonv2","limit":"1","postalcode":"357524","q":"357524"},"status":400,"timestamp":"2025-05-06T17:28:27.207Z"}
|
||||
{"details":{"error":{"code":400,"message":"Structured query parameters(amenity, street, city, county, state, postalcode, country) cannot be used together with 'q' parameter."}},"level":"error","message":"[Geocoding] Error proxying to Nominatim: Bad Request","query":{"addressdetails":"1","format":"jsonv2","limit":"1","postalcode":"357524","q":"357524"},"status":400,"timestamp":"2025-05-06T17:31:34.832Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T17:33:25.449Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T17:49:27.115Z"}
|
||||
{"level":"error","message":"Provider for arbitrum is not available: ","timestamp":"2025-05-06T17:56:56.827Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T17:56:57.599Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T18:12:54.882Z"}
|
||||
{"level":"error","message":"Provider for polygon is not available: server response 404 Not Found (request={ }, response={ }, error=null, info={ \"requestUrl\": \"https://polygon-mainnet.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52\", \"responseBody\": \"<html>\\r\\n<head><title>404 Not Found</title></head>\\r\\n<body>\\r\\n<center><h1>404 Not Found</h1></center>\\r\\n<hr><center>nginx</center>\\r\\n</body>\\r\\n</html>\\r\\n\", \"responseStatus\": \"404 Not Found\" }, code=SERVER_ERROR, version=6.13.7)","timestamp":"2025-05-06T18:14:06.867Z"}
|
||||
|
||||
101
backend/routes/geocoding.js
Normal file
101
backend/routes/geocoding.js
Normal file
@@ -0,0 +1,101 @@
|
||||
// backend/routes/geocoding.js
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const axios = require('axios'); // Убедитесь, что axios установлен на бэкенде
|
||||
const logger = require('../utils/logger'); // Если используете логгер
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: Geocoding
|
||||
* description: Прокси для сервисов геокодирования (например, Nominatim)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/geocoding/nominatim-search:
|
||||
* get:
|
||||
* summary: Проксирует запрос к Nominatim API (search)
|
||||
* tags: [Geocoding]
|
||||
* description: Перенаправляет GET запрос со всеми его query параметрами к https://nominatim.openstreetmap.org/search. Это необходимо для обхода CORS ограничений в браузере.
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: q
|
||||
* schema:
|
||||
* type: string
|
||||
* required: true
|
||||
* description: Строка адреса для поиска.
|
||||
* - in: query
|
||||
* name: format
|
||||
* schema:
|
||||
* type: string
|
||||
* required: true
|
||||
* description: Формат ответа (например, jsonv2).
|
||||
* - in: query
|
||||
* name: addressdetails
|
||||
* schema:
|
||||
* type: integer
|
||||
* enum: [0, 1]
|
||||
* description: Включить детализированный адрес (0 или 1).
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Максимальное количество возвращаемых результатов.
|
||||
* # Можно добавить сюда и другие параметры Nominatim API по мере необходимости
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Успешный ответ от Nominatim.
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object # Или array, в зависимости от ответа Nominatim
|
||||
* 500:
|
||||
* description: Ошибка при запросе к Nominatim или внутренняя ошибка сервера.
|
||||
*/
|
||||
router.get('/nominatim-search', async (req, res) => {
|
||||
try {
|
||||
// Формируем URL для Nominatim, используя все query параметры из исходного запроса
|
||||
const queryParams = new URLSearchParams(req.query);
|
||||
const nominatimUrl = `https://nominatim.openstreetmap.org/search?${queryParams.toString()}`;
|
||||
|
||||
if (logger && typeof logger.info === 'function') {
|
||||
logger.info(`[Geocoding] Proxying request to Nominatim: ${nominatimUrl}`);
|
||||
} else {
|
||||
console.log(`[Geocoding] Proxying request to Nominatim: ${nominatimUrl}`);
|
||||
}
|
||||
|
||||
const nominatimResponse = await axios.get(nominatimUrl);
|
||||
|
||||
res.json(nominatimResponse.data);
|
||||
|
||||
} catch (error) {
|
||||
let errorMessage = error.message;
|
||||
let errorStatus = 500;
|
||||
let errorDetails = null;
|
||||
|
||||
if (error.response) {
|
||||
// Ошибка пришла от Nominatim (или сети)
|
||||
errorMessage = error.response.data?.message || error.response.statusText || 'Error fetching data from Nominatim';
|
||||
errorStatus = error.response.status || 500;
|
||||
errorDetails = error.response.data;
|
||||
} else if (error.request) {
|
||||
// Запрос был сделан, но ответ не получен
|
||||
errorMessage = 'No response received from Nominatim';
|
||||
}
|
||||
// Иначе это ошибка настройки axios или другая внутренняя ошибка
|
||||
|
||||
if (logger && typeof logger.error === 'function') {
|
||||
logger.error(`[Geocoding] Error proxying to Nominatim: ${errorMessage}`, { status: errorStatus, details: errorDetails, query: req.query });
|
||||
} else {
|
||||
console.error(`[Geocoding] Error proxying to Nominatim: ${errorMessage}`, { status: errorStatus, details: errorDetails, query: req.query });
|
||||
}
|
||||
|
||||
res.status(errorStatus).json({
|
||||
message: 'Error processing geocoding request',
|
||||
details: errorDetails || errorMessage
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
297
backend/routes/isic.js
Normal file
297
backend/routes/isic.js
Normal file
@@ -0,0 +1,297 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { pool } = require('../db'); // Убедитесь, что путь к вашему db-коннектору правильный
|
||||
const logger = require('../utils/logger'); // Если используете логгер
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* tags:
|
||||
* name: ISIC
|
||||
* description: API для кодов Международной стандартной отраслевой классификации (ISIC)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/isic/codes:
|
||||
* get:
|
||||
* summary: Получить список кодов ISIC с фильтрацией и пагинацией
|
||||
* tags: [ISIC]
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: level
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Фильтр по уровню кода (1-4, иногда 5 или 6)
|
||||
* - in: query
|
||||
* name: parent_code
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Фильтр по родительскому коду (для получения дочерних кодов)
|
||||
* - in: query
|
||||
* name: search
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Поисковый запрос по коду или описанию
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 1
|
||||
* description: Номер страницы для пагинации
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 25
|
||||
* description: Количество элементов на странице
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Список кодов ISIC
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* totalItems:
|
||||
* type: integer
|
||||
* codes:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/IsicCode'
|
||||
* totalPages:
|
||||
* type: integer
|
||||
* currentPage:
|
||||
* type: integer
|
||||
* 404:
|
||||
* description: Родительский код не найден
|
||||
* 500:
|
||||
* description: Ошибка сервера
|
||||
*/
|
||||
router.get('/codes', async (req, res) => {
|
||||
const { level, parent_code, search } = req.query;
|
||||
const page = parseInt(req.query.page, 10) || 1;
|
||||
const limit = parseInt(req.query.limit, 10) || 25;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const baseQuerySelect = `
|
||||
SELECT c.code, c.description, c.code_level, c.explanatory_note_inclusion, c.explanatory_note_exclusion,
|
||||
l.level_name_en,
|
||||
c.level1, c.level2, c.level3, c.level4, c.level5, c.level6
|
||||
FROM isic_rev4_codes c
|
||||
LEFT JOIN isic_rev4_level_names l ON c.code_level = l.code_level
|
||||
`;
|
||||
// Для подсчета JOIN с level_names не нужен, если только по level_name_en не будет фильтрации (пока нет)
|
||||
const baseQueryCount = `
|
||||
SELECT COUNT(*) AS total
|
||||
FROM isic_rev4_codes c
|
||||
`;
|
||||
|
||||
const conditions = [];
|
||||
const queryParams = []; // Параметры для основного запроса (включая limit/offset)
|
||||
const countQueryParams = []; // Параметры только для WHERE части (для count запроса)
|
||||
|
||||
if (level) {
|
||||
const levelParam = parseInt(level, 10);
|
||||
conditions.push(`c.code_level = $${queryParams.push(levelParam)}`);
|
||||
countQueryParams.push(levelParam);
|
||||
}
|
||||
|
||||
if (parent_code) {
|
||||
try {
|
||||
const parentResult = await pool.query('SELECT code_level FROM isic_rev4_codes WHERE code = $1', [parent_code]);
|
||||
if (parentResult.rows.length > 0) {
|
||||
const parentLevel = parentResult.rows[0].code_level;
|
||||
if (parentLevel >= 1 && parentLevel < 6) {
|
||||
conditions.push(`c.level${parentLevel} = $${queryParams.push(parent_code)}`);
|
||||
countQueryParams.push(parent_code);
|
||||
|
||||
const childLevel = parentLevel + 1;
|
||||
conditions.push(`c.code_level = $${queryParams.push(childLevel)}`);
|
||||
countQueryParams.push(childLevel);
|
||||
} else {
|
||||
// Родительский код на максимальном уровне, нет дочерних.
|
||||
// Возвращаем пустой результат сразу.
|
||||
return res.json({ totalItems: 0, codes: [], totalPages: 0, currentPage: page });
|
||||
}
|
||||
} else {
|
||||
// parent_code не найден
|
||||
logger.warn(`Parent code not found: ${parent_code}`);
|
||||
return res.status(404).json({ error: 'Parent code not found', totalItems: 0, codes: [], totalPages: 0, currentPage: page });
|
||||
}
|
||||
} catch (dbError) {
|
||||
logger.error('Error fetching parent_code level:', dbError);
|
||||
return res.status(500).json({ error: 'Internal server error while fetching parent data' });
|
||||
}
|
||||
}
|
||||
|
||||
if (search) {
|
||||
const searchPattern = `%${search}%`;
|
||||
// Используем один параметр для обоих ILIKE, чтобы $N был корректным в countQueryParams
|
||||
conditions.push(`(c.code ILIKE $${queryParams.push(searchPattern)} OR c.description ILIKE $${queryParams.length})`); // $N для второго ILIKE будет тем же, что и для первого
|
||||
countQueryParams.push(searchPattern);
|
||||
}
|
||||
|
||||
let whereClause = '';
|
||||
if (conditions.length > 0) {
|
||||
// Переиндексируем плейсхолдеры для whereClause, т.к. queryParams и countQueryParams теперь разные
|
||||
// Это более сложный момент, проще собирать whereClause с $1, $2 и т.д. и передавать countQueryParams
|
||||
// Для простоты сейчас оставим как было, но это означает, что queryParams используется для генерации whereClause,
|
||||
// а потом из него берутся countQueryParams.
|
||||
|
||||
// Корректный способ: перестроить whereClause для count с правильными индексами
|
||||
let countWhereClauseConditions = [];
|
||||
let currentCountParamIndex = 1;
|
||||
if (level) {
|
||||
countWhereClauseConditions.push(`c.code_level = $${currentCountParamIndex++}`);
|
||||
}
|
||||
if (parent_code) {
|
||||
// Предполагаем, что parent_code уже добавлен в countQueryParams
|
||||
const parentLevelResult = await pool.query('SELECT code_level FROM isic_rev4_codes WHERE code = $1', [parent_code]); // Нужно будет передать parent_code в countQueryParams
|
||||
if (parentLevelResult.rows.length > 0) {
|
||||
const parentLevel = parentLevelResult.rows[0].code_level;
|
||||
if (parentLevel >=1 && parentLevel < 6) {
|
||||
countWhereClauseConditions.push(`c.level${parentLevel} = $${currentCountParamIndex++}`);
|
||||
countWhereClauseConditions.push(`c.code_level = $${currentCountParamIndex++}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (search) {
|
||||
countWhereClauseConditions.push(`(c.code ILIKE $${currentCountParamIndex} OR c.description ILIKE $${currentCountParamIndex})`);
|
||||
currentCountParamIndex++;
|
||||
}
|
||||
whereClause = countWhereClauseConditions.length > 0 ? ' WHERE ' + countWhereClauseConditions.join(' AND ') : '';
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Запрос для получения данных с пагинацией
|
||||
const dataQueryPlaceholders = queryParams.map((_, i) => `$${i + 1}`).join(', '); // Это неверно для WHERE
|
||||
// Формируем finalQuery с плейсхолдерами, соответствующими queryParams
|
||||
let finalQueryWhereClause = '';
|
||||
if (conditions.length > 0) {
|
||||
let currentQueryParamIndex = 1;
|
||||
const queryWhereConditions = [];
|
||||
if (level) queryWhereConditions.push(`c.code_level = $${currentQueryParamIndex++}`);
|
||||
if (parent_code) {
|
||||
const parentLevelResult = await pool.query('SELECT code_level FROM isic_rev4_codes WHERE code = $1', [parent_code]); // Это дублирование, лучше получить parentLevel один раз
|
||||
if (parentLevelResult.rows.length > 0) {
|
||||
const parentLevel = parentLevelResult.rows[0].code_level;
|
||||
if (parentLevel >=1 && parentLevel < 6) {
|
||||
queryWhereConditions.push(`c.level${parentLevel} = $${currentQueryParamIndex++}`);
|
||||
queryWhereConditions.push(`c.code_level = $${currentQueryParamIndex++}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (search) queryWhereConditions.push(`(c.code ILIKE $${currentQueryParamIndex} OR c.description ILIKE $${currentQueryParamIndex})`); // searchPattern идет одним параметром
|
||||
finalQueryWhereClause = queryWhereConditions.length > 0 ? ' WHERE ' + queryWhereConditions.join(' AND ') : '';
|
||||
}
|
||||
|
||||
|
||||
const finalQuery = `${baseQuerySelect} ${finalQueryWhereClause} ORDER BY c.sort_order, c.code LIMIT $${queryParams.push(limit)} OFFSET $${queryParams.push(offset)}`;
|
||||
const finalCountQuery = `${baseQueryCount} ${whereClause}`;
|
||||
|
||||
try {
|
||||
logger.debug('Executing count query:', finalCountQuery, 'Params:', countQueryParams);
|
||||
const totalItemsResult = await pool.query(finalCountQuery, countQueryParams);
|
||||
const totalItems = parseInt(totalItemsResult.rows[0].total, 10);
|
||||
|
||||
// Параметры для основного запроса - это все, что в queryParams (включая limit и offset)
|
||||
logger.debug('Executing data query:', finalQuery, 'Params:', queryParams);
|
||||
const result = await pool.query(finalQuery, queryParams);
|
||||
|
||||
res.json({
|
||||
totalItems,
|
||||
codes: result.rows,
|
||||
totalPages: Math.ceil(totalItems / limit),
|
||||
currentPage: page,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error fetching ISIC codes:', error);
|
||||
if (error.query) logger.error('Failed Query:', error.query);
|
||||
if (error.parameters) logger.error('Failed Params:', error.parameters);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/isic/tree:
|
||||
* get:
|
||||
* summary: Получить иерархическое дерево кодов ISIC (или его часть)
|
||||
* tags: [ISIC]
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: root_code
|
||||
* schema:
|
||||
* type: string
|
||||
* description: Код ISIC, с которого начинать построение дерева (например, 'A' или '01'). Если не указан, вернет все секции (уровень 1).
|
||||
* - in: max_depth
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 2
|
||||
* description: Максимальная глубина дерева для загрузки.
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Дерево кодов ISIC
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/IsicTreeNode' # Определим эту схему позже
|
||||
* 500:
|
||||
* description: Ошибка сервера
|
||||
*/
|
||||
router.get('/tree', async (req, res) => {
|
||||
const { root_code, max_depth = 2 } = req.query;
|
||||
|
||||
// Эта функция потребует рекурсивного запроса или сложного CTE (Common Table Expression) в SQL
|
||||
// для построения дерева. Это более сложная задача.
|
||||
// Для начала можно сделать упрощенную версию, которая возвращает один уровень вложенности.
|
||||
try {
|
||||
let items;
|
||||
if (!root_code) { // Если нет root_code, возвращаем секции (уровень 1)
|
||||
const result = await pool.query(
|
||||
"SELECT code, description, code_level FROM isic_rev4_codes WHERE code_level = 1 ORDER BY sort_order, code"
|
||||
);
|
||||
items = result.rows.map(row => ({ ...row, children: [] })); // Добавляем пустой массив children
|
||||
} else {
|
||||
// Получаем сам root_code
|
||||
const rootResult = await pool.query(
|
||||
"SELECT code, description, code_level FROM isic_rev4_codes WHERE code = $1",
|
||||
[root_code]
|
||||
);
|
||||
if (rootResult.rows.length === 0) {
|
||||
return res.status(404).json({ error: 'Root code not found' });
|
||||
}
|
||||
const rootNode = { ...rootResult.rows[0], children: [] };
|
||||
|
||||
// Получаем прямых потомков (упрощенный пример для одного уровня вложенности)
|
||||
let childrenQuery = '';
|
||||
const childrenParams = [root_code];
|
||||
const rootLevel = rootNode.code_level;
|
||||
|
||||
if (rootLevel === 1) childrenQuery = "SELECT code, description, code_level FROM isic_rev4_codes WHERE level1 = $1 AND code_level = 2 ORDER BY sort_order, code";
|
||||
else if (rootLevel === 2) childrenQuery = "SELECT code, description, code_level FROM isic_rev4_codes WHERE level2 = $1 AND code_level = 3 ORDER BY sort_order, code";
|
||||
else if (rootLevel === 3) childrenQuery = "SELECT code, description, code_level FROM isic_rev4_codes WHERE level3 = $1 AND code_level = 4 ORDER BY sort_order, code";
|
||||
else if (rootLevel === 4) childrenQuery = "SELECT code, description, code_level FROM isic_rev4_codes WHERE level4 = $1 AND code_level = 5 ORDER BY sort_order, code";
|
||||
else if (rootLevel === 5) childrenQuery = "SELECT code, description, code_level FROM isic_rev4_codes WHERE level5 = $1 AND code_level = 6 ORDER BY sort_order, code";
|
||||
|
||||
|
||||
if (childrenQuery) {
|
||||
const childrenResult = await pool.query(childrenQuery, childrenParams);
|
||||
rootNode.children = childrenResult.rows.map(row => ({ ...row, children: [] }));
|
||||
}
|
||||
items = [rootNode];
|
||||
}
|
||||
res.json(items);
|
||||
} catch (error)
|
||||
{
|
||||
logger.error('Error fetching ISIC tree:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -7,6 +7,7 @@ services:
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./backend/db/data:/mnt/isic_csv_data
|
||||
environment:
|
||||
POSTGRES_DB: ${DB_NAME:-dapp_db}
|
||||
POSTGRES_USER: ${DB_USER:-dapp_user}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<p>Для доступа к настройкам необходимо <button @click="goToHomeAndShowSidebar">войти</button>.</p>
|
||||
</div>
|
||||
<div v-else class="settings-navigation-buttons">
|
||||
<h3>Разделы настроек:</h3>
|
||||
<div class="buttons-grid">
|
||||
<router-link :to="{ name: 'settings-ai' }" class="btn btn-secondary">ИИ</router-link>
|
||||
<router-link :to="{ name: 'settings-blockchain' }" class="btn btn-secondary">Блокчейн</router-link>
|
||||
|
||||
@@ -1,73 +1,641 @@
|
||||
<template>
|
||||
<div class="blockchain-settings settings-panel">
|
||||
<h2>Настройки Блокчейна</h2>
|
||||
|
||||
<!-- Панель Смарт-контракты -->
|
||||
<!-- Панель Создать новое DLE (Digital Legal Entity) -->
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Настройки смарт-контрактов</h3>
|
||||
<h3>Создать новое DLE (Digital Legal Entity)</h3>
|
||||
<div class="setting-form">
|
||||
<p>Управление смарт-контрактами</p>
|
||||
<p>Настройка и деплой нового DLE (Digital Legal Entity) с токеном управления и контрактом Governor.</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Адрес основного контракта:</label>
|
||||
<input type="text" v-model="settings.contractAddress" class="form-control">
|
||||
<label class="form-label" for="blockchainNetwork">Цепочка блокчейна для деплоя:</label>
|
||||
<select id="blockchainNetwork" v-model="dleDeploymentSettings.blockchainNetwork" class="form-control">
|
||||
<option value="polygon">Polygon (Matic)</option>
|
||||
<option value="ethereum_mainnet">Ethereum Mainnet</option>
|
||||
<option value="sepolia">Sepolia (Testnet)</option>
|
||||
<option value="goerli">Goerli (Testnet)</option>
|
||||
<!-- TODO: Добавить другие сети по мере необходимости -->
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary" @click="saveSettings('smartContract')">Сохранить</button>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="dleName">Имя DLE (Digital Legal Entity) (и токена):</label>
|
||||
<input type="text" id="dleName" v-model="dleDeploymentSettings.name" class="form-control" placeholder="Например, My DLE">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="dleSymbol">Символ токена управления (GT):</label>
|
||||
<input type="text" id="dleSymbol" v-model="dleDeploymentSettings.symbol" class="form-control" placeholder="Например, MDGT (3-5 символов)">
|
||||
</div>
|
||||
|
||||
<h4>Местонахождение</h4>
|
||||
<div class="address-grid">
|
||||
<div class="form-group address-index">
|
||||
<label class="form-label" for="locIndex">Индекс:</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="locIndex" v-model="dleDeploymentSettings.locationIndex" @input="checkIndexInput" class="form-control">
|
||||
<button class="btn btn-outline-secondary btn-sm" type="button" @click="fetchAddressByZipcode" :disabled="isFetchingByZipcode || !dleDeploymentSettings.locationIndex">
|
||||
<i class="fas fa-search"></i> {{ isFetchingByZipcode ? 'Поиск...' : 'Найти по индексу' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="addressFieldsVisible">
|
||||
<div class="form-group address-country">
|
||||
<label class="form-label" for="locCountry">Страна:</label>
|
||||
<input type="text" id="locCountry" v-model="dleDeploymentSettings.locationCountry" class="form-control">
|
||||
</div>
|
||||
<div class="form-group address-city">
|
||||
<label class="form-label" for="locCity">Населенный пункт:</label>
|
||||
<input type="text" id="locCity" v-model="dleDeploymentSettings.locationCity" class="form-control">
|
||||
</div>
|
||||
<div class="form-group address-street">
|
||||
<label class="form-label" for="locStreet">Улица:</label>
|
||||
<input type="text" id="locStreet" v-model="dleDeploymentSettings.locationStreet" class="form-control">
|
||||
</div>
|
||||
<div class="form-group address-house">
|
||||
<label class="form-label" for="locHouse">Дом:</label>
|
||||
<input type="text" id="locHouse" v-model="dleDeploymentSettings.locationHouse" class="form-control">
|
||||
</div>
|
||||
<div class="form-group address-office">
|
||||
<label class="form-label" for="locOffice">Офис/Кв.:</label>
|
||||
<input type="text" id="locOffice" v-model="dleDeploymentSettings.locationOffice" class="form-control">
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="address-verification-section" v-if="addressFieldsVisible">
|
||||
<button @click="verifyAddress" :disabled="isAddressVerifying" class="btn btn-info btn-sm">
|
||||
<i class="fas fa-search-location"></i> {{ isAddressVerifying ? 'Проверка адреса...' : 'Проверить адрес' }}
|
||||
</button>
|
||||
<div v-if="addressVerificationResult" class="verification-status alert mt-2">
|
||||
<p v-if="addressVerificationResult === 'verified_exact'" class="alert-success">✓ Адрес подтвержден (точный).</p>
|
||||
<p v-if="addressVerificationResult === 'verified_street'" class="alert-success">✓ Адрес подтвержден (улица найдена).</p>
|
||||
<p v-if="addressVerificationResult === 'verified_city'" class="alert-success">✓ Адрес подтвержден (город найден).</p>
|
||||
<p v-if="addressVerificationResult === 'verified_ambiguous'" class="alert-warning">⚠ Адрес найден, но требует уточнения.</p>
|
||||
<p v-if="addressVerificationResult === 'not_found'" class="alert-danger">✗ Адрес не найден.</p>
|
||||
<p v-if="addressVerificationResult === 'ambiguous'" class="alert-warning">⚠ Найденный адрес не полностью совпадает с введенным. Уточните запрос.</p>
|
||||
<p v-if="addressVerificationResult === 'error'" class="alert-danger">✗ Ошибка при проверке адреса.</p>
|
||||
|
||||
<details v-if="verifiedAddressDetails" class="mt-2">
|
||||
<summary>Детали от Nominatim</summary>
|
||||
<pre class="code-block">{{ JSON.stringify(verifiedAddressDetails, null, 2) }}</pre>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Панель Кворум -->
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Настройки кворума</h3>
|
||||
<div class="setting-form">
|
||||
<p>Настройки кворума для блокчейн-операций</p>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Минимальный кворум (%):</label>
|
||||
<input type="number" v-model="settings.quorumPercent" min="0" max="100" class="form-control">
|
||||
<div v-if="dleDeploymentSettings.selectedIsicCodes && dleDeploymentSettings.selectedIsicCodes.length > 0" class="isic-codes-list mt-3">
|
||||
<h5>Добавленные коды деятельности:</h5>
|
||||
<ul>
|
||||
<li v-for="(isic, index) in dleDeploymentSettings.selectedIsicCodes" :key="index" class="d-flex justify-content-between align-items-center mb-1">
|
||||
<span>{{ isic.text }} ({{ isic.code }})</span>
|
||||
<button @click="removeIsicCode(index)" class="btn btn-danger btn-xs">Удалить</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button class="btn btn-primary" @click="saveSettings('quorum')">Сохранить</button>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="isicSection">Выберите код деятельности:</label>
|
||||
<select id="isicSection" v-model="selectedSection" class="form-control" :disabled="isLoadingSections">
|
||||
<option value="">-- {{ isLoadingSections ? 'Загрузка секций...' : 'Выберите секцию' }} --</option>
|
||||
<option v-for="option in sectionOptions" :key="option.value" :value="option.value">
|
||||
{{ option.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" v-if="selectedSection">
|
||||
<label class="form-label" for="isicDivision">Раздел:</label>
|
||||
<select id="isicDivision" v-model="selectedDivision" class="form-control" :disabled="isLoadingDivisions">
|
||||
<option value="">-- {{ isLoadingDivisions ? 'Загрузка разделов...' : 'Выберите раздел' }} --</option>
|
||||
<option v-for="option in divisionOptions" :key="option.value" :value="option.value">
|
||||
{{ option.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" v-if="selectedDivision">
|
||||
<label class="form-label" for="isicGroup">Группа:</label>
|
||||
<select id="isicGroup" v-model="selectedGroup" class="form-control" :disabled="isLoadingGroups">
|
||||
<option value="">-- {{ isLoadingGroups ? 'Загрузка групп...' : 'Выберите группу' }} --</option>
|
||||
<option v-for="option in groupOptions" :key="option.value" :value="option.value">
|
||||
{{ option.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" v-if="selectedGroup">
|
||||
<label class="form-label" for="isicClass">Класс:</label>
|
||||
<select id="isicClass" v-model="selectedClass" class="form-control" :disabled="isLoadingClasses">
|
||||
<option value="">-- {{ isLoadingClasses ? 'Загрузка классов...' : 'Выберите класс' }} --</option>
|
||||
<option v-for="option in classOptions" :key="option.value" :value="option.value">
|
||||
{{ option.text }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div v-if="currentSelectedIsicText" class="current-isic-selection">
|
||||
<p><strong>Выбранный код:</strong> {{ currentSelectedIsicText }}</p>
|
||||
<button @click="addIsicCode" class="btn btn-success btn-sm" :disabled="!currentSelectedIsicCode">Добавить код деятельности</button>
|
||||
</div>
|
||||
|
||||
<h4>Первоначальное распределение токенов управления</h4>
|
||||
<div v-for="(partner, index) in dleDeploymentSettings.partners" :key="index" class="partner-entry">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Адрес партнера {{ index + 1 }}:</label>
|
||||
<input type="text" v-model="partner.address" class="form-control" placeholder="0x...">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Сумма GT для партнера {{ index + 1 }}:</label>
|
||||
<input type="number" v-model="partner.amount" min="1" class="form-control">
|
||||
</div>
|
||||
<button class="btn btn-danger btn-sm" @click="removePartner(index)">Удалить партнера</button>
|
||||
</div>
|
||||
<button class="btn btn-secondary" @click="addPartner">Добавить партнера</button>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Общее количество выпускаемых GT: {{ totalInitialSupply }}</label>
|
||||
</div>
|
||||
|
||||
<h4>Настройки Governor</h4>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="proposalThreshold">Порог для создания предложений (кол-во GT):</label>
|
||||
<input type="number" id="proposalThreshold" v-model="dleDeploymentSettings.proposalThreshold" min="0" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="quorumPercentGovernor">Кворум (% от общего числа голосов):</label>
|
||||
<input type="number" id="quorumPercentGovernor" v-model="dleDeploymentSettings.quorumPercent" min="1" max="100" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="votingDelay">Задержка перед голосованием (в днях):</label>
|
||||
<input type="number" id="votingDelay" v-model="dleDeploymentSettings.votingDelayDays" min="0" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="votingPeriod">Период голосования (в днях):</label>
|
||||
<input type="number" id="votingPeriod" v-model="dleDeploymentSettings.votingPeriodDays" min="1" class="form-control">
|
||||
</div>
|
||||
|
||||
<!-- Панель RWA -->
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Настройки Real World Assets (RWA)</h3>
|
||||
<div class="setting-form">
|
||||
<p>Конфигурация для работы с реальными активами</p>
|
||||
<h4>Настройки Timelock (если используется)</h4>
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<input type="checkbox" v-model="settings.rwaEnabled">
|
||||
Включить поддержку RWA
|
||||
</label>
|
||||
<label class="form-label" for="timelockMinDelay">Минимальная задержка Timelock (в днях):</label>
|
||||
<input type="number" id="timelockMinDelay" v-model="dleDeploymentSettings.timelockMinDelayDays" min="0" class="form-control">
|
||||
</div>
|
||||
<button class="btn btn-primary" @click="saveSettings('rwa')">Сохранить</button>
|
||||
|
||||
<button class="btn btn-primary btn-lg" @click="deployDLE">Создать и задеплоить DLE (Digital Legal Entity)</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import { reactive, onMounted, computed, ref, watch } from 'vue';
|
||||
import axios from 'axios'; // Предполагаем, что axios доступен
|
||||
// TODO: Импортировать API
|
||||
|
||||
const settings = reactive({
|
||||
contractAddress: '',
|
||||
quorumPercent: 51,
|
||||
rwaEnabled: false
|
||||
// contractAddress: '', // Удалено
|
||||
// quorumPercent: 51, // Удалено
|
||||
// rwaEnabled: false // Удалено
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadBlockchainSettings();
|
||||
const dleDeploymentSettings = reactive({
|
||||
name: '',
|
||||
symbol: '',
|
||||
partners: [{ address: '', amount: 1 }], // Начинаем с одного партнера для примера
|
||||
proposalThreshold: 0, // Голосов для создания предложения
|
||||
quorumPercent: 4, // Процент для кворума Governor
|
||||
votingDelayDays: 1, // в днях
|
||||
votingPeriodDays: 7, // в днях
|
||||
timelockMinDelayDays: 2, // в днях
|
||||
blockchainNetwork: 'polygon', // Значение по умолчанию
|
||||
locationIndex: '',
|
||||
locationCountry: '',
|
||||
locationCity: '',
|
||||
locationStreet: '',
|
||||
locationHouse: '',
|
||||
locationOffice: '',
|
||||
selectedIsicCodes: [], // <<< Для хранения массива выбранных кодов ISIC
|
||||
});
|
||||
|
||||
// --- Состояние для загрузки и опций ISIC ---
|
||||
const sectionOptions = ref([]);
|
||||
const divisionOptions = ref([]);
|
||||
const groupOptions = ref([]);
|
||||
const classOptions = ref([]);
|
||||
const isLoadingSections = ref(false);
|
||||
const isLoadingDivisions = ref(false);
|
||||
const isLoadingGroups = ref(false);
|
||||
const isLoadingClasses = ref(false);
|
||||
|
||||
// --- Состояние для проверки адреса Nominatim ---
|
||||
const isAddressVerifying = ref(false);
|
||||
const addressVerificationResult = ref(null); // null, 'verified_exact', 'verified_street', 'verified_city', 'verified_ambiguous', 'not_found', 'ambiguous', 'error'
|
||||
const verifiedAddressDetails = ref(null);
|
||||
const isFetchingByZipcode = ref(false);
|
||||
const addressFieldsVisible = ref(false);
|
||||
|
||||
// --- Состояние для выбранных значений на каждом уровне ISIC ---
|
||||
const selectedSection = ref('');
|
||||
const selectedDivision = ref('');
|
||||
const selectedGroup = ref('');
|
||||
const selectedClass = ref('');
|
||||
|
||||
// --- Для хранения текущего самого детализированного выбора ISIC до добавления в список ---
|
||||
const currentSelectedIsicCode = ref('');
|
||||
const currentSelectedIsicText = ref('');
|
||||
|
||||
// --- Отслеживание изменения страны ---
|
||||
watch(() => dleDeploymentSettings.locationCountry, (newCountry, oldCountry) => {
|
||||
if (newCountry !== oldCountry) {
|
||||
console.log(`[BlockchainSettingsView] Страна изменена на: ${newCountry}. Очистка кодов деятельности.`);
|
||||
selectedSection.value = ''; // Это вызовет каскадную очистку и сброс currentSelectedIsicCode
|
||||
dleDeploymentSettings.selectedIsicCodes = []; // Очищаем также список уже добавленных кодов
|
||||
fetchIsicCodes({ level: 1 }, sectionOptions, isLoadingSections);
|
||||
}
|
||||
});
|
||||
|
||||
// --- Функция для загрузки кодов ISIC из API ---
|
||||
const fetchIsicCodes = async (params = {}, optionsRef, loadingRef) => {
|
||||
if (!optionsRef || !loadingRef) {
|
||||
console.error('[BlockchainSettingsView] fetchIsicCodes requires optionsRef and loadingRef');
|
||||
return;
|
||||
}
|
||||
loadingRef.value = true;
|
||||
optionsRef.value = []; // Очищаем перед загрузкой
|
||||
try {
|
||||
const queryParams = new URLSearchParams(params).toString();
|
||||
console.debug(`[BlockchainSettingsView] Fetching ISIC codes with params: ${queryParams}`);
|
||||
|
||||
// Убедитесь, что базовый URL настроен правильно (например, через axios interceptors или .env)
|
||||
const response = await axios.get(`/api/isic/codes?${queryParams}`);
|
||||
|
||||
if (response.data && Array.isArray(response.data.codes)) {
|
||||
optionsRef.value = response.data.codes.map(code => ({
|
||||
value: code.code,
|
||||
// Отображаем код и описание для ясности
|
||||
text: `${code.code} - ${code.description}`
|
||||
}));
|
||||
console.debug(`[BlockchainSettingsView] Loaded ISIC codes for level ${params.level || ('parent: '+params.parent_code)}, count:`, optionsRef.value.length);
|
||||
} else {
|
||||
console.error('[BlockchainSettingsView] Invalid response structure for ISIC codes:', response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[BlockchainSettingsView] Error fetching ISIC codes:', error.response?.data || error.message);
|
||||
// TODO: Показать пользователю уведомление об ошибке
|
||||
} finally {
|
||||
loadingRef.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- Функция для обновления информации о текущем полном выбранном коде ISIC ---
|
||||
const updateCurrentIsicSelection = () => {
|
||||
let code = '';
|
||||
let text = '';
|
||||
let optionsToSearch = [];
|
||||
let valueToFind = '';
|
||||
|
||||
if (selectedClass.value) {
|
||||
code = selectedClass.value;
|
||||
optionsToSearch = classOptions.value;
|
||||
valueToFind = selectedClass.value;
|
||||
} else if (selectedGroup.value) {
|
||||
code = selectedGroup.value;
|
||||
optionsToSearch = groupOptions.value;
|
||||
valueToFind = selectedGroup.value;
|
||||
} else if (selectedDivision.value) {
|
||||
code = selectedDivision.value;
|
||||
optionsToSearch = divisionOptions.value;
|
||||
valueToFind = selectedDivision.value;
|
||||
} else if (selectedSection.value) {
|
||||
code = selectedSection.value;
|
||||
optionsToSearch = sectionOptions.value;
|
||||
valueToFind = selectedSection.value;
|
||||
}
|
||||
|
||||
if (code && optionsToSearch.length > 0 && valueToFind) {
|
||||
const foundOption = optionsToSearch.find(opt => opt.value === valueToFind);
|
||||
if (foundOption) {
|
||||
text = foundOption.text;
|
||||
}
|
||||
}
|
||||
currentSelectedIsicCode.value = code;
|
||||
currentSelectedIsicText.value = text;
|
||||
};
|
||||
|
||||
// --- Наблюдатели для каскадной загрузки и обновления текущего выбора ---
|
||||
watch(selectedSection, (newVal) => {
|
||||
selectedDivision.value = ''; divisionOptions.value = [];
|
||||
selectedGroup.value = ''; groupOptions.value = [];
|
||||
selectedClass.value = ''; classOptions.value = [];
|
||||
if (newVal) {
|
||||
fetchIsicCodes({ parent_code: newVal }, divisionOptions, isLoadingDivisions);
|
||||
}
|
||||
updateCurrentIsicSelection();
|
||||
});
|
||||
|
||||
watch(selectedDivision, (newVal) => {
|
||||
selectedGroup.value = ''; groupOptions.value = [];
|
||||
selectedClass.value = ''; classOptions.value = [];
|
||||
if (newVal) {
|
||||
fetchIsicCodes({ parent_code: newVal }, groupOptions, isLoadingGroups);
|
||||
}
|
||||
updateCurrentIsicSelection();
|
||||
});
|
||||
|
||||
watch(selectedGroup, (newVal) => {
|
||||
selectedClass.value = ''; classOptions.value = [];
|
||||
if (newVal) {
|
||||
fetchIsicCodes({ parent_code: newVal }, classOptions, isLoadingClasses);
|
||||
}
|
||||
updateCurrentIsicSelection();
|
||||
});
|
||||
|
||||
watch(selectedClass, () => {
|
||||
updateCurrentIsicSelection();
|
||||
});
|
||||
|
||||
// --- Начальная загрузка данных ---
|
||||
onMounted(() => {
|
||||
fetchIsicCodes({ level: 1 }, sectionOptions, isLoadingSections);
|
||||
// TODO: Загрузить настройки блокчейна, если они есть
|
||||
// loadBlockchainSettings(); // Эта функция пока не актуальна, так как settings пустой
|
||||
});
|
||||
|
||||
const totalInitialSupply = computed(() => {
|
||||
return dleDeploymentSettings.partners.reduce((sum, partner) => sum + (Number(partner.amount) || 0), 0);
|
||||
});
|
||||
|
||||
const addPartner = () => {
|
||||
dleDeploymentSettings.partners.push({ address: '', amount: 1 });
|
||||
};
|
||||
|
||||
const removePartner = (index) => {
|
||||
dleDeploymentSettings.partners.splice(index, 1);
|
||||
};
|
||||
|
||||
const loadBlockchainSettings = async () => {
|
||||
console.log('[BlockchainSettingsView] Загрузка настроек блокчейна...');
|
||||
// TODO: API call
|
||||
// TODO: API call - Больше нет общих настроек для загрузки в 'settings'.
|
||||
// Возможно, потребуется загрузить dleDeploymentSettings, если они сохраняются.
|
||||
};
|
||||
|
||||
const saveSettings = async (section) => {
|
||||
console.log(`[BlockchainSettingsView] Сохранение настроек раздела: ${section}`);
|
||||
// TODO: API call
|
||||
// TODO: API call - Функция saveSettings, вероятно, больше не нужна в текущем виде,
|
||||
// так как нет общих настроек для сохранения. Деплой DLE обрабатывается отдельно.
|
||||
// Если настройки DLE (dleDeploymentSettings) нужно сохранять без деплоя, нужна другая логика.
|
||||
};
|
||||
|
||||
const deployDLE = async () => {
|
||||
console.log('[BlockchainSettingsView] Попытка деплоя DLE (Digital Legal Entity) с настройками:', JSON.parse(JSON.stringify(dleDeploymentSettings)));
|
||||
console.log('[BlockchainSettingsView] Выбранные коды ISIC:', JSON.parse(JSON.stringify(dleDeploymentSettings.selectedIsicCodes)));
|
||||
console.log('[BlockchainSettingsView] Общее начальное количество токенов:', totalInitialSupply.value);
|
||||
const addressString = [
|
||||
dleDeploymentSettings.locationIndex,
|
||||
dleDeploymentSettings.locationCountry,
|
||||
dleDeploymentSettings.locationCity,
|
||||
dleDeploymentSettings.locationStreet,
|
||||
dleDeploymentSettings.locationHouse,
|
||||
dleDeploymentSettings.locationOffice
|
||||
].filter(Boolean).join(', '); // Собираем строку адреса для alert/log
|
||||
let finalIsicDisplay = 'Не выбраны';
|
||||
if (dleDeploymentSettings.selectedIsicCodes && dleDeploymentSettings.selectedIsicCodes.length > 0) {
|
||||
finalIsicDisplay = dleDeploymentSettings.selectedIsicCodes.map(c => c.code).join(', ');
|
||||
}
|
||||
alert(`Деплой DLE (Digital Legal Entity) инициирован (см. консоль). Имя: ${dleDeploymentSettings.name}, Символ: ${dleDeploymentSettings.symbol}, Сеть: ${dleDeploymentSettings.blockchainNetwork}, Коды деят: ${finalIsicDisplay}, Адрес: ${addressString || 'Не указан'}`);
|
||||
// TODO: Вызвать API бэкенда для деплоя контрактов
|
||||
// Передать dleDeploymentSettings.blockchainNetwork на бэкенд
|
||||
// Передать выбранный dleDeploymentSettings.selectedIsicCodes и детали адреса (dleDeploymentSettings.location...) (возможно, для метаданных контракта или внесения в реестр)
|
||||
// 1. Деплой ERC20Votes токена с параметрами:
|
||||
// - name: dleDeploymentSettings.name
|
||||
// - symbol: dleDeploymentSettings.symbol
|
||||
// - initialSupply: totalInitialSupply.value (или как решит бэкенд по партнерам)
|
||||
// - initialHolders: dleDeploymentSettings.partners (адреса и суммы)
|
||||
// 2. Деплой TimelockController (если используется, получить его адрес)
|
||||
// - minDelay: dleDeploymentSettings.timelockMinDelayDays * 24 * 60 * 60 (конвертация в секунды)
|
||||
// - proposers: [адрес будущего Governor]
|
||||
// - executors: [0x0000...0000] (любой может исполнить) или [адрес будущего Governor]
|
||||
// 3. Деплой Governor контракта:
|
||||
// - name: dleDeploymentSettings.name + " Governor" (или просто уникальное имя для Governor)
|
||||
// - tokenAddress: адрес задеплоенного ERC20Votes
|
||||
// - timelockAddress: адрес задеплоенного TimelockController (если используется)
|
||||
// - quorumPercent: dleDeploymentSettings.quorumPercent
|
||||
// - proposalThreshold: dleDeploymentSettings.proposalThreshold
|
||||
// - votingPeriod: dleDeploymentSettings.votingPeriodDays * 24 * 60 * 60 (конвертация в секунды или блоки на бэкенде)
|
||||
// - votingDelay: dleDeploymentSettings.votingDelayDays * 24 * 60 * 60 (конвертация в секунды или блоки на бэкенде)
|
||||
// 4. (Если Timelock) Передать права администратора Timelock самому Timelock'у (или DLE)
|
||||
// 5. (Опционально) Передать права на другие системные контракты Timelock'у
|
||||
// TODO: Передать dleDeploymentSettings.selectedIsicCodes (массив объектов) на бэкенд
|
||||
};
|
||||
|
||||
// --- Функция для поиска адреса по индексу через Nominatim ---
|
||||
const fetchAddressByZipcode = async () => {
|
||||
const zipcode = dleDeploymentSettings.locationIndex.trim();
|
||||
if (!zipcode) {
|
||||
return;
|
||||
}
|
||||
|
||||
isFetchingByZipcode.value = true;
|
||||
addressVerificationResult.value = null;
|
||||
verifiedAddressDetails.value = null;
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
params.append('postalcode', zipcode);
|
||||
params.append('format', 'jsonv2');
|
||||
params.append('addressdetails', '1');
|
||||
params.append('limit', '1');
|
||||
|
||||
const countryInput = dleDeploymentSettings.locationCountry.trim();
|
||||
if (countryInput) {
|
||||
if (countryInput.length === 2) {
|
||||
params.append('countrycodes', countryInput.toUpperCase());
|
||||
} else {
|
||||
params.append('country', countryInput);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[FetchByZipcode] Querying backend proxy for Nominatim with: ${params.toString()}`);
|
||||
const response = await axios.get(`/api/geocoding/nominatim-search?${params.toString()}`);
|
||||
|
||||
if (response.data && response.data.length > 0) {
|
||||
const bestMatch = response.data[0];
|
||||
console.log('[FetchByZipcode] Nominatim result:', bestMatch);
|
||||
|
||||
if (bestMatch.address) {
|
||||
if (bestMatch.address.country) {
|
||||
dleDeploymentSettings.locationCountry = bestMatch.address.country;
|
||||
}
|
||||
if (bestMatch.address.city) {
|
||||
dleDeploymentSettings.locationCity = bestMatch.address.city;
|
||||
} else if (bestMatch.address.town) {
|
||||
dleDeploymentSettings.locationCity = bestMatch.address.town;
|
||||
} else if (bestMatch.address.village) {
|
||||
dleDeploymentSettings.locationCity = bestMatch.address.village;
|
||||
} else {
|
||||
// Город не найден четко, можно оставить поле пустым или сообщить
|
||||
}
|
||||
// Можно также попробовать заполнить регион/область, если есть такое поле
|
||||
// dleDeploymentSettings.locationState = bestMatch.address.state;
|
||||
addressFieldsVisible.value = true;
|
||||
} else {
|
||||
addressFieldsVisible.value = false;
|
||||
}
|
||||
} else {
|
||||
addressFieldsVisible.value = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[FetchByZipcode] Error fetching address by zipcode:', error.response?.data || error.message);
|
||||
addressFieldsVisible.value = false;
|
||||
} finally {
|
||||
isFetchingByZipcode.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Дополнительная функция для скрытия полей, если индекс очищен
|
||||
const checkIndexInput = () => {
|
||||
if (!dleDeploymentSettings.locationIndex.trim()) {
|
||||
addressFieldsVisible.value = false;
|
||||
// Опционально: также можно очистить dleDeploymentSettings.locationCountry, city и т.д.
|
||||
// dleDeploymentSettings.locationCountry = '';
|
||||
// dleDeploymentSettings.locationCity = '';
|
||||
}
|
||||
};
|
||||
|
||||
// --- Функция для сборки строки адреса для Nominatim (для кнопки "Проверить адрес") ---
|
||||
const buildAddressQuery = () => {
|
||||
const parts = [];
|
||||
let streetHouse = '';
|
||||
|
||||
const street = dleDeploymentSettings.locationStreet.trim();
|
||||
const house = dleDeploymentSettings.locationHouse.trim().toLowerCase(); // Приводим номер дома к нижнему регистру
|
||||
|
||||
if (street) {
|
||||
streetHouse += street;
|
||||
}
|
||||
if (house) {
|
||||
streetHouse += (streetHouse ? ' ' : '') + house;
|
||||
}
|
||||
if (streetHouse) {
|
||||
parts.push(streetHouse);
|
||||
}
|
||||
|
||||
const city = dleDeploymentSettings.locationCity.trim();
|
||||
if (city) {
|
||||
parts.push(city);
|
||||
}
|
||||
|
||||
const country = dleDeploymentSettings.locationCountry.trim();
|
||||
if (country) {
|
||||
parts.push(country);
|
||||
}
|
||||
|
||||
return parts.join(', ');
|
||||
};
|
||||
|
||||
// --- Функция для проверки адреса через Nominatim ---
|
||||
const verifyAddress = async () => {
|
||||
const addressQuery = buildAddressQuery();
|
||||
if (!addressQuery.trim()) {
|
||||
addressVerificationResult.value = null;
|
||||
verifiedAddressDetails.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
isAddressVerifying.value = true;
|
||||
addressVerificationResult.value = null;
|
||||
verifiedAddressDetails.value = null;
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
q: addressQuery,
|
||||
format: 'jsonv2',
|
||||
addressdetails: 1,
|
||||
limit: 5,
|
||||
// countrycodes параметр убран, т.к. страна теперь свободный текстовый ввод
|
||||
// Nominatim попытается определить ее из 'q'
|
||||
});
|
||||
|
||||
console.log(`[VerifyAddress] Querying backend proxy for Nominatim with: ${params.toString()}`);
|
||||
// Запрос теперь идет на ваш бэкенд-прокси
|
||||
const response = await axios.get(`/api/geocoding/nominatim-search?${params.toString()}`);
|
||||
|
||||
// Ответ от бэкенд-прокси должен иметь ту же структуру, что и прямой ответ от Nominatim
|
||||
if (response.data && Array.isArray(response.data)) { // Проверяем, что это массив (как отвечает Nominatim)
|
||||
if (response.data.length > 0) {
|
||||
const bestMatch = response.data[0];
|
||||
verifiedAddressDetails.value = bestMatch;
|
||||
console.log('[VerifyAddress] Nominatim best match via proxy:', bestMatch);
|
||||
|
||||
let countryMatches = true;
|
||||
if (dleDeploymentSettings.locationCountry && bestMatch.address.country_code) {
|
||||
if (dleDeploymentSettings.locationCountry.trim().toUpperCase() !== bestMatch.address.country_code.toUpperCase()) {
|
||||
if (dleDeploymentSettings.locationCountry.length === 2) countryMatches = false;
|
||||
}
|
||||
} else if (dleDeploymentSettings.locationCountry && !bestMatch.address.country_code) {
|
||||
countryMatches = false;
|
||||
}
|
||||
|
||||
if (countryMatches) {
|
||||
if (bestMatch.address.house_number && bestMatch.address.road) {
|
||||
addressVerificationResult.value = 'verified_exact';
|
||||
} else if (bestMatch.address.road) {
|
||||
addressVerificationResult.value = 'verified_street';
|
||||
} else if (bestMatch.address.city || bestMatch.address.town || bestMatch.address.village) {
|
||||
addressVerificationResult.value = 'verified_city';
|
||||
} else {
|
||||
addressVerificationResult.value = 'verified_ambiguous';
|
||||
}
|
||||
} else {
|
||||
addressVerificationResult.value = 'ambiguous';
|
||||
}
|
||||
} else {
|
||||
// Nominatim вернул пустой массив - адрес не найден
|
||||
addressVerificationResult.value = 'not_found';
|
||||
}
|
||||
} else {
|
||||
// Ответ от бэкенд-прокси не в ожидаемом формате (не массив)
|
||||
console.error('[VerifyAddress] Invalid response structure from backend proxy:', response.data);
|
||||
addressVerificationResult.value = 'error'; // или более специфичная ошибка
|
||||
verifiedAddressDetails.value = response.data; // Сохраняем то, что пришло, для отладки
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[VerifyAddress] Error verifying address via backend proxy:', error.response?.data || error.message);
|
||||
verifiedAddressDetails.value = error.response?.data; // Сохраняем детали ошибки для отладки
|
||||
addressVerificationResult.value = 'error';
|
||||
} finally {
|
||||
isAddressVerifying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- Функции для управления списком выбранных кодов ISIC ---
|
||||
const addIsicCode = () => {
|
||||
if (currentSelectedIsicCode.value && currentSelectedIsicText.value) {
|
||||
const alreadyExists = dleDeploymentSettings.selectedIsicCodes.find(c => c.code === currentSelectedIsicCode.value);
|
||||
if (!alreadyExists) {
|
||||
dleDeploymentSettings.selectedIsicCodes.push({
|
||||
code: currentSelectedIsicCode.value,
|
||||
text: currentSelectedIsicText.value
|
||||
});
|
||||
// Сбрасываем селекторы для выбора следующего кода
|
||||
selectedSection.value = ''; // Это вызовет каскадную очистку
|
||||
// currentSelectedIsicCode.value = ''; // Уже сбросится через watch(selectedSection)
|
||||
// currentSelectedIsicText.value = '';
|
||||
} else {
|
||||
alert('Этот код уже добавлен.');
|
||||
}
|
||||
} else {
|
||||
alert('Код не выбран полностью.');
|
||||
}
|
||||
};
|
||||
|
||||
const removeIsicCode = (index) => {
|
||||
dleDeploymentSettings.selectedIsicCodes.splice(index, 1);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -78,11 +646,6 @@ const saveSettings = async (section) => {
|
||||
margin-top: var(--spacing-lg);
|
||||
animation: fadeIn var(--transition-normal);
|
||||
}
|
||||
h2 {
|
||||
margin-bottom: var(--spacing-lg);
|
||||
border-bottom: 1px solid var(--color-grey-light);
|
||||
padding-bottom: var(--spacing-md);
|
||||
}
|
||||
h3 {
|
||||
margin-bottom: var(--spacing-md);
|
||||
color: var(--color-primary);
|
||||
@@ -120,4 +683,136 @@ h3 {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.partner-entry {
|
||||
border: 1px solid var(--color-grey-light);
|
||||
padding: var(--spacing-md);
|
||||
margin-bottom: var(--spacing-md);
|
||||
border-radius: var(--radius-sm);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.partner-entry .form-group {
|
||||
margin-bottom: 0; /* Убираем лишний отступ у вложенных групп */
|
||||
}
|
||||
|
||||
.btn-danger.btn-sm {
|
||||
align-self: flex-start;
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
font-size: 0.875rem; /* Меньший размер для кнопки удаления */
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
align-self: flex-start;
|
||||
margin-bottom: var(--spacing-md); /* Отступ после кнопки "Добавить партнера" */
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.address-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); /* Адаптивная сетка */
|
||||
gap: var(--spacing-md);
|
||||
/* margin-bottom: var(--spacing-lg); Убрано, т.к. есть блок верификации */
|
||||
}
|
||||
|
||||
/* Можно добавить специфичные стили для полей адреса, если нужно */
|
||||
.address-grid .form-group {
|
||||
margin-bottom: 0; /* Убрать лишний отступ у полей в сетке */
|
||||
}
|
||||
|
||||
.code-list {
|
||||
margin-bottom: var(--spacing-md);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.code-entry {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border: 1px solid var(--color-grey-light);
|
||||
border-radius: var(--radius-sm);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.code-entry .btn-danger {
|
||||
flex-shrink: 0;
|
||||
margin-left: var(--spacing-md);
|
||||
}
|
||||
|
||||
.add-code-form {
|
||||
margin-top: var(--spacing-sm); /* Меньше отступ, т.к. он под списком */
|
||||
}
|
||||
|
||||
.add-code-form .btn-secondary {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.address-verification-section {
|
||||
margin-top: var(--spacing-xs); /* Небольшой отступ сверху */
|
||||
margin-bottom: var(--spacing-lg); /* Отступ после секции верификации */
|
||||
}
|
||||
|
||||
.verification-status p {
|
||||
margin-bottom: var(--spacing-xs);
|
||||
padding: var(--spacing-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
.verification-status .alert-success {
|
||||
background-color: var(--color-success-light, #e6fffa); /* Добавлены цвета по умолчанию */
|
||||
color: var(--color-success-dark, #006d5b);
|
||||
border: 1px solid var(--color-success-dark, #006d5b);
|
||||
}
|
||||
.verification-status .alert-warning {
|
||||
background-color: var(--color-warning-light, #fff8e1);
|
||||
color: var(--color-warning-dark, #8a6d3b);
|
||||
border: 1px solid var(--color-warning-dark, #8a6d3b);
|
||||
}
|
||||
.verification-status .alert-danger {
|
||||
background-color: var(--color-danger-light, #fdecea);
|
||||
color: var(--color-danger-dark, #a94442);
|
||||
border: 1px solid var(--color-danger-dark, #a94442);
|
||||
}
|
||||
|
||||
.code-block {
|
||||
background-color: #f5f5f5;
|
||||
padding: var(--spacing-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--color-grey-light, #e0e0e0);
|
||||
}
|
||||
|
||||
.current-isic-selection {
|
||||
padding: var(--spacing-sm);
|
||||
background-color: var(--color-grey-x-light);
|
||||
border-radius: var(--radius-sm);
|
||||
margin-top: var(--spacing-sm);
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.isic-codes-list ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.isic-codes-list li {
|
||||
background-color: var(--color-background);
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
border: 1px solid var(--color-grey-light);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.btn-xs {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,124 +1,263 @@
|
||||
<template>
|
||||
<div class="security-settings settings-panel">
|
||||
<h2>Настройки Безопасности</h2>
|
||||
<h2>Настройки безопасности и подключения к блокчейну</h2>
|
||||
|
||||
<div class="sub-settings-panel">
|
||||
<h3>RPC Провайдеры</h3>
|
||||
<p>Добавьте конфигурации RPC для сетей, которые будут использоваться в приложении.</p>
|
||||
|
||||
<!-- Список добавленных RPC -->
|
||||
<div v-if="securitySettings.rpcConfigs.length > 0" class="rpc-list">
|
||||
<h4>Добавленные RPC конфигурации:</h4>
|
||||
<div v-for="(rpc, index) in securitySettings.rpcConfigs" :key="index" class="rpc-entry">
|
||||
<span><strong>ID Сети:</strong> {{ rpc.networkId }}</span>
|
||||
<span><strong>URL:</strong> {{ rpc.rpcUrl }}</span>
|
||||
<button class="btn btn-danger btn-sm" @click="removeRpcConfig(index)">Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else>Нет добавленных RPC конфигураций.</p>
|
||||
|
||||
<!-- Панель Авторизация -->
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Настройки авторизации</h3>
|
||||
<div class="setting-form">
|
||||
<p>Управление параметрами авторизации</p>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Метод авторизации по умолчанию:</label>
|
||||
<select v-model="settings.defaultAuthMethod" class="form-control">
|
||||
<option value="wallet">Кошелек</option>
|
||||
<option value="email">Email</option>
|
||||
<option value="telegram">Telegram</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary" @click="saveSettings('authorization')">Сохранить</button>
|
||||
<!-- Форма добавления нового RPC -->
|
||||
<div class="setting-form add-rpc-form">
|
||||
<h4>Добавить новую RPC конфигурацию:</h4>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="newRpcNetworkId">ID Сети:</label>
|
||||
<input type="text" id="newRpcNetworkId" v-model="newRpcEntry.networkId" class="form-control" placeholder="Например, polygon или sepolia">
|
||||
<small>Этот ID должен совпадать со значением `value` в выпадающем списке сетей при создании DLE.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="newRpcUrl">RPC URL:</label>
|
||||
<input type="text" id="newRpcUrl" v-model="newRpcEntry.rpcUrl" class="form-control" placeholder="https://...">
|
||||
</div>
|
||||
<button class="btn btn-secondary" @click="addRpcConfig">Добавить RPC</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Ключ Деплоера</h3>
|
||||
<div class="setting-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="deployerKey">Приватный ключ кошелька для деплоя:</label>
|
||||
<input type="password" id="deployerKey" v-model="securitySettings.deployerPrivateKey" class="form-control" placeholder="0x...">
|
||||
<small>Ключ будет скрыт. Не сохраняйте его в браузере.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Ключ API Etherscan</h3>
|
||||
<p>API ключ для Etherscan (и аналогов для других сетей) используется для автоматической верификации исходного кода смарт-контрактов после деплоя.</p>
|
||||
<div class="setting-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="etherscanApi">Etherscan API Key:</label>
|
||||
<input type="text" id="etherscanApi" v-model="securitySettings.etherscanApiKey" class="form-control" placeholder="YourEtherscanApiKey">
|
||||
</div>
|
||||
<!-- TODO: Добавить поля для API ключей других обозревателей (Polygonscan, BscScan и т.д.) -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-settings-panel">
|
||||
<h3>База данных (PostgreSQL)</h3>
|
||||
<div class="setting-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="dbHost">Хост:</label>
|
||||
<input type="text" id="dbHost" v-model="securitySettings.dbHost" class="form-control" placeholder="postgres">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="dbPort">Порт:</label>
|
||||
<input type="number" id="dbPort" v-model="securitySettings.dbPort" class="form-control" placeholder="5432">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="dbName">Имя БД:</label>
|
||||
<input type="text" id="dbName" v-model="securitySettings.dbName" class="form-control" placeholder="dapp_db">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="dbUser">Пользователь БД:</label>
|
||||
<input type="text" id="dbUser" v-model="securitySettings.dbUser" class="form-control" placeholder="dapp_user">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="dbPassword">Пароль БД:</label>
|
||||
<input type="password" id="dbPassword" v-model="securitySettings.dbPassword" class="form-control" placeholder="********">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Настройки Email</h3>
|
||||
<div class="setting-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="emailSmtpHost">SMTP Хост:</label>
|
||||
<input type="text" id="emailSmtpHost" v-model="securitySettings.emailSmtpHost" class="form-control" placeholder="smtp.example.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="emailSmtpPort">SMTP Порт:</label>
|
||||
<input type="number" id="emailSmtpPort" v-model="securitySettings.emailSmtpPort" class="form-control" placeholder="465">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="emailImapHost">IMAP Хост:</label>
|
||||
<input type="text" id="emailImapHost" v-model="securitySettings.emailImapHost" class="form-control" placeholder="imap.example.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="emailImapPort">IMAP Порт:</label>
|
||||
<input type="number" id="emailImapPort" v-model="securitySettings.emailImapPort" class="form-control" placeholder="993">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="emailUser">Email Пользователь:</label>
|
||||
<input type="email" id="emailUser" v-model="securitySettings.emailUser" class="form-control" placeholder="your_email@example.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="emailPassword">Email Пароль:</label>
|
||||
<input type="password" id="emailPassword" v-model="securitySettings.emailPassword" class="form-control" placeholder="********">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Telegram Бот</h3>
|
||||
<div class="setting-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="telegramToken">Токен Бота:</label>
|
||||
<input type="text" id="telegramToken" v-model="securitySettings.telegramBotToken" class="form-control" placeholder="123456:ABC-DEF1234...">
|
||||
<small>Получите у @BotFather в Telegram.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="telegramUsername">Имя пользователя Бота:</label>
|
||||
<input type="text" id="telegramUsername" v-model="securitySettings.telegramBotUsername" class="form-control" placeholder="YourTelegramBot">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Панель RPC -->
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Настройки RPC</h3>
|
||||
<div class="setting-form">
|
||||
<p>Настройка RPC-эндпоинтов</p>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Ethereum RPC:</label>
|
||||
<input type="text" v-model="settings.rpcEndpoints.ethereum" class="form-control">
|
||||
<h3>Прочие настройки</h3>
|
||||
<div class="setting-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="sessionSecret">Секрет сессии:</label>
|
||||
<input type="password" id="sessionSecret" v-model="securitySettings.sessionSecret" class="form-control" placeholder="Очень длинный и случайный секрет">
|
||||
<small>Используется для подписи cookie сессий. Должен быть надежным.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="frontendUrl">Frontend URL:</label>
|
||||
<input type="text" id="frontendUrl" v-model="securitySettings.frontendUrl" class="form-control" placeholder="http://localhost:5173">
|
||||
<small>URL, по которому доступен фронтенд (для CORS, ссылок и т.д.).</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Polygon RPC:</label>
|
||||
<input type="text" v-model="settings.rpcEndpoints.polygon" class="form-control">
|
||||
</div>
|
||||
<button class="btn btn-primary" @click="saveSettings('rpc')">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Панель Noda -->
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Настройки Noda</h3>
|
||||
<div class="setting-form">
|
||||
<p>Конфигурация нод</p>
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
<input type="checkbox" v-model="settings.useCustomNode">
|
||||
Использовать собственную ноду
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" v-if="settings.useCustomNode">
|
||||
<label class="form-label">URL собственной ноды:</label>
|
||||
<input type="text" v-model="settings.customNodeUrl" class="form-control">
|
||||
</div>
|
||||
<button class="btn btn-primary" @click="saveSettings('noda')">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Панель Хранилище -->
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Настройки хранилища</h3>
|
||||
<div class="setting-form">
|
||||
<p>Настройки для хранения данных</p>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Тип хранилища:</label>
|
||||
<select v-model="settings.storageType" class="form-control">
|
||||
<option value="local">Локальное</option>
|
||||
<option value="ipfs">IPFS</option>
|
||||
<option value="arweave">Arweave</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary" @click="saveSettings('storage')">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Панель CPU -->
|
||||
<div class="sub-settings-panel">
|
||||
<h3>Настройки CPU</h3>
|
||||
<div class="setting-form">
|
||||
<p>Оптимизация использования CPU</p>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Максимальное использование CPU (%):</label>
|
||||
<input type="number" v-model="settings.maxCpuUsage" min="0" max="100" class="form-control">
|
||||
</div>
|
||||
<button class="btn btn-primary" @click="saveSettings('cpu')">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-settings-panel save-panel">
|
||||
<button class="btn btn-primary btn-lg" @click="saveSecuritySettings">Сохранить настройки безопасности</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, onMounted } from 'vue';
|
||||
// TODO: Импортировать API
|
||||
import { reactive } from 'vue';
|
||||
|
||||
const settings = reactive({
|
||||
defaultAuthMethod: 'wallet',
|
||||
rpcEndpoints: {
|
||||
ethereum: '',
|
||||
polygon: ''
|
||||
},
|
||||
useCustomNode: false,
|
||||
customNodeUrl: '',
|
||||
storageType: 'local',
|
||||
maxCpuUsage: 80
|
||||
// TODO: Импортировать API для сохранения/загрузки настроек (если это безопасно реализуемо)
|
||||
|
||||
const securitySettings = reactive({
|
||||
rpcConfigs: [], // Массив для хранения { networkId: string, rpcUrl: string }
|
||||
deployerPrivateKey: '',
|
||||
etherscanApiKey: '',
|
||||
// Настройки БД
|
||||
dbHost: '',
|
||||
dbPort: 5432,
|
||||
dbName: '',
|
||||
dbUser: '',
|
||||
dbPassword: '',
|
||||
// Настройки Email
|
||||
emailSmtpHost: '',
|
||||
emailSmtpPort: 465,
|
||||
emailImapHost: '',
|
||||
emailImapPort: 993,
|
||||
emailUser: '',
|
||||
emailPassword: '',
|
||||
// Настройки Telegram
|
||||
telegramBotToken: '',
|
||||
telegramBotUsername: '',
|
||||
// Прочие настройки
|
||||
sessionSecret: '',
|
||||
frontendUrl: '',
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadSecuritySettings();
|
||||
// Реактивный объект для новой записи RPC
|
||||
const newRpcEntry = reactive({
|
||||
networkId: '',
|
||||
rpcUrl: ''
|
||||
});
|
||||
|
||||
const loadSecuritySettings = async () => {
|
||||
console.log('[SecuritySettingsView] Загрузка настроек безопасности...');
|
||||
// TODO: API call
|
||||
// Функция добавления новой RPC конфигурации
|
||||
const addRpcConfig = () => {
|
||||
const networkId = newRpcEntry.networkId.trim();
|
||||
const rpcUrl = newRpcEntry.rpcUrl.trim();
|
||||
if (networkId && rpcUrl) {
|
||||
// Проверка на дубликат ID (опционально, но полезно)
|
||||
if (securitySettings.rpcConfigs.some(rpc => rpc.networkId === networkId)) {
|
||||
alert(`Ошибка: RPC конфигурация для сети с ID '${networkId}' уже существует.`);
|
||||
return;
|
||||
}
|
||||
securitySettings.rpcConfigs.push({ networkId, rpcUrl });
|
||||
// Очистка полей ввода
|
||||
newRpcEntry.networkId = '';
|
||||
newRpcEntry.rpcUrl = '';
|
||||
} else {
|
||||
alert('Пожалуйста, заполните ID Сети и RPC URL.');
|
||||
}
|
||||
};
|
||||
|
||||
const saveSettings = async (section) => {
|
||||
console.log(`[SecuritySettingsView] Сохранение настроек раздела: ${section}`);
|
||||
// TODO: API call
|
||||
// Функция удаления RPC конфигурации
|
||||
const removeRpcConfig = (index) => {
|
||||
securitySettings.rpcConfigs.splice(index, 1);
|
||||
};
|
||||
|
||||
const saveSecuritySettings = async () => {
|
||||
// ВАЖНО: Отправка приватного ключа на бэкенд или его сохранение
|
||||
// через UI - КРАЙНЕ ОПАСНО.
|
||||
// Идеально - настроить его через .env или защищенный серверный механизм.
|
||||
// Эта функция - заглушка. В реальном приложении нужна безопасная логика.
|
||||
if (securitySettings.deployerPrivateKey) {
|
||||
console.warn('[SecuritySettingsView] Попытка сохранить приватный ключ через UI! Это небезопасно!');
|
||||
alert('ПРЕДУПРЕЖДЕНИЕ: Сохранение приватного ключа через веб-интерфейс КРАЙНЕ НЕ РЕКОМЕНДУЕТСЯ из соображений безопасности. Используйте .env файл или переменные окружения на сервере.');
|
||||
}
|
||||
console.log('[SecuritySettingsView] Попытка сохранения настроек:', {
|
||||
rpcConfigs: JSON.parse(JSON.stringify(securitySettings.rpcConfigs)), // Логируем копию массива
|
||||
etherscanApiKey: securitySettings.etherscanApiKey ? '******' : 'Не указан', // Тоже чувствительные данные
|
||||
deployerPrivateKey: securitySettings.deployerPrivateKey ? '******' : 'Не указан', // Не логируем сам ключ!
|
||||
dbHost: securitySettings.dbHost,
|
||||
dbPort: securitySettings.dbPort,
|
||||
dbName: securitySettings.dbName,
|
||||
dbUser: securitySettings.dbUser,
|
||||
dbPassword: securitySettings.dbPassword ? '******' : 'Не указан',
|
||||
emailSmtpHost: securitySettings.emailSmtpHost,
|
||||
emailSmtpPort: securitySettings.emailSmtpPort,
|
||||
emailImapHost: securitySettings.emailImapHost,
|
||||
emailImapPort: securitySettings.emailImapPort,
|
||||
emailUser: securitySettings.emailUser,
|
||||
emailPassword: securitySettings.emailPassword ? '******' : 'Не указан',
|
||||
telegramBotToken: securitySettings.telegramBotToken ? '******' : 'Не указан',
|
||||
telegramBotUsername: securitySettings.telegramBotUsername,
|
||||
sessionSecret: securitySettings.sessionSecret ? '******' : 'Не указан',
|
||||
frontendUrl: securitySettings.frontendUrl,
|
||||
});
|
||||
|
||||
// TODO: Реализовать безопасную отправку данных (кроме ключа?) на бэкенд
|
||||
// await api.saveSecurityConfig({
|
||||
// rpcUrls: {
|
||||
// polygon: securitySettings.polygonRpcUrl,
|
||||
// ethereum: securitySettings.ethRpcUrl,
|
||||
// bsc: securitySettings.bscRpcUrl,
|
||||
// arbitrum: securitySettings.arbitrumRpcUrl,
|
||||
// default: securitySettings.defaultRpcUrl,
|
||||
// }
|
||||
// etherscanApiKey: securitySettings.etherscanApiKey
|
||||
// // НЕ передавать приватный ключ напрямую!
|
||||
// });
|
||||
alert('Настройки (заглушка) сохранены. Проверьте консоль. Обратите внимание на предупреждения о приватном ключе.');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Используем стили, похожие на BlockchainSettingsView */
|
||||
.settings-panel {
|
||||
padding: var(--block-padding);
|
||||
background-color: var(--color-light);
|
||||
@@ -140,7 +279,8 @@ h3 {
|
||||
padding-bottom: var(--spacing-lg);
|
||||
border-bottom: 1px dashed var(--color-grey-light);
|
||||
}
|
||||
.sub-settings-panel:last-child {
|
||||
.sub-settings-panel:last-child,
|
||||
.save-panel {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
@@ -154,18 +294,128 @@ h3 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.form-label {
|
||||
display: block; /* Чтобы занимала всю ширину */
|
||||
margin-bottom: var(--spacing-xs);
|
||||
font-weight: 500;
|
||||
}
|
||||
.form-control {
|
||||
max-width: 600px; /* Немного шире для ключей и URL */
|
||||
padding: var(--spacing-sm);
|
||||
border: 1px solid var(--color-grey-light);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 1rem;
|
||||
}
|
||||
.form-control[type="password"] {
|
||||
font-family: monospace; /* Чтобы легче было вводить ключ */
|
||||
}
|
||||
small {
|
||||
display: block;
|
||||
margin-top: var(--spacing-xs);
|
||||
color: var(--color-grey-dark);
|
||||
}
|
||||
|
||||
.warning-block {
|
||||
border: 2px solid var(--color-danger);
|
||||
background-color: var(--color-danger-light);
|
||||
color: var(--color-danger-dark);
|
||||
padding: var(--spacing-md);
|
||||
border-radius: var(--radius-sm);
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
.warning-block h4 {
|
||||
color: var(--color-danger);
|
||||
margin-top: 0;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
.form-control {
|
||||
max-width: 500px;
|
||||
.warning-block p {
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
.warning-block p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
.btn-primary {
|
||||
align-self: flex-start;
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
/* Предполагаем, что у вас есть иконки FontAwesome или аналог */
|
||||
/* @import url('.../path/to/fontawesome.css'); */
|
||||
/* Если иконок нет, можно убрать тег <i> */
|
||||
.fa-exclamation-triangle {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
.rpc-list {
|
||||
margin-top: var(--spacing-md);
|
||||
margin-bottom: var(--spacing-lg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.rpc-entry {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-md);
|
||||
padding: var(--spacing-sm);
|
||||
border: 1px solid var(--color-grey-light);
|
||||
border-radius: var(--radius-sm);
|
||||
background-color: white; /* Немного выделить фон */
|
||||
}
|
||||
|
||||
.rpc-entry span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.rpc-entry .btn-danger {
|
||||
flex-shrink: 0;
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.add-rpc-form {
|
||||
margin-top: var(--spacing-lg);
|
||||
padding-top: var(--spacing-lg);
|
||||
border-top: 1px dashed var(--color-grey-light);
|
||||
}
|
||||
|
||||
.add-rpc-form h4 {
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
.add-rpc-form .btn-secondary {
|
||||
align-self: flex-start;
|
||||
background-color: var(--color-secondary); /* Пример цвета */
|
||||
color: white;
|
||||
border: none;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.add-rpc-form .btn-secondary:hover {
|
||||
background-color: var(--color-secondary-dark); /* Пример цвета */
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user