weather&bottom
This commit is contained in:
+3
-3
@@ -18,7 +18,7 @@
|
||||
"category": "Высшая",
|
||||
"departmentName": "Технический отдел",
|
||||
"departmentCode": "101",
|
||||
"organization": "ООО ТехноЦентр",
|
||||
"organization": "Филиал «Завод Химволокно» ОАО «Гродно Азот»",
|
||||
"identNumber": "123456789"
|
||||
},
|
||||
{
|
||||
@@ -32,7 +32,7 @@
|
||||
"category": "1",
|
||||
"departmentName": "Бухгалтерия",
|
||||
"departmentCode": "202",
|
||||
"organization": "ООО ТехноЦентр",
|
||||
"organization": "Филиал «Завод Химволокно» ОАО «Гродно Азот»",
|
||||
"identNumber": "987654321"
|
||||
},
|
||||
{
|
||||
@@ -46,7 +46,7 @@
|
||||
"category": "2",
|
||||
"departmentName": "IT-отдел",
|
||||
"departmentCode": "303",
|
||||
"organization": "ООО ТехноЦентр",
|
||||
"organization": "Филиал «Завод Химволокно» ОАО «Гродно Азот»",
|
||||
"identNumber": "555555555"
|
||||
}
|
||||
],
|
||||
|
||||
Generated
+149
-61
@@ -17,10 +17,11 @@
|
||||
"mammoth": "^1.9.1",
|
||||
"mysql2": "^3.14.2",
|
||||
"node-cron": "^4.2.1",
|
||||
"winston": "^3.17.0"
|
||||
"winston": "^3.17.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.22"
|
||||
"nodemon": "^3.1.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@colors/colors": {
|
||||
@@ -91,6 +92,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/adler-32": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
|
||||
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
@@ -130,11 +140,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
@@ -213,14 +226,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
|
||||
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
@@ -274,6 +289,19 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/cfb": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
|
||||
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"crc-32": "~1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
@@ -299,6 +327,15 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/codepage": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
||||
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz",
|
||||
@@ -345,13 +382,6 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
|
||||
@@ -415,6 +445,18 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/crc-32": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"crc32": "bin/crc32.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
@@ -674,6 +716,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/frac": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
|
||||
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
||||
@@ -1133,16 +1184,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"version": "10.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
|
||||
"integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
"brace-expansion": "^5.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
@@ -1204,19 +1258,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "2.0.22",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz",
|
||||
"integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==",
|
||||
"version": "3.1.14",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
|
||||
"integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
"debug": "^3.2.7",
|
||||
"debug": "^4",
|
||||
"ignore-by-default": "^1.0.1",
|
||||
"minimatch": "^3.1.2",
|
||||
"minimatch": "^10.2.1",
|
||||
"pstree.remy": "^1.1.8",
|
||||
"semver": "^5.7.1",
|
||||
"simple-update-notifier": "^1.0.7",
|
||||
"semver": "^7.5.3",
|
||||
"simple-update-notifier": "^2.0.0",
|
||||
"supports-color": "^5.5.0",
|
||||
"touch": "^3.1.0",
|
||||
"undefsafe": "^2.0.5"
|
||||
@@ -1225,23 +1279,13 @@
|
||||
"nodemon": "bin/nodemon.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/nodemon"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@@ -1492,13 +1536,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"version": "7.8.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
|
||||
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
@@ -1631,26 +1678,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
|
||||
"integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"semver": "~7.0.0"
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier/node_modules/semver": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
|
||||
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
@@ -1674,6 +1711,18 @@
|
||||
"url": "https://github.com/mysqljs/sql-escaper?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ssf": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
|
||||
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"frac": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/stack-trace": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||
@@ -1900,12 +1949,51 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/wmf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
|
||||
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/word": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
|
||||
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/xlsx": {
|
||||
"version": "0.18.5",
|
||||
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
|
||||
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"cfb": "~1.2.1",
|
||||
"codepage": "~1.15.0",
|
||||
"crc-32": "~1.2.1",
|
||||
"ssf": "~0.11.2",
|
||||
"wmf": "~1.0.1",
|
||||
"word": "~0.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"xlsx": "bin/xlsx.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz",
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
"mammoth": "^1.9.1",
|
||||
"mysql2": "^3.14.2",
|
||||
"node-cron": "^4.2.1",
|
||||
"winston": "^3.17.0"
|
||||
"winston": "^3.17.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^2.0.22"
|
||||
"nodemon": "^3.1.14"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
+369
-117
@@ -1,27 +1,34 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const fs = require('fs');
|
||||
const fsPromises = require('fs').promises;
|
||||
const { connectDB } = require('./config/db');
|
||||
const { logger } = require('./config/logger');
|
||||
const handbookRoutes = require('./routes/handbook');
|
||||
const employeeRoutes = require('./routes/employee');
|
||||
const helmet = require('helmet');
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const XLSX = require('xlsx');
|
||||
require('dotenv').config({ path: path.join(__dirname, '../config.env') });
|
||||
|
||||
const limiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 100
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 📚 ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
|
||||
// ============================================
|
||||
|
||||
async function readJson(filePath) {
|
||||
const data = await fs.readFile(filePath, 'utf8');
|
||||
const data = await fsPromises.readFile(filePath, 'utf8');
|
||||
return JSON.parse(data);
|
||||
}
|
||||
|
||||
async function pathExists(filePath) {
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
await fsPromises.access(filePath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
@@ -38,33 +45,34 @@ async function connectToNetworkShare(config) {
|
||||
const username = config.networkCredentials.username;
|
||||
const password = config.networkCredentials.password;
|
||||
const domain = config.networkCredentials.domain;
|
||||
const netUseCommand = `net use \\${payslipsPath} ${password} /user:${domain ? domain + '\\' : ''}${username}`;
|
||||
const netUseCommand = 'net use \\\\' + payslipsPath + ' ' + password + ' /user:' + (domain ? domain + '\\' : '') + username;
|
||||
|
||||
logger.info(`Attempting to connect to network share with credentials`);
|
||||
const { stdout, stderr } = await execAsync(netUseCommand);
|
||||
logger.info('Attempting to connect to network share with credentials');
|
||||
const result = await execAsync(netUseCommand);
|
||||
|
||||
if (stderr) {
|
||||
logger.warn(`Network share connection warning: ${stderr}`);
|
||||
if (result.stderr) {
|
||||
logger.warn('Network share connection warning: ' + result.stderr);
|
||||
}
|
||||
|
||||
logger.info(`Network share connection result: ${stdout}`);
|
||||
logger.info('Network share connection result: ' + result.stdout);
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('Failed to connect to network share', { error: error.message });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 🚀 Инициализация Express
|
||||
// 🚀 ИНИЦИАЛИЗАЦИЯ EXPRESS
|
||||
// ============================================
|
||||
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use((req, res, next) => {
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
if (req.method === 'POST' || req.method === 'PUT') {
|
||||
if (!req.is('application/json')) {
|
||||
return res.status(415).json({ message: 'Only JSON allowed' });
|
||||
@@ -73,47 +81,292 @@ app.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 📍 Маршруты API
|
||||
// ============================================
|
||||
app.use(helmet());
|
||||
app.use('/api/', limiter);
|
||||
|
||||
// ============================================
|
||||
// 📍 МАРШРУТЫ API
|
||||
// ============================================
|
||||
|
||||
app.use('/api/handbook', handbookRoutes);
|
||||
app.use('/api/employee', employeeRoutes);
|
||||
|
||||
// ============================================
|
||||
// 🧪 Тестовые эндпоинты
|
||||
// 🧪 ТЕСТОВЫЙ ЭНДПОИНТ
|
||||
// ============================================
|
||||
|
||||
app.get('/api/test', (req, res) => {
|
||||
app.get('/api/test', function(req, res) {
|
||||
res.json({ message: 'API работает' });
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 📊 Эндпоинт для получения статуса системы
|
||||
// 📊 КУРСЫ ВАЛЮТ ИЗ EXCEL
|
||||
// ============================================
|
||||
|
||||
app.get('/api/system-status', async (req, res) => {
|
||||
var EXCEL_PATH = path.join(__dirname, '../rates.xlsx');
|
||||
|
||||
async function createExcelTemplate() {
|
||||
var wb = XLSX.utils.book_new();
|
||||
|
||||
var currencyData = [
|
||||
['currency', 'rate', 'change'],
|
||||
['USD', 3.22, 0.5],
|
||||
['EUR', 3.45, -0.2],
|
||||
['CNY', 0.44, 0.1],
|
||||
['RUB', 0.035, -0.3]
|
||||
];
|
||||
var currencySheet = XLSX.utils.aoa_to_sheet(currencyData);
|
||||
XLSX.utils.book_append_sheet(wb, currencySheet, 'Currency');
|
||||
|
||||
var metalsData = [
|
||||
['metal', 'price', 'change'],
|
||||
['Золото', 1950, 0.8],
|
||||
['Серебро', 23.5, -0.5],
|
||||
['Платина', 920, 0.3],
|
||||
['Палладий', 1450, -0.7]
|
||||
];
|
||||
var metalsSheet = XLSX.utils.aoa_to_sheet(metalsData);
|
||||
XLSX.utils.book_append_sheet(wb, metalsSheet, 'Metals');
|
||||
|
||||
XLSX.writeFile(wb, EXCEL_PATH);
|
||||
logger.info('Excel-шаблон создан');
|
||||
}
|
||||
|
||||
function getRatesFromExcel() {
|
||||
try {
|
||||
const configPath = path.join(__dirname, '../config.json');
|
||||
const config = await readJson(configPath);
|
||||
const { getDBStatus } = require('./config/db');
|
||||
const dbStatus = await getDBStatus();
|
||||
if (!fs.existsSync(EXCEL_PATH)) {
|
||||
return null;
|
||||
}
|
||||
var workbook = XLSX.readFile(EXCEL_PATH);
|
||||
|
||||
let networkAccess = false;
|
||||
let networkError = null;
|
||||
let networkContents = [];
|
||||
var currencySheet = workbook.Sheets['Currency'];
|
||||
var currency = [];
|
||||
if (currencySheet) {
|
||||
var data = XLSX.utils.sheet_to_json(currencySheet);
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var row = data[i];
|
||||
currency.push({
|
||||
currency: String(row.currency || row.Currency || '').toUpperCase(),
|
||||
rate: parseFloat(row.rate || row.Rate || 0),
|
||||
change: parseFloat(row.change || row.Change || 0)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var metalsSheet = workbook.Sheets['Metals'];
|
||||
var metals = [];
|
||||
if (metalsSheet) {
|
||||
var data = XLSX.utils.sheet_to_json(metalsSheet);
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var row = data[i];
|
||||
metals.push({
|
||||
metal: String(row.metal || row.Metal || row.name || row.Name || ''),
|
||||
price: parseFloat(row.price || row.Price || 0),
|
||||
change: parseFloat(row.change || row.Change || 0)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { currency: currency, metals: metals };
|
||||
} catch (error) {
|
||||
logger.error('Ошибка чтения Excel:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📊 GET /api/rates
|
||||
// ============================================
|
||||
|
||||
app.get('/api/rates', async function(req, res) {
|
||||
try {
|
||||
var fileExists = await pathExists(EXCEL_PATH);
|
||||
if (!fileExists) {
|
||||
await createExcelTemplate();
|
||||
}
|
||||
|
||||
var data = getRatesFromExcel();
|
||||
|
||||
if (data && data.currency.length > 0 && data.metals.length > 0) {
|
||||
res.json({
|
||||
currency: data.currency,
|
||||
metals: data.metals,
|
||||
source: 'excel',
|
||||
updated: new Date().toISOString()
|
||||
});
|
||||
} else {
|
||||
res.json({
|
||||
currency: [
|
||||
{ currency: 'USD', rate: 3.22, change: 0.5 },
|
||||
{ currency: 'EUR', rate: 3.45, change: -0.2 },
|
||||
{ currency: 'CNY', rate: 0.44, change: 0.1 },
|
||||
{ currency: 'RUB', rate: 0.035, change: -0.3 }
|
||||
],
|
||||
metals: [
|
||||
{ metal: 'Золото', price: 1950, change: 0.8 },
|
||||
{ metal: 'Серебро', price: 23.5, change: -0.5 },
|
||||
{ metal: 'Платина', price: 920, change: 0.3 },
|
||||
{ metal: 'Палладий', price: 1450, change: -0.7 }
|
||||
],
|
||||
source: 'default'
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Ошибка получения курсов:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 🔄 POST /api/rates
|
||||
// ============================================
|
||||
|
||||
app.post('/api/rates', async function(req, res) {
|
||||
try {
|
||||
var currency = req.body.currency;
|
||||
var metals = req.body.metals;
|
||||
|
||||
var wb = XLSX.utils.book_new();
|
||||
|
||||
if (currency && currency.length > 0) {
|
||||
var currencyData = [
|
||||
['currency', 'rate', 'change']
|
||||
];
|
||||
for (var i = 0; i < currency.length; i++) {
|
||||
currencyData.push([currency[i].currency, currency[i].rate, currency[i].change]);
|
||||
}
|
||||
var currencySheet = XLSX.utils.aoa_to_sheet(currencyData);
|
||||
XLSX.utils.book_append_sheet(wb, currencySheet, 'Currency');
|
||||
}
|
||||
|
||||
if (metals && metals.length > 0) {
|
||||
var metalsData = [
|
||||
['metal', 'price', 'change']
|
||||
];
|
||||
for (var i = 0; i < metals.length; i++) {
|
||||
metalsData.push([metals[i].metal, metals[i].price, metals[i].change]);
|
||||
}
|
||||
var metalsSheet = XLSX.utils.aoa_to_sheet(metalsData);
|
||||
XLSX.utils.book_append_sheet(wb, metalsSheet, 'Metals');
|
||||
}
|
||||
|
||||
XLSX.writeFile(wb, EXCEL_PATH);
|
||||
logger.info('Excel-файл обновлён');
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Курсы обновлены в Excel',
|
||||
path: EXCEL_PATH
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
logger.error('Ошибка при обновлении Excel:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 🔄 POST /api/rates/update-from-nbrb
|
||||
// ============================================
|
||||
|
||||
app.post('/api/rates/update-from-nbrb', async function(req, res) {
|
||||
try {
|
||||
var currencyResponse = await fetch('https://api.nbrb.by/exrates/rates?periodicity=0');
|
||||
var currencyData = await currencyResponse.json();
|
||||
|
||||
var metalResponse = await fetch('https://api.nbrb.by/ingots/prices');
|
||||
var metalData = await metalResponse.json();
|
||||
|
||||
var currency = [
|
||||
{ currency: 'USD', rate: 0, change: 0 },
|
||||
{ currency: 'EUR', rate: 0, change: 0 },
|
||||
{ currency: 'CNY', rate: 0, change: 0 },
|
||||
{ currency: 'RUB', rate: 0, change: 0 }
|
||||
];
|
||||
|
||||
for (var i = 0; i < currencyData.length; i++) {
|
||||
var c = currencyData[i];
|
||||
if (c.Cur_Abbreviation === 'USD') currency[0].rate = c.Cur_OfficialRate || 0;
|
||||
if (c.Cur_Abbreviation === 'EUR') currency[1].rate = c.Cur_OfficialRate || 0;
|
||||
if (c.Cur_Abbreviation === 'CNY') currency[2].rate = c.Cur_OfficialRate || 0;
|
||||
if (c.Cur_Abbreviation === 'RUB') currency[3].rate = c.Cur_OfficialRate || 0;
|
||||
}
|
||||
|
||||
var metals = [
|
||||
{ metal: 'Золото', price: 0, change: 0 },
|
||||
{ metal: 'Серебро', price: 0, change: 0 },
|
||||
{ metal: 'Платина', price: 0, change: 0 },
|
||||
{ metal: 'Палладий', price: 0, change: 0 }
|
||||
];
|
||||
|
||||
for (var i = 0; i < metalData.length; i++) {
|
||||
var m = metalData[i];
|
||||
if (m.MetalId === 0) metals[0].price = m.EntitiesDollars || 0;
|
||||
if (m.MetalId === 1) metals[1].price = m.EntitiesDollars || 0;
|
||||
if (m.MetalId === 2) metals[2].price = m.EntitiesDollars || 0;
|
||||
if (m.MetalId === 3) metals[3].price = m.EntitiesDollars || 0;
|
||||
}
|
||||
|
||||
var wb = XLSX.utils.book_new();
|
||||
|
||||
var currencySheetData = [
|
||||
['currency', 'rate', 'change']
|
||||
];
|
||||
for (var i = 0; i < currency.length; i++) {
|
||||
currencySheetData.push([currency[i].currency, currency[i].rate, currency[i].change]);
|
||||
}
|
||||
var currencySheet = XLSX.utils.aoa_to_sheet(currencySheetData);
|
||||
XLSX.utils.book_append_sheet(wb, currencySheet, 'Currency');
|
||||
|
||||
var metalsSheetData = [
|
||||
['metal', 'price', 'change']
|
||||
];
|
||||
for (var i = 0; i < metals.length; i++) {
|
||||
metalsSheetData.push([metals[i].metal, metals[i].price, metals[i].change]);
|
||||
}
|
||||
var metalsSheet = XLSX.utils.aoa_to_sheet(metalsSheetData);
|
||||
XLSX.utils.book_append_sheet(wb, metalsSheet, 'Metals');
|
||||
|
||||
XLSX.writeFile(wb, EXCEL_PATH);
|
||||
logger.info('Курсы обновлены из НБРБ');
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Курсы обновлены из НБРБ',
|
||||
data: { currency: currency, metals: metals }
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
logger.error('Ошибка при обновлении курсов из НБРБ:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 🗄️ ДРУГИЕ ЭНДПОИНТЫ
|
||||
// ============================================
|
||||
|
||||
app.get('/api/system-status', async function(req, res) {
|
||||
try {
|
||||
var configPath = path.join(__dirname, '../config.json');
|
||||
var config = await readJson(configPath);
|
||||
var db = require('./config/db');
|
||||
var dbStatus = await db.getDBStatus();
|
||||
|
||||
var networkAccess = false;
|
||||
var networkError = null;
|
||||
var networkContents = [];
|
||||
|
||||
try {
|
||||
networkAccess = await pathExists(config.payslipsPath);
|
||||
if (networkAccess) {
|
||||
const stats = await fs.stat(config.payslipsPath);
|
||||
var stats = await fsPromises.stat(config.payslipsPath);
|
||||
networkAccess = stats.isDirectory();
|
||||
if (networkAccess) {
|
||||
networkContents = await fs.readdir(config.payslipsPath);
|
||||
networkContents = await fsPromises.readdir(config.payslipsPath);
|
||||
if (networkContents.length > 10) {
|
||||
networkContents = networkContents.slice(0, 10);
|
||||
networkContents.push(`... и еще ${networkContents.length - 10} элементов`);
|
||||
networkContents.push('... и еще ' + (networkContents.length - 10) + ' элементов');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +375,7 @@ app.get('/api/system-status', async (req, res) => {
|
||||
networkError = err.message;
|
||||
}
|
||||
|
||||
const serverInfo = {
|
||||
var serverInfo = {
|
||||
uptime: process.uptime(),
|
||||
memory: process.memoryUsage(),
|
||||
nodeVersion: process.version,
|
||||
@@ -145,8 +398,8 @@ app.get('/api/system-status', async (req, res) => {
|
||||
},
|
||||
config: {
|
||||
testMode: config.testMode || false,
|
||||
serverHost: config.server?.host || 'localhost',
|
||||
serverPort: config.server?.port || '3001'
|
||||
serverHost: config.server ? config.server.host : 'localhost',
|
||||
serverPort: config.server ? config.server.port : '3001'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -160,22 +413,18 @@ app.get('/api/system-status', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 🌐 Тест сетевой папки
|
||||
// ============================================
|
||||
|
||||
app.get('/api/test-network', async (req, res) => {
|
||||
app.get('/api/test-network', async function(req, res) {
|
||||
try {
|
||||
const configPath = path.join(__dirname, '../config.json');
|
||||
const config = await readJson(configPath);
|
||||
const payslipBasePath = config.payslipsPath;
|
||||
var configPath = path.join(__dirname, '../config.json');
|
||||
var config = await readJson(configPath);
|
||||
var payslipBasePath = config.payslipsPath;
|
||||
|
||||
logger.info(`Testing network connection to: ${payslipBasePath}`);
|
||||
logger.info('Testing network connection to: ' + payslipBasePath);
|
||||
|
||||
const networkConnected = await connectToNetworkShare(config);
|
||||
logger.info(`Network share connection successful: ${networkConnected}`);
|
||||
var networkConnected = await connectToNetworkShare(config);
|
||||
logger.info('Network share connection successful: ' + networkConnected);
|
||||
|
||||
const testResults = {
|
||||
var testResults = {
|
||||
basePath: payslipBasePath,
|
||||
basePathExists: false,
|
||||
basePathAccessible: false,
|
||||
@@ -186,16 +435,16 @@ app.get('/api/test-network', async (req, res) => {
|
||||
|
||||
try {
|
||||
testResults.basePathExists = await pathExists(payslipBasePath);
|
||||
logger.info(`Base path exists: ${testResults.basePathExists}`);
|
||||
logger.info('Base path exists: ' + testResults.basePathExists);
|
||||
|
||||
if (testResults.basePathExists) {
|
||||
const stats = await fs.stat(payslipBasePath);
|
||||
var stats = await fsPromises.stat(payslipBasePath);
|
||||
testResults.basePathAccessible = stats.isDirectory();
|
||||
logger.info(`Base path is directory: ${testResults.basePathAccessible}`);
|
||||
logger.info('Base path is directory: ' + testResults.basePathAccessible);
|
||||
|
||||
if (testResults.basePathAccessible) {
|
||||
testResults.basePathContents = await fs.readdir(payslipBasePath);
|
||||
logger.info(`Base path contents: ${JSON.stringify(testResults.basePathContents)}`);
|
||||
testResults.basePathContents = await fsPromises.readdir(payslipBasePath);
|
||||
logger.info('Base path contents: ' + JSON.stringify(testResults.basePathContents));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -210,55 +459,52 @@ app.get('/api/test-network', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 📄 Получение расчетного листка (с периодом)
|
||||
// ============================================
|
||||
|
||||
app.get('/api/payslip/:period/:fileName', async (req, res) => {
|
||||
app.get('/api/payslip/:period/:fileName', async function(req, res) {
|
||||
try {
|
||||
const { period, fileName } = req.params;
|
||||
const configPath = path.join(__dirname, '../config.json');
|
||||
const config = await readJson(configPath);
|
||||
const payslipBasePath = config.payslipsPath;
|
||||
var period = req.params.period;
|
||||
var fileName = req.params.fileName;
|
||||
var configPath = path.join(__dirname, '../config.json');
|
||||
var config = await readJson(configPath);
|
||||
var payslipBasePath = config.payslipsPath;
|
||||
|
||||
logger.info(`Payslip request - Period: ${period}, File: ${fileName}`);
|
||||
logger.info(`Base path from config: ${payslipBasePath}`);
|
||||
logger.info('Payslip request - Period: ' + period + ', File: ' + fileName);
|
||||
logger.info('Base path from config: ' + payslipBasePath);
|
||||
|
||||
const networkConnected = await connectToNetworkShare(config);
|
||||
logger.info(`Network share connection for payslip: ${networkConnected}`);
|
||||
var networkConnected = await connectToNetworkShare(config);
|
||||
logger.info('Network share connection for payslip: ' + networkConnected);
|
||||
|
||||
let finalPayslipPath;
|
||||
var finalPayslipPath;
|
||||
if (path.isAbsolute(payslipBasePath)) {
|
||||
finalPayslipPath = path.join(payslipBasePath, period, fileName);
|
||||
} else {
|
||||
const projectRoot = path.resolve(__dirname, '../../');
|
||||
var projectRoot = path.resolve(__dirname, '../../');
|
||||
finalPayslipPath = path.join(projectRoot, payslipBasePath, period, fileName);
|
||||
}
|
||||
|
||||
logger.info(`Final payslip path: ${finalPayslipPath}`);
|
||||
logger.info('Final payslip path: ' + finalPayslipPath);
|
||||
|
||||
try {
|
||||
const basePathExists = await pathExists(payslipBasePath);
|
||||
logger.info(`Base path exists: ${basePathExists}`);
|
||||
var basePathExists = await pathExists(payslipBasePath);
|
||||
logger.info('Base path exists: ' + basePathExists);
|
||||
|
||||
if (basePathExists) {
|
||||
const basePathStats = await fs.stat(payslipBasePath);
|
||||
logger.info(`Base path stats: ${JSON.stringify({
|
||||
var basePathStats = await fsPromises.stat(payslipBasePath);
|
||||
logger.info('Base path stats: ' + JSON.stringify({
|
||||
isDirectory: basePathStats.isDirectory(),
|
||||
permissions: basePathStats.mode,
|
||||
size: basePathStats.size
|
||||
})}`);
|
||||
}));
|
||||
|
||||
const basePathContents = await fs.readdir(payslipBasePath);
|
||||
logger.info(`Base path contents: ${JSON.stringify(basePathContents)}`);
|
||||
var basePathContents = await fsPromises.readdir(payslipBasePath);
|
||||
logger.info('Base path contents: ' + JSON.stringify(basePathContents));
|
||||
|
||||
const periodPath = path.join(payslipBasePath, period);
|
||||
const periodPathExists = await pathExists(periodPath);
|
||||
logger.info(`Period path exists: ${periodPathExists} (${periodPath})`);
|
||||
var periodPath = path.join(payslipBasePath, period);
|
||||
var periodPathExists = await pathExists(periodPath);
|
||||
logger.info('Period path exists: ' + periodPathExists + ' (' + periodPath + ')');
|
||||
|
||||
if (periodPathExists) {
|
||||
const periodPathContents = await fs.readdir(periodPath);
|
||||
logger.info(`Period path contents: ${JSON.stringify(periodPathContents)}`);
|
||||
var periodPathContents = await fsPromises.readdir(periodPath);
|
||||
logger.info('Period path contents: ' + JSON.stringify(periodPathContents));
|
||||
}
|
||||
}
|
||||
} catch (basePathError) {
|
||||
@@ -269,10 +515,10 @@ app.get('/api/payslip/:period/:fileName', async (req, res) => {
|
||||
}
|
||||
|
||||
if (await pathExists(finalPayslipPath)) {
|
||||
logger.info(`Payslip found, sending file: ${finalPayslipPath}`);
|
||||
logger.info('Payslip found, sending file: ' + finalPayslipPath);
|
||||
res.sendFile(finalPayslipPath);
|
||||
} else {
|
||||
logger.warn(`Payslip not found: ${finalPayslipPath}`);
|
||||
logger.warn('Payslip not found: ' + finalPayslipPath);
|
||||
res.status(404).json({
|
||||
message: 'Расчетный листок не найден',
|
||||
path: finalPayslipPath,
|
||||
@@ -288,31 +534,27 @@ app.get('/api/payslip/:period/:fileName', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 📄 Получение расчетного листка (без периода)
|
||||
// ============================================
|
||||
|
||||
app.get('/api/payslip/:fileName', async (req, res) => {
|
||||
app.get('/api/payslip/:fileName', async function(req, res) {
|
||||
try {
|
||||
const { fileName } = req.params;
|
||||
const configPath = path.join(__dirname, '../config.json');
|
||||
const config = await readJson(configPath);
|
||||
const payslipBasePath = config.payslipsPath;
|
||||
var fileName = req.params.fileName;
|
||||
var configPath = path.join(__dirname, '../config.json');
|
||||
var config = await readJson(configPath);
|
||||
var payslipBasePath = config.payslipsPath;
|
||||
|
||||
let finalPayslipPath;
|
||||
var finalPayslipPath;
|
||||
if (path.isAbsolute(payslipBasePath)) {
|
||||
finalPayslipPath = path.join(payslipBasePath, fileName);
|
||||
} else {
|
||||
const projectRoot = path.resolve(__dirname, '../../');
|
||||
var projectRoot = path.resolve(__dirname, '../../');
|
||||
finalPayslipPath = path.join(projectRoot, payslipBasePath, fileName);
|
||||
}
|
||||
|
||||
logger.info(`Attempting to find payslip at resolved path: ${finalPayslipPath}`);
|
||||
logger.info('Attempting to find payslip at resolved path: ' + finalPayslipPath);
|
||||
|
||||
if (await pathExists(finalPayslipPath)) {
|
||||
res.sendFile(finalPayslipPath);
|
||||
} else {
|
||||
logger.warn(`Payslip not found: ${finalPayslipPath}`);
|
||||
logger.warn('Payslip not found: ' + finalPayslipPath);
|
||||
res.status(404).json({ message: 'Расчетный листок не найден' });
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -321,43 +563,47 @@ app.get('/api/payslip/:fileName', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
|
||||
// ============================================
|
||||
|
||||
app.get('/api/db-status', async (req, res) => {
|
||||
app.get('/api/db-status', async function(req, res) {
|
||||
try {
|
||||
const { query } = require('./config/db');
|
||||
await query('SELECT 1');
|
||||
var db = require('./config/db');
|
||||
await db.query('SELECT 1');
|
||||
res.status(200).json({ status: 'ok', message: 'DB connection successful' });
|
||||
} catch (err) {
|
||||
logger.error('Database connection check failed', { error: err.message, stack: err.stack });
|
||||
res.status(500).json({ status: 'error', message: 'DB connection failed' });
|
||||
}
|
||||
});
|
||||
app.get('/api/time', (req, res) => {
|
||||
res.json({
|
||||
time: new Date().toISOString(),
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
});
|
||||
});
|
||||
async function startServer() {
|
||||
try {
|
||||
const configPath = path.join(__dirname, '../config.json');
|
||||
const config = await readJson(configPath);
|
||||
const { host, port } = config.server;
|
||||
|
||||
await connectDB();
|
||||
app.get('/api/time', (req, res) => {
|
||||
const now = new Date();
|
||||
logger.info(`[TIME] Запрос времени от ${req.ip || req.connection.remoteAddress}`);
|
||||
app.get('/api/time', function(req, res) {
|
||||
var now = new Date();
|
||||
logger.info('[TIME] Запрос времени от ' + (req.ip || req.connection.remoteAddress));
|
||||
res.json({
|
||||
time: now.toISOString(),
|
||||
timestamp: now.getTime()
|
||||
});
|
||||
});
|
||||
app.listen(port, host, () => {
|
||||
logger.info(`Server is running on http://${host}:${port}`);
|
||||
|
||||
// ============================================
|
||||
// 🚀 ЗАПУСК СЕРВЕРА
|
||||
// ============================================
|
||||
|
||||
async function startServer() {
|
||||
try {
|
||||
var configPath = path.join(__dirname, '../config.json');
|
||||
var config = await readJson(configPath);
|
||||
var host = config.server.host;
|
||||
var port = config.server.port;
|
||||
|
||||
var excelExists = await pathExists(EXCEL_PATH);
|
||||
if (!excelExists) {
|
||||
await createExcelTemplate();
|
||||
logger.info('Создан Excel-шаблон');
|
||||
}
|
||||
|
||||
await connectDB();
|
||||
app.listen(port, host, function() {
|
||||
logger.info('Server is running on http://' + host + ':' + port);
|
||||
logger.info('Excel file: ' + EXCEL_PATH);
|
||||
});
|
||||
} catch (err) {
|
||||
if (logger) {
|
||||
@@ -367,8 +613,14 @@ app.get('/api/time', (req, res) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
app.use((err, req, res, next) => {
|
||||
|
||||
// ============================================
|
||||
// ❌ ОБРАБОТКА ОШИБОК
|
||||
// ============================================
|
||||
|
||||
app.use(function(err, req, res, next) {
|
||||
logger.error('Unhandled error', { error: err.message, stack: err.stack });
|
||||
res.status(500).json({ message: 'Внутренняя ошибка сервера' });
|
||||
});
|
||||
|
||||
startServer();
|
||||
Reference in New Issue
Block a user