From ee78449131b529c1499e351df743c7f45869c6f4 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 30 Oct 2026 16:16:55 +0500 Subject: [PATCH 1/9] =?UTF-8?q?=D0=9C=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3:=20=D0=BB=D0=BE=D0=B3=D0=B8,=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=B8=20=D0=B7=D0=B0=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D1=8C=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=80=D1=82=D0=B0=D0=BB=D0=B0=20=D0=B7=D0=B0=D1=8F=D0=B2?= =?UTF-8?q?=D0=BE=D0=BA=20=D0=B2=20monitoring.tnsu.kz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit initMonitoring в onInit веб-части (один раз на страницу), service.name = requestPortal, токен — молча через AAD веб-части (spfxTokenProvider). В package-solution.json запрос Logs.Write к регистрации мониторинга. Хэш коммита сборки — патч webpack (config/webpack-patch.json): GIT_COMMIT, GITHUB_SHA или git. Пакет @tnsu/monitoring-web ставится отдельно. Co-Authored-By: Claude Opus 5.5 --- config/package-solution.json | 4 ++++ config/webpack-patch.json | 4 ++++ config/webpack-patch/monitoring.js | 20 +++++++++++++++++++ .../requestPortal/RequestPortalWebPart.ts | 10 ++++++++++ 4 files changed, 38 insertions(+) create mode 100644 config/webpack-patch.json create mode 100644 config/webpack-patch/monitoring.js diff --git a/config/package-solution.json b/config/package-solution.json index 2307bd4..0ef3b19 100644 --- a/config/package-solution.json +++ b/config/package-solution.json @@ -30,6 +30,10 @@ "resource": "9232d9e2-9807-4753-808f-07250e5474b0", "scope": "Reports.Read" }, + { + "resource": "9232d9e2-9807-4753-808f-07250e5474b0", + "scope": "Logs.Write" + }, { "resource": "Microsoft Flow Service", "scope": "Flows.Read.All" diff --git a/config/webpack-patch.json b/config/webpack-patch.json new file mode 100644 index 0000000..4229f6c --- /dev/null +++ b/config/webpack-patch.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/webpack-patch.schema.json", + "patchFiles": ["./config/webpack-patch/monitoring.js"] +} diff --git a/config/webpack-patch/monitoring.js b/config/webpack-patch/monitoring.js new file mode 100644 index 0000000..06655af --- /dev/null +++ b/config/webpack-patch/monitoring.js @@ -0,0 +1,20 @@ +// Хэш коммита сборки — в логи (git.commit) и записи экрана мониторинга: @tnsu/monitoring-web читает +// __TNSU_GIT_COMMIT__, а webpack подставляет его при сборке. Хэш — GIT_COMMIT или GITHUB_SHA пайплайна, иначе git. +const { execSync } = require('node:child_process'); +const webpack = require('webpack'); + +function gitCommit() { + const fromEnv = process.env.GIT_COMMIT || process.env.GITHUB_SHA; + if (fromEnv) return fromEnv; + try { + return execSync('git rev-parse HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim(); + } catch { + return ''; + } +} + +module.exports = function monitoringPatch(config) { + config.plugins = config.plugins || []; + config.plugins.push(new webpack.DefinePlugin({ __TNSU_GIT_COMMIT__: JSON.stringify(gitCommit()) })); + return config; +}; diff --git a/src/webparts/requestPortal/RequestPortalWebPart.ts b/src/webparts/requestPortal/RequestPortalWebPart.ts index 988eb37..7a391c1 100644 --- a/src/webparts/requestPortal/RequestPortalWebPart.ts +++ b/src/webparts/requestPortal/RequestPortalWebPart.ts @@ -9,6 +9,7 @@ import { } from '@microsoft/sp-property-pane'; import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; import { IReadonlyTheme } from '@microsoft/sp-component-base'; +import { initMonitoring, spfxTokenProvider } from '@tnsu/monitoring-web'; import * as strings from 'RequestPortalWebPartStrings'; import RequestPortal from './components/RequestPortal'; @@ -62,6 +63,9 @@ export interface IRequestPortalWebPartProps { chatSelfNotify?: boolean; } +// Мониторинг — один на страницу: экземпляров веб-части на странице может быть несколько. +let monitoringStarted = false; + export default class RequestPortalWebPart extends BaseClientSideWebPart { private _isDarkTheme: boolean = false; @@ -98,6 +102,12 @@ export default class RequestPortalWebPart extends BaseClientSideWebPart { + // Логи, ошибки, console.error и запись экрана в мониторинг (monitoring.tnsu.kz). Токен — молча через + // AAD веб-части (разрешение Logs.Write в package-solution.json); при отладке в workbench окружение — development. + if (!monitoringStarted) { + monitoringStarted = true; + initMonitoring({ service: 'requestPortal', getAccessToken: spfxTokenProvider(this.context.aadTokenProviderFactory) }); + } installErrorLogging(); // до всего остального — чтобы поймать и ранние отклонения initSP(this.context); // единый клиент PnPjs для записи в списки -- 2.34.1 From 4b09833bc4f8817fc4836c1e8dce0e1e2618dee9 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 30 Oct 2026 16:21:23 +0500 Subject: [PATCH 2/9] =?UTF-8?q?=D0=9F=D0=B0=D0=BA=D0=B5=D1=82=20@tnsu/moni?= =?UTF-8?q?toring-web=200.3.0=20=D0=B8=D0=B7=20=D1=80=D0=B5=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=20tansu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 109 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 1 + 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b807ab1..7da1cab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", + "@tnsu/monitoring-web": "^0.3.0", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", "react": "17.0.1", @@ -8726,6 +8727,28 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rrweb/rrweb-plugin-console-record": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rrweb/rrweb-plugin-console-record/-/rrweb-plugin-console-record-2.2.0.tgz", + "integrity": "sha512-2c6LEsoH3+1NFcHckcwuErg7bkpFadt9++uKJh2x9BtWUDxoVKzLp+qLCq6f90MLTjSIABDkAiCswVskENZT3g==", + "license": "MIT", + "peerDependencies": { + "@rrweb/utils": "^2.1.1", + "rrweb": "^2.1.1" + } + }, + "node_modules/@rrweb/types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rrweb/types/-/types-2.2.0.tgz", + "integrity": "sha512-2gTA/pRpIbuET123oSdX5ZUDZfWCOIMYBvpSai3fr5pk9LcbccPqqaXflX+roP4oKenLjNtJPGpFK3BfYeBKnA==", + "license": "MIT" + }, + "node_modules/@rrweb/utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rrweb/utils/-/utils-2.2.0.tgz", + "integrity": "sha512-e8FEp8wLswqgjDHpQNUUVfEXeDsrRMOVouvqIGKvnPxKYonjLlh0mU3O/17QNvCixFG5eUFR1B2jgTdiLlZxeQ==", + "license": "MIT" + }, "node_modules/@rushstack/debug-certificate-manager": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/@rushstack/debug-certificate-manager/-/debug-certificate-manager-1.6.2.tgz", @@ -9885,6 +9908,28 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/@tnsu/monitoring-web": { + "version": "0.3.0", + "resolved": "https://git.tnsu.kz/api/packages/tansu/npm/%40tnsu%2Fmonitoring-web/-/0.3.0/monitoring-web-0.3.0.tgz", + "integrity": "sha512-4b9H643hl/KyWvugmuHW6lxk10wLRqfzeBTtEW4hZ2X7dfB4K7djESudurtwZibsWlfHibQNT2wqWgUznZ1DjA==", + "license": "UNLICENSED", + "dependencies": { + "@rrweb/rrweb-plugin-console-record": "^2.2.0", + "rrweb": "^2.2.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "vue": "^3.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, "node_modules/@tnsu/ui-kit": { "version": "0.15.0", "resolved": "https://git.tnsu.kz/api/packages/tansu/npm/%40tnsu%2Fui-kit/-/0.15.0/ui-kit-0.15.0.tgz", @@ -10022,6 +10067,12 @@ "@types/node": "*" } }, + "node_modules/@types/css-font-loading-module": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", + "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==", + "license": "MIT" + }, "node_modules/@types/ejs": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", @@ -11627,6 +11678,12 @@ "node": ">=10.0.0" } }, + "node_modules/@xstate/fsm": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@xstate/fsm/-/fsm-1.6.5.tgz", + "integrity": "sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==", + "license": "MIT" + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -12513,6 +12570,15 @@ } } }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -19813,6 +19879,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -19935,7 +20007,6 @@ "version": "3.3.16", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", - "dev": true, "funding": [ { "type": "github", @@ -20742,7 +20813,6 @@ "version": "8.5.25", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -22018,6 +22088,40 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rrdom": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/rrdom/-/rrdom-2.2.0.tgz", + "integrity": "sha512-gxcBJWVtS/Xz+Ys8xcjPoWqr+C7wz/MS8NgmKw136I47Xygy17FYOGZqoGwEyvBn724jRDK1ljS+8w9FsZStOg==", + "license": "MIT", + "dependencies": { + "rrweb-snapshot": "^2.2.0" + } + }, + "node_modules/rrweb": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/rrweb/-/rrweb-2.2.0.tgz", + "integrity": "sha512-ofVzPq11/yIQ7WhVb++rJGI2LoPPrxdqu0QAvRA4KGeXJ7LH52jQ0VlRH4+VXn5PA/7pz85/rRkQyBUqT1LraQ==", + "license": "MIT", + "dependencies": { + "@rrweb/types": "^2.2.0", + "@rrweb/utils": "^2.2.0", + "@types/css-font-loading-module": "0.0.7", + "@xstate/fsm": "^1.4.0", + "base64-arraybuffer": "^1.0.1", + "mitt": "^3.0.0", + "rrdom": "^2.2.0", + "rrweb-snapshot": "^2.2.0" + } + }, + "node_modules/rrweb-snapshot": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-2.2.0.tgz", + "integrity": "sha512-Tg/PiCLy+zV9C0Jfb4IAGABtt5l/UOvwW5xjFk41ZQhBfUjeqPP1rZ9HMDrFPc7cawCUEenWw54+g/4vluO5lg==", + "license": "MIT", + "dependencies": { + "postcss": "^8.4.38" + } + }, "node_modules/rtl-css-js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz", @@ -23127,7 +23231,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" diff --git a/package.json b/package.json index 768353b..4073f83 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", + "@tnsu/monitoring-web": "^0.3.0", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", "react": "17.0.1", -- 2.34.1 From e204eeab7282d595eaa2d3fd95dd46ba99ddcd68 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 30 Oct 2026 16:34:11 +0500 Subject: [PATCH 3/9] =?UTF-8?q?=D0=9C=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3:=20@tnsu/monitoring-web=200.3.1=20=E2=80=94?= =?UTF-8?q?=20=D0=BF=D0=B0=D1=82=D1=87=20webpack=20=D0=B8=20=C2=AB=D0=BE?= =?UTF-8?q?=D0=B4=D0=B8=D0=BD=20=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=86=D1=83=C2=BB=20=D0=B8=D0=B7=20=D0=B1=D0=B8?= =?UTF-8?q?=D0=B1=D0=BB=D0=B8=D0=BE=D1=82=D0=B5=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit config/webpack-patch.json ссылается на патч из пакета (spfx/webpack-patch.cjs), свой config/webpack-patch/monitoring.js удалён. Флаг monitoringStarted не нужен: повторный initMonitoring библиотека игнорирует сама. Co-Authored-By: Claude Opus 5.5 --- config/webpack-patch.json | 2 +- config/webpack-patch/monitoring.js | 20 ------- package-lock.json | 54 ++++++++++--------- package.json | 2 +- .../requestPortal/RequestPortalWebPart.ts | 9 +--- 5 files changed, 33 insertions(+), 54 deletions(-) delete mode 100644 config/webpack-patch/monitoring.js diff --git a/config/webpack-patch.json b/config/webpack-patch.json index 4229f6c..55d994e 100644 --- a/config/webpack-patch.json +++ b/config/webpack-patch.json @@ -1,4 +1,4 @@ { "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/webpack-patch.schema.json", - "patchFiles": ["./config/webpack-patch/monitoring.js"] + "patchFiles": ["./node_modules/@tnsu/monitoring-web/spfx/webpack-patch.cjs"] } diff --git a/config/webpack-patch/monitoring.js b/config/webpack-patch/monitoring.js deleted file mode 100644 index 06655af..0000000 --- a/config/webpack-patch/monitoring.js +++ /dev/null @@ -1,20 +0,0 @@ -// Хэш коммита сборки — в логи (git.commit) и записи экрана мониторинга: @tnsu/monitoring-web читает -// __TNSU_GIT_COMMIT__, а webpack подставляет его при сборке. Хэш — GIT_COMMIT или GITHUB_SHA пайплайна, иначе git. -const { execSync } = require('node:child_process'); -const webpack = require('webpack'); - -function gitCommit() { - const fromEnv = process.env.GIT_COMMIT || process.env.GITHUB_SHA; - if (fromEnv) return fromEnv; - try { - return execSync('git rev-parse HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim(); - } catch { - return ''; - } -} - -module.exports = function monitoringPatch(config) { - config.plugins = config.plugins || []; - config.plugins.push(new webpack.DefinePlugin({ __TNSU_GIT_COMMIT__: JSON.stringify(gitCommit()) })); - return config; -}; diff --git a/package-lock.json b/package-lock.json index 7da1cab..1738b19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", - "@tnsu/monitoring-web": "^0.3.0", + "@tnsu/monitoring-web": "^0.3.1", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", "react": "17.0.1", @@ -342,7 +342,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -352,7 +352,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", @@ -383,7 +383,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "devOptional": true, + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -409,7 +409,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.29.7", @@ -426,7 +426,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "devOptional": true, + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -458,7 +458,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.29.7", @@ -504,7 +504,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -514,7 +514,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.29.7", @@ -3980,7 +3980,7 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -9909,9 +9909,9 @@ "license": "0BSD" }, "node_modules/@tnsu/monitoring-web": { - "version": "0.3.0", - "resolved": "https://git.tnsu.kz/api/packages/tansu/npm/%40tnsu%2Fmonitoring-web/-/0.3.0/monitoring-web-0.3.0.tgz", - "integrity": "sha512-4b9H643hl/KyWvugmuHW6lxk10wLRqfzeBTtEW4hZ2X7dfB4K7djESudurtwZibsWlfHibQNT2wqWgUznZ1DjA==", + "version": "0.3.1", + "resolved": "https://git.tnsu.kz/api/packages/tansu/npm/%40tnsu%2Fmonitoring-web/-/0.3.1/monitoring-web-0.3.1.tgz", + "integrity": "sha512-HcHP8Utb9gXBRt4nEC4RW5UrqT8ssIs7Prs2LSQaE/oy3ybFNiqpLp3XbmGx1ssCArZcwBozov0MbIHpC9dVgg==", "license": "UNLICENSED", "dependencies": { "@rrweb/rrweb-plugin-console-record": "^2.2.0", @@ -10301,7 +10301,7 @@ "version": "26.1.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~8.3.0" @@ -10330,6 +10330,7 @@ "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, "license": "MIT" }, "node_modules/@types/qs": { @@ -10359,6 +10360,7 @@ "version": "17.0.45", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.45.tgz", "integrity": "sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg==", + "dev": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -10370,6 +10372,7 @@ "version": "17.0.17", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz", "integrity": "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==", + "dev": true, "license": "MIT", "dependencies": { "@types/react": "^17" @@ -10392,6 +10395,7 @@ "version": "0.26.0", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.26.0.tgz", "integrity": "sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==", + "dev": true, "license": "MIT" }, "node_modules/@types/send": { @@ -12603,7 +12607,7 @@ "version": "2.11.7", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.7.tgz", "integrity": "sha512-APw5YuIQAg6L9w4sHDI6j26DGFJI6RpYOhnkMPdC9lWbkKvsyPHzDsve1yd73lk21yz7Y09Kci8B2Pp9FonzWA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -12777,7 +12781,7 @@ "version": "4.28.7", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -12986,7 +12990,7 @@ "version": "1.0.30001806", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -13411,7 +13415,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/cookie": { @@ -14300,7 +14304,7 @@ "version": "1.5.398", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.398.tgz", "integrity": "sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/embla-carousel": { @@ -14630,7 +14634,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -16050,7 +16054,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -19199,7 +19203,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -20148,7 +20152,7 @@ "version": "2.0.51", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -24510,7 +24514,7 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/unicorn-magic": { @@ -24564,7 +24568,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index 4073f83..792db1e 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", - "@tnsu/monitoring-web": "^0.3.0", + "@tnsu/monitoring-web": "^0.3.1", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", "react": "17.0.1", diff --git a/src/webparts/requestPortal/RequestPortalWebPart.ts b/src/webparts/requestPortal/RequestPortalWebPart.ts index 7a391c1..ab9e955 100644 --- a/src/webparts/requestPortal/RequestPortalWebPart.ts +++ b/src/webparts/requestPortal/RequestPortalWebPart.ts @@ -63,9 +63,6 @@ export interface IRequestPortalWebPartProps { chatSelfNotify?: boolean; } -// Мониторинг — один на страницу: экземпляров веб-части на странице может быть несколько. -let monitoringStarted = false; - export default class RequestPortalWebPart extends BaseClientSideWebPart { private _isDarkTheme: boolean = false; @@ -104,10 +101,8 @@ export default class RequestPortalWebPart extends BaseClientSideWebPart { // Логи, ошибки, console.error и запись экрана в мониторинг (monitoring.tnsu.kz). Токен — молча через // AAD веб-части (разрешение Logs.Write в package-solution.json); при отладке в workbench окружение — development. - if (!monitoringStarted) { - monitoringStarted = true; - initMonitoring({ service: 'requestPortal', getAccessToken: spfxTokenProvider(this.context.aadTokenProviderFactory) }); - } + // Один на страницу: повторный вызов (несколько веб-частей) библиотека игнорирует сама. + initMonitoring({ service: 'requestPortal', getAccessToken: spfxTokenProvider(this.context.aadTokenProviderFactory) }); installErrorLogging(); // до всего остального — чтобы поймать и ранние отклонения initSP(this.context); // единый клиент PnPjs для записи в списки -- 2.34.1 From f2c601bf99aca2264648f8c0a3d6e9047c92c095 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 30 Oct 2026 16:50:08 +0500 Subject: [PATCH 4/9] =?UTF-8?q?=D0=9C=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3:=20=D0=B8=D0=BC=D1=8F=20=D1=81=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D0=B8=D1=81=D0=B0=20request-portal=20(=D0=BA=D0=B0=D0=BA?= =?UTF-8?q?=20=D1=80=D0=B5=D0=BF=D0=BE=D0=B7=D0=B8=D1=82=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=B9),=20=D0=B0=20=D0=BD=D0=B5=20requestPortal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- src/webparts/requestPortal/RequestPortalWebPart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webparts/requestPortal/RequestPortalWebPart.ts b/src/webparts/requestPortal/RequestPortalWebPart.ts index ab9e955..3a4ea97 100644 --- a/src/webparts/requestPortal/RequestPortalWebPart.ts +++ b/src/webparts/requestPortal/RequestPortalWebPart.ts @@ -102,7 +102,7 @@ export default class RequestPortalWebPart extends BaseClientSideWebPart Date: Fri, 30 Oct 2026 17:15:27 +0500 Subject: [PATCH 5/9] =?UTF-8?q?=D0=9C=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3:=20@tnsu/monitoring-web=200.3.3=20=E2=80=94?= =?UTF-8?q?=20console.error=20SharePoint=20=D0=BD=D0=B5=20=D1=83=D1=85?= =?UTF-8?q?=D0=BE=D0=B4=D0=B8=D1=82=20=D0=B2=20=D0=BB=D0=BE=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1738b19..bb965de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", - "@tnsu/monitoring-web": "^0.3.1", + "@tnsu/monitoring-web": "^0.3.3", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", "react": "17.0.1", @@ -9909,9 +9909,9 @@ "license": "0BSD" }, "node_modules/@tnsu/monitoring-web": { - "version": "0.3.1", - "resolved": "https://git.tnsu.kz/api/packages/tansu/npm/%40tnsu%2Fmonitoring-web/-/0.3.1/monitoring-web-0.3.1.tgz", - "integrity": "sha512-HcHP8Utb9gXBRt4nEC4RW5UrqT8ssIs7Prs2LSQaE/oy3ybFNiqpLp3XbmGx1ssCArZcwBozov0MbIHpC9dVgg==", + "version": "0.3.3", + "resolved": "https://git.tnsu.kz/api/packages/tansu/npm/%40tnsu%2Fmonitoring-web/-/0.3.3/monitoring-web-0.3.3.tgz", + "integrity": "sha512-jxuGTHA8pI71HZzAl+pDhSKgMY7YA/F8Xiq0U4koTw2w7oWcF8XeRwQ3z6lJUpvpWGn1FLpZOjE9/8SoMBPNrA==", "license": "UNLICENSED", "dependencies": { "@rrweb/rrweb-plugin-console-record": "^2.2.0", diff --git a/package.json b/package.json index 792db1e..c47ca85 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", - "@tnsu/monitoring-web": "^0.3.1", + "@tnsu/monitoring-web": "^0.3.3", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", "react": "17.0.1", -- 2.34.1 From eaa1c855d985e8f717bcbfc8d2fc37922ab01137 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 30 Oct 2026 17:18:18 +0500 Subject: [PATCH 6/9] =?UTF-8?q?=D0=9C=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3:=20=D0=BB=D0=BE=D0=B3=D0=B8=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B7=D0=B0=D1=8F=D0=B2?= =?UTF-8?q?=D0=BE=D0=BA,=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BD=D0=B0=20=D1=81=D0=BE=D0=B3=D0=BB=D0=B0=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B8=20=D1=80=D0=B5?= =?UTF-8?q?=D1=88=D0=B5=D0=BD=D0=B8=D0=B9=20=D1=81=D0=BE=D0=B3=D0=BB=D0=B0?= =?UTF-8?q?=D1=81=D1=83=D1=8E=D1=89=D0=B8=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit saveRequestItem — «Создана заявка» (тип, id, код); writeRoute/writeApprovers — «Заявка отправлена на согласование» (статус, число согласующих и исполнителей); ApproverService.decide — «Заявка согласована / возвращена / отклонена / выполнена исполнителем» (новый статус, строка маршрута, комментарий до 500 символов). Пользователь — из токена мониторинга. Co-Authored-By: Claude Opus 5.5 --- .../approval/approvalSubmission.service.ts | 12 ++++++++++++ .../shared/approval/approverDecision.service.ts | 17 +++++++++++++++++ src/webparts/requestPortal/shared/dataList.ts | 4 ++++ 3 files changed, 33 insertions(+) diff --git a/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts b/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts index 974b663..a7edf90 100644 --- a/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts +++ b/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts @@ -1,4 +1,5 @@ import { WebPartContext } from '@microsoft/sp-webpart-base'; +import { log } from '@tnsu/monitoring-web'; import { resolveRoute } from './approvalRoute.service'; import { ApprovalListService, IApproverToWrite } from './approvalList.service'; import { IMatrixRow } from './models/IMatrixRow'; @@ -68,6 +69,11 @@ export async function writeApprovers( await approval.createSteps(requestId, approvers, rowStatus, APPROVAL_PHASE.approval, 1); if (submitted) { await approval.activateNextQueued(requestId); + log.info('Заявка отправлена на согласование', { + 'request.id': requestId, + 'request.status': status, + 'route.approvers': approvers.length, + }); } } @@ -138,5 +144,11 @@ export async function writeRoute( if (submitted) { await approval.activateNextQueued(requestId); + log.info('Заявка отправлена на согласование', { + 'request.id': requestId, + 'request.status': status, + 'route.approvers': approvers.length, + 'route.executors': executors.length, + }); } } diff --git a/src/webparts/requestPortal/shared/approval/approverDecision.service.ts b/src/webparts/requestPortal/shared/approval/approverDecision.service.ts index bfba56e..b19e9aa 100644 --- a/src/webparts/requestPortal/shared/approval/approverDecision.service.ts +++ b/src/webparts/requestPortal/shared/approval/approverDecision.service.ts @@ -1,3 +1,4 @@ +import { log } from '@tnsu/monitoring-web'; import { getSP } from '../pnp/pnpjs'; import '@pnp/sp/webs'; import '@pnp/sp/lists'; @@ -25,6 +26,14 @@ export interface IPendingRow extends IApprovalRow { isDelegator: boolean; } +// Решение → текст события в логах мониторинга. +const DECISION_MESSAGE: Record = { + approve: 'Заявка согласована', + return: 'Заявка возвращена на доработку', + reject: 'Заявка отклонена', + execute: 'Заявка выполнена исполнителем', +}; + // Решение → статус его строки в списке согласования. const ROW_STATUS: Record = { approve: REQUEST_STATUS.approved, @@ -336,6 +345,14 @@ export class ApproverService { const rows = await this.approval.getForRequest(requestId); const requestStatus = this.computeRequestStatus(rows); await this.updateRequestStatus(requestId, requestStatus); + log.info(DECISION_MESSAGE[decision], { + 'request.id': requestId, + 'request.list': this.dataListRef, + 'request.status': requestStatus, + 'approval.decision': decision, + 'approval.rowId': rowId, + 'approval.comment': comment.slice(0, 500), + }); emitRequestsChanged(); return requestStatus; } diff --git a/src/webparts/requestPortal/shared/dataList.ts b/src/webparts/requestPortal/shared/dataList.ts index 97620e5..1ba49b7 100644 --- a/src/webparts/requestPortal/shared/dataList.ts +++ b/src/webparts/requestPortal/shared/dataList.ts @@ -2,6 +2,7 @@ import '@pnp/sp/webs'; import '@pnp/sp/lists'; import '@pnp/sp/items'; import { IList } from '@pnp/sp/lists'; +import { log } from '@tnsu/monitoring-web'; import { getSP } from './pnp/pnpjs'; import { buildRequestUniqueCode } from './requestCode'; import { dataListTitle } from './listTitles'; @@ -58,5 +59,8 @@ export async function saveRequestItem( // Код известен только после создания элемента (нужен его Id). const code = buildRequestUniqueCode(codePrefix, id, new Date()); await list.items.getById(id).update({ RequestUniqueCode: code }); + // Событие для мониторинга: кто (пользователь — из токена) и какую заявку создал. Отправка на согласование + // и решения пишутся отдельно — approvalSubmission.service.ts и approverDecision.service.ts. + log.info('Создана заявка', { 'request.type': formId, 'request.id': id, 'request.code': code }); return { id, code }; } -- 2.34.1 From c1c63b33e12e576e7b9a79a1e5a974f25d845d39 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 30 Oct 2026 17:55:42 +0500 Subject: [PATCH 7/9] =?UTF-8?q?=D0=9C=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3:=20@tnsu/monitoring-web=200.3.4=20=E2=80=94?= =?UTF-8?q?=20=D1=88=D1=83=D0=BC=20SharePoint=20(=C2=ABScript=20error.?= =?UTF-8?q?=C2=BB,=20=D0=BF=D1=83=D1=81=D1=82=D1=8B=D0=B5=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=D0=B7=D1=8B=20=D0=BF=D1=80=D0=BE=D0=BC=D0=B8=D1=81?= =?UTF-8?q?=D0=BE=D0=B2)=20=D0=BD=D0=B5=20=D1=83=D1=85=D0=BE=D0=B4=D0=B8?= =?UTF-8?q?=D1=82=20=D0=B2=20=D0=BB=D0=BE=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index bb965de..3e037eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", - "@tnsu/monitoring-web": "^0.3.3", + "@tnsu/monitoring-web": "^0.3.4", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", "react": "17.0.1", @@ -9909,9 +9909,9 @@ "license": "0BSD" }, "node_modules/@tnsu/monitoring-web": { - "version": "0.3.3", - "resolved": "https://git.tnsu.kz/api/packages/tansu/npm/%40tnsu%2Fmonitoring-web/-/0.3.3/monitoring-web-0.3.3.tgz", - "integrity": "sha512-jxuGTHA8pI71HZzAl+pDhSKgMY7YA/F8Xiq0U4koTw2w7oWcF8XeRwQ3z6lJUpvpWGn1FLpZOjE9/8SoMBPNrA==", + "version": "0.3.4", + "resolved": "https://git.tnsu.kz/api/packages/tansu/npm/%40tnsu%2Fmonitoring-web/-/0.3.4/monitoring-web-0.3.4.tgz", + "integrity": "sha512-tQe0ZM1tBVO3XtbNFliRaaooqvCX3gb/Q/j2lV6+rYCXprodBZByQ6PLrCu/P0me/rW8KdQ02hu7avyLXXfkoQ==", "license": "UNLICENSED", "dependencies": { "@rrweb/rrweb-plugin-console-record": "^2.2.0", diff --git a/package.json b/package.json index c47ca85..c596520 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", - "@tnsu/monitoring-web": "^0.3.3", + "@tnsu/monitoring-web": "^0.3.4", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", "react": "17.0.1", -- 2.34.1 From 168c242a8463ec2878f45783223b7822a4c42c11 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 30 Oct 2026 18:04:33 +0500 Subject: [PATCH 8/9] =?UTF-8?q?=D0=9C=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3:=20=D0=B2=20=D0=BB=D0=BE=D0=B3=D0=B0=D1=85?= =?UTF-8?q?=20=D0=B7=D0=B0=D1=8F=D0=B2=D0=BE=D0=BA=20=E2=80=94=20=D0=B2?= =?UTF-8?q?=D0=B8=D0=B4,=20=D0=BA=D0=BE=D0=B4=20=D0=B8=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D1=8F=D0=B2?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit «Создана заявка: Заявка на прием · TNS-… · Инженер — Проект X», «Заявка отправлена на согласование: …», «Заявка согласована: TNS-… · …» — заявку видно прямо в списке логов; те же данные — в полях request.*. Отправка на согласование пишется при сохранении заявки с отправленным статусом (там известно, что за заявка), из writeRoute лог убран. Код и название для решения дочитываются, сбой чтения решение не ломает. Co-Authored-By: Claude Opus 5.5 --- .../approval/approvalSubmission.service.ts | 12 ----- .../approval/approverDecision.service.ts | 45 +++++++++++++++---- src/webparts/requestPortal/shared/dataList.ts | 35 +++++++++++++-- 3 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts b/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts index a7edf90..974b663 100644 --- a/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts +++ b/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts @@ -1,5 +1,4 @@ import { WebPartContext } from '@microsoft/sp-webpart-base'; -import { log } from '@tnsu/monitoring-web'; import { resolveRoute } from './approvalRoute.service'; import { ApprovalListService, IApproverToWrite } from './approvalList.service'; import { IMatrixRow } from './models/IMatrixRow'; @@ -69,11 +68,6 @@ export async function writeApprovers( await approval.createSteps(requestId, approvers, rowStatus, APPROVAL_PHASE.approval, 1); if (submitted) { await approval.activateNextQueued(requestId); - log.info('Заявка отправлена на согласование', { - 'request.id': requestId, - 'request.status': status, - 'route.approvers': approvers.length, - }); } } @@ -144,11 +138,5 @@ export async function writeRoute( if (submitted) { await approval.activateNextQueued(requestId); - log.info('Заявка отправлена на согласование', { - 'request.id': requestId, - 'request.status': status, - 'route.approvers': approvers.length, - 'route.executors': executors.length, - }); } } diff --git a/src/webparts/requestPortal/shared/approval/approverDecision.service.ts b/src/webparts/requestPortal/shared/approval/approverDecision.service.ts index b19e9aa..970e1c6 100644 --- a/src/webparts/requestPortal/shared/approval/approverDecision.service.ts +++ b/src/webparts/requestPortal/shared/approval/approverDecision.service.ts @@ -345,14 +345,7 @@ export class ApproverService { const rows = await this.approval.getForRequest(requestId); const requestStatus = this.computeRequestStatus(rows); await this.updateRequestStatus(requestId, requestStatus); - log.info(DECISION_MESSAGE[decision], { - 'request.id': requestId, - 'request.list': this.dataListRef, - 'request.status': requestStatus, - 'approval.decision': decision, - 'approval.rowId': rowId, - 'approval.comment': comment.slice(0, 500), - }); + await this.logDecision(requestId, rowId, decision, comment, requestStatus); emitRequestsChanged(); return requestStatus; } @@ -381,6 +374,42 @@ export class ApproverService { return REQUEST_STATUS.execution; } + /** + * Решение — в логи мониторинга, с кодом и названием заявки, чтобы её было видно прямо в списке логов. + * Код и название дочитываются отдельно; не получилось — пишем без них: решение уже применено. + */ + private async logDecision( + requestId: number, + rowId: number, + decision: ApproverDecision, + comment: string, + requestStatus: string + ): Promise { + let code = ''; + let title = ''; + try { + const item = (await this.dataList().items.getById(requestId).select('Title', 'RequestUniqueCode')()) as { + Title?: string; + RequestUniqueCode?: string; + }; + code = item.RequestUniqueCode || ''; + title = item.Title || ''; + } catch { + // без кода и названия — лог всё равно нужен + } + const what = [code, title].filter(Boolean).join(' · ') || `№${requestId}`; + log.info(`${DECISION_MESSAGE[decision]}: ${what}`, { + 'request.id': requestId, + 'request.code': code, + 'request.title': title, + 'request.list': this.dataListRef, + 'request.status': requestStatus, + 'approval.decision': decision, + 'approval.rowId': rowId, + 'approval.comment': comment.slice(0, 500), + }); + } + private dataList(): IList { return this.dataListRef.startsWith('/') ? getSP().web.getList(this.dataListRef) diff --git a/src/webparts/requestPortal/shared/dataList.ts b/src/webparts/requestPortal/shared/dataList.ts index 1ba49b7..1eab111 100644 --- a/src/webparts/requestPortal/shared/dataList.ts +++ b/src/webparts/requestPortal/shared/dataList.ts @@ -7,6 +7,8 @@ import { getSP } from './pnp/pnpjs'; import { buildRequestUniqueCode } from './requestCode'; import { dataListTitle } from './listTitles'; import { RequestTypeId } from '../models/types'; +import { REQUEST_TYPES } from '../data/requests'; +import { isSubmittedStatus } from './approval/models/statuses'; /** * Список данных, в который фактически пишем: listId (конкретная партиция — @@ -51,6 +53,7 @@ export async function saveRequestItem( if (existingId) { await list.items.getById(existingId).update(item); + logSaved(formId, existingId, undefined, item, false); return { id: existingId }; } @@ -59,8 +62,34 @@ export async function saveRequestItem( // Код известен только после создания элемента (нужен его Id). const code = buildRequestUniqueCode(codePrefix, id, new Date()); await list.items.getById(id).update({ RequestUniqueCode: code }); - // Событие для мониторинга: кто (пользователь — из токена) и какую заявку создал. Отправка на согласование - // и решения пишутся отдельно — approvalSubmission.service.ts и approverDecision.service.ts. - log.info('Создана заявка', { 'request.type': formId, 'request.id': id, 'request.code': code }); + logSaved(formId, id, code, item, true); return { id, code }; } + +/** + * События для мониторинга: какая заявка создана и отправлена на согласование (пользователь — из токена). + * В тексте — вид, код и название заявки, чтобы её было видно прямо в списке логов. Решения согласующих + * пишет approverDecision.service.ts. + */ +function logSaved( + formId: RequestTypeId, + id: number, + code: string | undefined, + item: Record, + created: boolean +): void { + const typeTitle = REQUEST_TYPES.find((t) => t.id === formId)?.title ?? formId; + const title = typeof item.Title === 'string' ? item.Title : ''; + const status = typeof item.RequestStatus === 'string' ? item.RequestStatus : ''; + const what = [typeTitle, code, title].filter(Boolean).join(' · '); + const attributes = { + 'request.type': formId, + 'request.typeTitle': typeTitle, + 'request.id': id, + 'request.code': code ?? '', + 'request.title': title, + 'request.status': status, + }; + if (created) log.info(`Создана заявка: ${what}`, attributes); + if (isSubmittedStatus(status)) log.info(`Заявка отправлена на согласование: ${what}`, attributes); +} -- 2.34.1 From da413abd142d0052da8ca4129a040b0ad1b8a8b8 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 30 Oct 2026 18:42:47 +0500 Subject: [PATCH 9/9] =?UTF-8?q?=D0=9E=D1=82=D0=BA=D0=BB=D1=8E=D1=87=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=82=D0=B5=D0=BB=D0=B5=D0=BC=D0=B5=D1=82=D1=80?= =?UTF-8?q?=D0=B8=D1=8E=20PnP:=20=D0=BD=D0=B5=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=BD=D0=BD=D1=8B=D0=B9=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=D0=B7=20=D0=BF=D1=80=D0=BE=D0=BC=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=20=D0=B2=20=D0=BC=D0=BE=D0=BD=D0=B8=D1=82=D0=BE=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D0=B3=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @pnp/spfx-controls-react через @pnp/telemetry-js шлёт fetch на pnptelemetryproxy.azurewebsites.net без catch. Где адрес закрыт прокси или блокировщиком, каждый раз «Failed to fetch» и «Необработанный отказ промиса» в логах request-portal. Отключаем её в onInit (optOut), @pnp/telemetry-js — прямой зависимостью той же версии 2.0.0. Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 1 + package.json | 1 + src/webparts/requestPortal/RequestPortalWebPart.ts | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/package-lock.json b/package-lock.json index 3e037eb..040daa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", + "@pnp/telemetry-js": "2.0.0", "@tnsu/monitoring-web": "^0.3.4", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", diff --git a/package.json b/package.json index c596520..96208f0 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@microsoft/sp-webpart-base": "1.22.1", "@pnp/sp": "^4.20.0", "@pnp/spfx-controls-react": "^3.25.0", + "@pnp/telemetry-js": "2.0.0", "@tnsu/monitoring-web": "^0.3.4", "@tnsu/ui-kit": "^0.15.0", "jszip": "~3.8.0", diff --git a/src/webparts/requestPortal/RequestPortalWebPart.ts b/src/webparts/requestPortal/RequestPortalWebPart.ts index 3a4ea97..725e0db 100644 --- a/src/webparts/requestPortal/RequestPortalWebPart.ts +++ b/src/webparts/requestPortal/RequestPortalWebPart.ts @@ -9,6 +9,7 @@ import { } from '@microsoft/sp-property-pane'; import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; import { IReadonlyTheme } from '@microsoft/sp-component-base'; +import PnPTelemetry from '@pnp/telemetry-js'; import { initMonitoring, spfxTokenProvider } from '@tnsu/monitoring-web'; import * as strings from 'RequestPortalWebPartStrings'; @@ -104,6 +105,9 @@ export default class RequestPortalWebPart extends BaseClientSideWebPart