Compare commits
4 Commits
ce5508fe7f
...
2fe2b11659
Author | SHA1 | Date | |
---|---|---|---|
2fe2b11659 | |||
86cf636e16 | |||
7d78295b9a | |||
0e4b57af51 |
367
package-lock.json
generated
367
package-lock.json
generated
@ -8,17 +8,17 @@
|
|||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"version": "1.0.0-b0",
|
"version": "1.0.0-b0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^18.0.4",
|
"@angular/animations": "^18.0.5",
|
||||||
"@angular/cdk": "~18.0.4",
|
"@angular/cdk": "~18.0.5",
|
||||||
"@angular/cdk-experimental": "^18.0.4",
|
"@angular/cdk-experimental": "^18.0.5",
|
||||||
"@angular/common": "^18.0.4",
|
"@angular/common": "^18.0.5",
|
||||||
"@angular/compiler": "^18.0.4",
|
"@angular/compiler": "^18.0.5",
|
||||||
"@angular/core": "^18.0.4",
|
"@angular/core": "^18.0.5",
|
||||||
"@angular/forms": "^18.0.4",
|
"@angular/forms": "^18.0.5",
|
||||||
"@angular/material": "~18.0.4",
|
"@angular/material": "~18.0.5",
|
||||||
"@angular/platform-browser": "^18.0.4",
|
"@angular/platform-browser": "^18.0.5",
|
||||||
"@angular/platform-browser-dynamic": "^18.0.4",
|
"@angular/platform-browser-dynamic": "^18.0.5",
|
||||||
"@angular/router": "^18.0.4",
|
"@angular/router": "^18.0.5",
|
||||||
"@progress/kendo-date-math": "^1.5.13",
|
"@progress/kendo-date-math": "^1.5.13",
|
||||||
"rxjs": "~7.8.1",
|
"rxjs": "~7.8.1",
|
||||||
"tslib": "^2.6.3",
|
"tslib": "^2.6.3",
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^18.0.6",
|
"@angular-devkit/build-angular": "^18.0.6",
|
||||||
"@angular/cli": "^18.0.6",
|
"@angular/cli": "^18.0.6",
|
||||||
"@angular/compiler-cli": "^18.0.4",
|
"@angular/compiler-cli": "^18.0.5",
|
||||||
"@types/jasmine": "~5.1.4",
|
"@types/jasmine": "~5.1.4",
|
||||||
"jasmine-core": "~5.1.2",
|
"jasmine-core": "~5.1.2",
|
||||||
"karma": "~6.4.3",
|
"karma": "~6.4.3",
|
||||||
@ -198,6 +198,75 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular-devkit/build-angular/node_modules/@angular/build": {
|
||||||
|
"version": "18.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/build/-/build-18.0.6.tgz",
|
||||||
|
"integrity": "sha512-W6S1sx00D4pd7qDIyzPMNFmw8d783+/Aknl+2cUrYlJqw0Oan1Bt6mXVg48Jwxr0hVsovoNZXSRFXXI5hvW8ZA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@ampproject/remapping": "2.3.0",
|
||||||
|
"@angular-devkit/architect": "0.1800.6",
|
||||||
|
"@babel/core": "7.24.5",
|
||||||
|
"@babel/helper-annotate-as-pure": "7.22.5",
|
||||||
|
"@babel/helper-split-export-declaration": "7.24.5",
|
||||||
|
"@vitejs/plugin-basic-ssl": "1.1.0",
|
||||||
|
"ansi-colors": "4.1.3",
|
||||||
|
"browserslist": "^4.23.0",
|
||||||
|
"critters": "0.0.22",
|
||||||
|
"esbuild": "0.21.3",
|
||||||
|
"fast-glob": "3.3.2",
|
||||||
|
"https-proxy-agent": "7.0.4",
|
||||||
|
"inquirer": "9.2.22",
|
||||||
|
"lmdb": "3.0.8",
|
||||||
|
"magic-string": "0.30.10",
|
||||||
|
"mrmime": "2.0.0",
|
||||||
|
"ora": "5.4.1",
|
||||||
|
"parse5-html-rewriting-stream": "7.0.0",
|
||||||
|
"picomatch": "4.0.2",
|
||||||
|
"piscina": "4.5.0",
|
||||||
|
"sass": "1.77.2",
|
||||||
|
"semver": "7.6.2",
|
||||||
|
"undici": "6.18.0",
|
||||||
|
"vite": "5.2.11",
|
||||||
|
"watchpack": "2.4.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0",
|
||||||
|
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
||||||
|
"yarn": ">= 1.13.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/compiler-cli": "^18.0.0",
|
||||||
|
"@angular/localize": "^18.0.0",
|
||||||
|
"@angular/platform-server": "^18.0.0",
|
||||||
|
"@angular/service-worker": "^18.0.0",
|
||||||
|
"less": "^4.2.0",
|
||||||
|
"postcss": "^8.4.0",
|
||||||
|
"tailwindcss": "^2.0.0 || ^3.0.0",
|
||||||
|
"typescript": ">=5.4 <5.5"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@angular/localize": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@angular/platform-server": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@angular/service-worker": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"less": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"postcss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"tailwindcss": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular-devkit/build-angular/node_modules/@babel/core": {
|
"node_modules/@angular-devkit/build-angular/node_modules/@babel/core": {
|
||||||
"version": "7.24.5",
|
"version": "7.24.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
|
||||||
@ -237,6 +306,23 @@
|
|||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular-devkit/build-angular/node_modules/@ngtools/webpack": {
|
||||||
|
"version": "18.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.0.6.tgz",
|
||||||
|
"integrity": "sha512-chSRbPpnqTThURQqUvWAgEGkLcn5TQnUQPD1HBf4WcoO/OkaK4Q1Sa8FrEllkC6/Dlyj7myi8rskQz+V8K7GSg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0",
|
||||||
|
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
||||||
|
"yarn": ">= 1.13.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/compiler-cli": "^18.0.0",
|
||||||
|
"typescript": ">=5.4 <5.5",
|
||||||
|
"webpack": "^5.54.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular-devkit/build-angular/node_modules/convert-source-map": {
|
"node_modules/@angular-devkit/build-angular/node_modules/convert-source-map": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||||
@ -365,9 +451,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/animations": {
|
"node_modules/@angular/animations": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.0.5.tgz",
|
||||||
"integrity": "sha512-xbdtBUvpTGEmVQkCoOad26LBMRy9ddM9pvCidMZBWXiM7NEuc3dfVT99a1cU4MZFiJeiQEvOWQn03iXskbBMGQ==",
|
"integrity": "sha512-RYwlS+4I33beAWdzFFmaDPqXZN+r66qPzzMOk9LQguwF76eBJbykHniODalSLvjrY6Iz7CULavByYNpzq2TT7A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@ -376,143 +462,13 @@
|
|||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/core": "18.0.4"
|
"@angular/core": "18.0.5"
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@angular/build": {
|
|
||||||
"version": "18.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@angular/build/-/build-18.0.6.tgz",
|
|
||||||
"integrity": "sha512-W6S1sx00D4pd7qDIyzPMNFmw8d783+/Aknl+2cUrYlJqw0Oan1Bt6mXVg48Jwxr0hVsovoNZXSRFXXI5hvW8ZA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@ampproject/remapping": "2.3.0",
|
|
||||||
"@angular-devkit/architect": "0.1800.6",
|
|
||||||
"@babel/core": "7.24.5",
|
|
||||||
"@babel/helper-annotate-as-pure": "7.22.5",
|
|
||||||
"@babel/helper-split-export-declaration": "7.24.5",
|
|
||||||
"@vitejs/plugin-basic-ssl": "1.1.0",
|
|
||||||
"ansi-colors": "4.1.3",
|
|
||||||
"browserslist": "^4.23.0",
|
|
||||||
"critters": "0.0.22",
|
|
||||||
"esbuild": "0.21.3",
|
|
||||||
"fast-glob": "3.3.2",
|
|
||||||
"https-proxy-agent": "7.0.4",
|
|
||||||
"inquirer": "9.2.22",
|
|
||||||
"lmdb": "3.0.8",
|
|
||||||
"magic-string": "0.30.10",
|
|
||||||
"mrmime": "2.0.0",
|
|
||||||
"ora": "5.4.1",
|
|
||||||
"parse5-html-rewriting-stream": "7.0.0",
|
|
||||||
"picomatch": "4.0.2",
|
|
||||||
"piscina": "4.5.0",
|
|
||||||
"sass": "1.77.2",
|
|
||||||
"semver": "7.6.2",
|
|
||||||
"undici": "6.18.0",
|
|
||||||
"vite": "5.2.11",
|
|
||||||
"watchpack": "2.4.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0",
|
|
||||||
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
|
||||||
"yarn": ">= 1.13.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@angular/compiler-cli": "^18.0.0",
|
|
||||||
"@angular/localize": "^18.0.0",
|
|
||||||
"@angular/platform-server": "^18.0.0",
|
|
||||||
"@angular/service-worker": "^18.0.0",
|
|
||||||
"less": "^4.2.0",
|
|
||||||
"postcss": "^8.4.0",
|
|
||||||
"tailwindcss": "^2.0.0 || ^3.0.0",
|
|
||||||
"typescript": ">=5.4 <5.5"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@angular/localize": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@angular/platform-server": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@angular/service-worker": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"less": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"postcss": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"tailwindcss": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@angular/build/node_modules/@babel/core": {
|
|
||||||
"version": "7.24.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
|
|
||||||
"integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@ampproject/remapping": "^2.2.0",
|
|
||||||
"@babel/code-frame": "^7.24.2",
|
|
||||||
"@babel/generator": "^7.24.5",
|
|
||||||
"@babel/helper-compilation-targets": "^7.23.6",
|
|
||||||
"@babel/helper-module-transforms": "^7.24.5",
|
|
||||||
"@babel/helpers": "^7.24.5",
|
|
||||||
"@babel/parser": "^7.24.5",
|
|
||||||
"@babel/template": "^7.24.0",
|
|
||||||
"@babel/traverse": "^7.24.5",
|
|
||||||
"@babel/types": "^7.24.5",
|
|
||||||
"convert-source-map": "^2.0.0",
|
|
||||||
"debug": "^4.1.0",
|
|
||||||
"gensync": "^1.0.0-beta.2",
|
|
||||||
"json5": "^2.2.3",
|
|
||||||
"semver": "^6.3.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/babel"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@angular/build/node_modules/@babel/core/node_modules/semver": {
|
|
||||||
"version": "6.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
|
||||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@angular/build/node_modules/convert-source-map": {
|
|
||||||
"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==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@angular/build/node_modules/semver": {
|
|
||||||
"version": "7.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
|
|
||||||
"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/cdk": {
|
"node_modules/@angular/cdk": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.0.5.tgz",
|
||||||
"integrity": "sha512-OCG1EGv/nyZYGcSu7y6IAuarC5gZcZYhhvEQsgMUDrf1TGRSa+0dBN5W2HxRWKs6NsGgDjW1VcK+AC85PYLXPA==",
|
"integrity": "sha512-Yf94Udxip8xjVIJlxwh80h6fUpX5JFcBv3FCFer7DU/YzWdoTL+BTIYF8og+NjlDRt1nSbTxdyU2LVI0rTVkpg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@ -527,15 +483,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/cdk-experimental": {
|
"node_modules/@angular/cdk-experimental": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-18.0.5.tgz",
|
||||||
"integrity": "sha512-e1MXfNFUPJmg34dlg6mXvs/wn2U66QyKWhdha5DdZ5QECozBtdBY7WwQcjGOb7KnQm+lk38WiWG5REwqJjnvzg==",
|
"integrity": "sha512-vrEDGbQ6afdrausjP9F9fYWszT/hUe+OWlZczFUqVwd/2TveRuPoF419OIGHwJKzFkZF08wBP2qqUsHj4AZevQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/cdk": "18.0.4",
|
"@angular/cdk": "18.0.5",
|
||||||
"@angular/core": "^18.0.0 || ^19.0.0"
|
"@angular/core": "^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -586,9 +542,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/common": {
|
"node_modules/@angular/common": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.0.5.tgz",
|
||||||
"integrity": "sha512-7WxZKLzSu5QtyLGrtlZrtUQlP3WfDR++yHr5jF9DJZ3IY35UutwiPCegCcq4Qh5X2xWqnRKGm20TLlKVoj0t5Q==",
|
"integrity": "sha512-yItVQSu+Rx8gthWJDTOHwbzItY8/lqmmmYA1RMex0u3GkJoX3/3TZSGXbbBXl8GH8vmQOfp9yj3C02JmlwldRg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@ -597,14 +553,14 @@
|
|||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/core": "18.0.4",
|
"@angular/core": "18.0.5",
|
||||||
"rxjs": "^6.5.3 || ^7.4.0"
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/compiler": {
|
"node_modules/@angular/compiler": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.0.5.tgz",
|
||||||
"integrity": "sha512-OVPXtJo5SkGQUCioCVxKcRfEw48tz8xCtJGDXjVKWtyOkXnmWl8Y/e54mteiJd1KybXHvPLW0LPtWZYB06Qy7g==",
|
"integrity": "sha512-U1/qjNDjxMukXwQrJZjmr87KVxQmHbD7fxVlg0+qafHLe+YDuCtyOfQSGEZrWhwktxvAYZbl3FK+m3Hnk/D3Nw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@ -613,7 +569,7 @@
|
|||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/core": "18.0.4"
|
"@angular/core": "18.0.5"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@angular/core": {
|
"@angular/core": {
|
||||||
@ -622,9 +578,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/compiler-cli": {
|
"node_modules/@angular/compiler-cli": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.0.5.tgz",
|
||||||
"integrity": "sha512-pUv664JCZHKHsLDvO8iNjWXVHOB2ggKxVoxiowOMNpR4dqxrK/oOLGkPGltYUW/xF6Eajc7Zs0lK/R5uljoYQg==",
|
"integrity": "sha512-aFKDDTsRmc691EkNRj9OkrKNXDOaHdXB42MyUrj3WwJIJFMnSY/UDf6h+CRVF0U+CITszFyWhmeHQRA/3mJWNg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -646,14 +602,14 @@
|
|||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/compiler": "18.0.4",
|
"@angular/compiler": "18.0.5",
|
||||||
"typescript": ">=5.4 <5.5"
|
"typescript": ">=5.4 <5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/core": {
|
"node_modules/@angular/core": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.0.5.tgz",
|
||||||
"integrity": "sha512-k0AUZbJc0eyzRexvKlR1sR0qNhe54Om9ln6lRn7y1+gAsg+OwFDyF427fFuzqpZVe/MmpvX3CXWdl0twZAYEiA==",
|
"integrity": "sha512-0UuL+aMMWGYksz09YBsiHq1li7GmL8obB3IC3T5MwDqnn7FGRUBfBUOZEkM6B+pwgg+RAtNdJkbCfbh1z74bFQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@ -667,9 +623,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/forms": {
|
"node_modules/@angular/forms": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.0.5.tgz",
|
||||||
"integrity": "sha512-LM2rVIuJa2fGxP0oCy0uFSGY6h9tyL64gtGp02QqKaVszG4oJ8wue0/VSbBtKyH0xEN4eOXDzOXbiahbtFhRZA==",
|
"integrity": "sha512-nO7bN+nO2/czgKSvPx6ewqpfb8xXOyns06uovWpAXSH4jYoiZ6CHTHhOKrOL/3SRkhUV9u+EUXTTAOSBkS+OBA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@ -678,16 +634,16 @@
|
|||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/common": "18.0.4",
|
"@angular/common": "18.0.5",
|
||||||
"@angular/core": "18.0.4",
|
"@angular/core": "18.0.5",
|
||||||
"@angular/platform-browser": "18.0.4",
|
"@angular/platform-browser": "18.0.5",
|
||||||
"rxjs": "^6.5.3 || ^7.4.0"
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/material": {
|
"node_modules/@angular/material": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/material/-/material-18.0.5.tgz",
|
||||||
"integrity": "sha512-ES4peq3+tMEPKe9RgdQ3pp3CcjM0Cr+vi4f+0ruH2wu1NTBk522/1/ABHncg3A/eCurKS96JJdihqOAjMek4Ow==",
|
"integrity": "sha512-8xCyEs9yT7tp7vSBcVww+Adt2ue0oFG2yfISYRNweg0RXC9qk64DDhcIyYhn92xLSnFH1q6mPcjrNw8RmhA10A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material/animation": "15.0.0-canary.7f224ddd4.0",
|
"@material/animation": "15.0.0-canary.7f224ddd4.0",
|
||||||
@ -742,7 +698,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/animations": "^18.0.0 || ^19.0.0",
|
"@angular/animations": "^18.0.0 || ^19.0.0",
|
||||||
"@angular/cdk": "18.0.4",
|
"@angular/cdk": "18.0.5",
|
||||||
"@angular/common": "^18.0.0 || ^19.0.0",
|
"@angular/common": "^18.0.0 || ^19.0.0",
|
||||||
"@angular/core": "^18.0.0 || ^19.0.0",
|
"@angular/core": "^18.0.0 || ^19.0.0",
|
||||||
"@angular/forms": "^18.0.0 || ^19.0.0",
|
"@angular/forms": "^18.0.0 || ^19.0.0",
|
||||||
@ -751,9 +707,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/platform-browser": {
|
"node_modules/@angular/platform-browser": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.0.5.tgz",
|
||||||
"integrity": "sha512-8TJEPzIRV89s1ZP9T+7g9K7PFNfec+4Xyw5BLaTRBOqjXHmMzk+miRx0L18Lr66rp5r2vbNEE9vojMVHQRwhVA==",
|
"integrity": "sha512-hBKaGz7dhsjNhD0aWB8G2/YZQ/MaBhzFIQSAZMPs2ccAqH1Jx772/Y11k57seA3VaPpnL8WZ1apOSJgALUJ//w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@ -762,9 +718,9 @@
|
|||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/animations": "18.0.4",
|
"@angular/animations": "18.0.5",
|
||||||
"@angular/common": "18.0.4",
|
"@angular/common": "18.0.5",
|
||||||
"@angular/core": "18.0.4"
|
"@angular/core": "18.0.5"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@angular/animations": {
|
"@angular/animations": {
|
||||||
@ -773,9 +729,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/platform-browser-dynamic": {
|
"node_modules/@angular/platform-browser-dynamic": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.0.5.tgz",
|
||||||
"integrity": "sha512-K36/gamqs8etGlmWew7IwZ/bDJdI5ZeUqvOUmkKjJ9F2I/g5P/zZrB1qExwN/zsxzxd9idkvEhwY+YDeiZEEJg==",
|
"integrity": "sha512-i8CXojKcjsKzD2JR2clIisqavlHCW1jw+F2hJVrf/JR9iu6kVpGpZOqb3yYHoQCsPa7hUzQnn0ewYwBvlWsDmw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@ -784,16 +740,16 @@
|
|||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/common": "18.0.4",
|
"@angular/common": "18.0.5",
|
||||||
"@angular/compiler": "18.0.4",
|
"@angular/compiler": "18.0.5",
|
||||||
"@angular/core": "18.0.4",
|
"@angular/core": "18.0.5",
|
||||||
"@angular/platform-browser": "18.0.4"
|
"@angular/platform-browser": "18.0.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular/router": {
|
"node_modules/@angular/router": {
|
||||||
"version": "18.0.4",
|
"version": "18.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.0.5.tgz",
|
||||||
"integrity": "sha512-nr1ZI3lynKBtr3a75APuVkIaiXRG5mEnW/RIyxwzxbKBB14901mby46o0jm9Y/CPb2rH5UpuwZhTKRE6QS/xLw==",
|
"integrity": "sha512-GmdzD5FZYPKCGP6mV3AZraAU6czfGcjjCym6mIsdJr3DyMwnQSwaaHAu8qlQbPDVfsP+gKVSPh1JxI1lzzarLA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
@ -802,9 +758,9 @@
|
|||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/common": "18.0.4",
|
"@angular/common": "18.0.5",
|
||||||
"@angular/core": "18.0.4",
|
"@angular/core": "18.0.5",
|
||||||
"@angular/platform-browser": "18.0.4",
|
"@angular/platform-browser": "18.0.5",
|
||||||
"rxjs": "^6.5.3 || ^7.4.0"
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4217,23 +4173,6 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@ngtools/webpack": {
|
|
||||||
"version": "18.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.0.6.tgz",
|
|
||||||
"integrity": "sha512-chSRbPpnqTThURQqUvWAgEGkLcn5TQnUQPD1HBf4WcoO/OkaK4Q1Sa8FrEllkC6/Dlyj7myi8rskQz+V8K7GSg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0",
|
|
||||||
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
|
||||||
"yarn": ">= 1.13.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@angular/compiler-cli": "^18.0.0",
|
|
||||||
"typescript": ">=5.4 <5.5",
|
|
||||||
"webpack": "^5.54.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
24
package.json
24
package.json
@ -10,17 +10,17 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^18.0.4",
|
"@angular/animations": "^18.0.5",
|
||||||
"@angular/cdk": "~18.0.4",
|
"@angular/cdk": "~18.0.5",
|
||||||
"@angular/cdk-experimental": "^18.0.4",
|
"@angular/cdk-experimental": "^18.0.5",
|
||||||
"@angular/common": "^18.0.4",
|
"@angular/common": "^18.0.5",
|
||||||
"@angular/compiler": "^18.0.4",
|
"@angular/compiler": "^18.0.5",
|
||||||
"@angular/core": "^18.0.4",
|
"@angular/core": "^18.0.5",
|
||||||
"@angular/forms": "^18.0.4",
|
"@angular/forms": "^18.0.5",
|
||||||
"@angular/material": "~18.0.4",
|
"@angular/material": "~18.0.5",
|
||||||
"@angular/platform-browser": "^18.0.4",
|
"@angular/platform-browser": "^18.0.5",
|
||||||
"@angular/platform-browser-dynamic": "^18.0.4",
|
"@angular/platform-browser-dynamic": "^18.0.5",
|
||||||
"@angular/router": "^18.0.4",
|
"@angular/router": "^18.0.5",
|
||||||
"@progress/kendo-date-math": "^1.5.13",
|
"@progress/kendo-date-math": "^1.5.13",
|
||||||
"rxjs": "~7.8.1",
|
"rxjs": "~7.8.1",
|
||||||
"tslib": "^2.6.3",
|
"tslib": "^2.6.3",
|
||||||
@ -29,7 +29,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^18.0.6",
|
"@angular-devkit/build-angular": "^18.0.6",
|
||||||
"@angular/cli": "^18.0.6",
|
"@angular/cli": "^18.0.6",
|
||||||
"@angular/compiler-cli": "^18.0.4",
|
"@angular/compiler-cli": "^18.0.5",
|
||||||
"@types/jasmine": "~5.1.4",
|
"@types/jasmine": "~5.1.4",
|
||||||
"jasmine-core": "~5.1.2",
|
"jasmine-core": "~5.1.2",
|
||||||
"karma": "~6.4.3",
|
"karma": "~6.4.3",
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import {catchError, mergeMap, Observable, retryWhen, tap, timer} from "rxjs";
|
import {catchError, filter, mergeMap, Observable, retryWhen, switchMap, take, tap, timer} from "rxjs";
|
||||||
import {HttpClient, HttpErrorResponse} from "@angular/common/http";
|
import {HttpClient, HttpErrorResponse} from "@angular/common/http";
|
||||||
import {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
|
import {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
|
||||||
import {environment} from "@environment";
|
import {environment} from "@environment";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {Injectable} from "@angular/core";
|
import {Injectable} from "@angular/core";
|
||||||
import {RequestBuilder, RequestData, SetRequestBuilderAfterBuild} from "@api/RequestBuilder";
|
import {RequestBuilder, RequestData, SetRequestBuilderAfterBuild} from "@api/RequestBuilder";
|
||||||
|
import {TokenRefreshService} from "@service/token-refresh.service";
|
||||||
|
import {AuthToken} from "@service/auth.service";
|
||||||
|
|
||||||
export function retryWithInterval<T>(): (source: Observable<T>) => Observable<T> {
|
export function retryWithInterval<T>(): (source: Observable<T>) => Observable<T> {
|
||||||
return (source: Observable<T>) =>
|
return (source: Observable<T>) =>
|
||||||
@ -35,13 +37,14 @@ export enum AvailableVersion {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default abstract class ApiService implements SetRequestBuilderAfterBuild {
|
export default abstract class ApiService implements SetRequestBuilderAfterBuild {
|
||||||
constructor(private http: HttpClient, private notify: OpenNotifyService, private router: Router) {
|
constructor(private http: HttpClient, private notify: OpenNotifyService, private router: Router, protected tokenRefreshService: TokenRefreshService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private apiUrl = environment.apiUrl;
|
private apiUrl = environment.apiUrl;
|
||||||
protected abstract basePath: string;
|
protected abstract basePath: string;
|
||||||
protected abstract version: AvailableVersion;
|
protected abstract version: AvailableVersion;
|
||||||
private request: RequestData = RequestBuilder.getStandardRequestData();
|
private request: RequestData = RequestBuilder.getStandardRequestData();
|
||||||
|
public static readonly tokenKey = 'auth_token';
|
||||||
|
|
||||||
public setRequestBuilder(request: RequestData): void {
|
public setRequestBuilder(request: RequestData): void {
|
||||||
this.request = request;
|
this.request = request;
|
||||||
@ -128,6 +131,18 @@ export default abstract class ApiService implements SetRequestBuilderAfterBuild
|
|||||||
return this.makeHttpRequest<Type>('delete');
|
return this.makeHttpRequest<Type>('delete');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addAuth() {
|
||||||
|
const token = localStorage.getItem(ApiService.tokenKey);
|
||||||
|
|
||||||
|
if (!token)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
const authToken = AuthToken.httpHeader((JSON.parse(token) as AuthToken));
|
||||||
|
authToken.keys().forEach(key => this.request.httpHeaders = this.request.httpHeaders.append(key, authToken.get(key) ?? ''))
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private handleError(error: HttpErrorResponse): void {
|
private handleError(error: HttpErrorResponse): void {
|
||||||
// todo: change to Retry-After condition
|
// todo: change to Retry-After condition
|
||||||
if (error.error.toString().includes("setup")) {
|
if (error.error.toString().includes("setup")) {
|
||||||
@ -147,6 +162,7 @@ export default abstract class ApiService implements SetRequestBuilderAfterBuild
|
|||||||
message = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.';
|
message = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.';
|
||||||
break;
|
break;
|
||||||
case 401:
|
case 401:
|
||||||
|
this.router.navigate(['/login/']).then();
|
||||||
message = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.';
|
message = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.';
|
||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
|
@ -4,6 +4,7 @@ import {FooterComponent} from "@component/common/footer/footer.component";
|
|||||||
import localeRu from '@angular/common/locales/ru';
|
import localeRu from '@angular/common/locales/ru';
|
||||||
import { registerLocaleData } from '@angular/common';
|
import { registerLocaleData } from '@angular/common';
|
||||||
import {FocusNextDirective} from "@/directives/focus-next.directive";
|
import {FocusNextDirective} from "@/directives/focus-next.directive";
|
||||||
|
import {TokenRefreshService} from "@service/token-refresh.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@ -14,7 +15,8 @@ import {FocusNextDirective} from "@/directives/focus-next.directive";
|
|||||||
<app-footer/>`
|
<app-footer/>`
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
constructor() {
|
constructor(tokenRefreshService: TokenRefreshService) {
|
||||||
registerLocaleData(localeRu);
|
registerLocaleData(localeRu);
|
||||||
|
tokenRefreshService.startTokenRefresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
88
src/services/auth.service.ts
Normal file
88
src/services/auth.service.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import {EventEmitter, Injectable} from '@angular/core';
|
||||||
|
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
||||||
|
import {catchError, Observable, of, tap} from "rxjs";
|
||||||
|
import {TokenResponse} from "@api/v1/tokenResponse";
|
||||||
|
import ApiService from "@api/api.service";
|
||||||
|
|
||||||
|
export enum AvailableAuthenticationProvider {
|
||||||
|
Bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuthToken {
|
||||||
|
accessToken: string;
|
||||||
|
expiresIn: Date;
|
||||||
|
authProvider: AvailableAuthenticationProvider;
|
||||||
|
endpoint: string;
|
||||||
|
|
||||||
|
constructor(accessToken: string, expiresIn: Date, authProvider: AvailableAuthenticationProvider, refreshEndpoint: string) {
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.expiresIn = expiresIn;
|
||||||
|
this.authProvider = authProvider;
|
||||||
|
this.endpoint = refreshEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
static httpHeader(token: AuthToken): HttpHeaders {
|
||||||
|
let header = new HttpHeaders();
|
||||||
|
|
||||||
|
if (token.authProvider === AvailableAuthenticationProvider.Bearer)
|
||||||
|
header = header.set('Authorization', `Bearer ${token.accessToken}`);
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AuthService {
|
||||||
|
public expireTokenChange = new EventEmitter<Date>();
|
||||||
|
public tokenChangeError = new EventEmitter();
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static setToken(token: TokenResponse, provider: AvailableAuthenticationProvider, refreshEndpoint: string) {
|
||||||
|
localStorage.setItem(ApiService.tokenKey, JSON.stringify(
|
||||||
|
new AuthToken(token.accessToken, token.expiresIn, provider, refreshEndpoint)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get tokenExpiresIn(): Date {
|
||||||
|
const token = localStorage.getItem(ApiService.tokenKey);
|
||||||
|
|
||||||
|
if (!token)
|
||||||
|
return new Date();
|
||||||
|
|
||||||
|
const result = new Date((JSON.parse(token) as AuthToken).expiresIn);
|
||||||
|
return result <= new Date() ? new Date() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public refreshToken(): Observable<TokenResponse> {
|
||||||
|
const token = localStorage.getItem(ApiService.tokenKey);
|
||||||
|
|
||||||
|
if (!token)
|
||||||
|
return of();
|
||||||
|
|
||||||
|
const authToken = JSON.parse(token) as AuthToken;
|
||||||
|
|
||||||
|
switch (authToken.authProvider) {
|
||||||
|
case AvailableAuthenticationProvider.Bearer:
|
||||||
|
return this.http.get<TokenResponse>(authToken.endpoint, {withCredentials: true})
|
||||||
|
.pipe(
|
||||||
|
catchError(error => {
|
||||||
|
this.tokenChangeError.emit();
|
||||||
|
throw error;
|
||||||
|
}),
|
||||||
|
tap(response => {
|
||||||
|
const newExpireDate = new Date(response.expiresIn);
|
||||||
|
const oldExpireDate = new Date(authToken.expiresIn);
|
||||||
|
|
||||||
|
if (newExpireDate.getTime() !== oldExpireDate.getTime()) {
|
||||||
|
AuthService.setToken(response, AvailableAuthenticationProvider.Bearer, authToken.endpoint);
|
||||||
|
this.expireTokenChange.emit(newExpireDate);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
src/services/token-refresh.service.ts
Normal file
72
src/services/token-refresh.service.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import {BehaviorSubject, interval, Subscription, switchMap} from "rxjs";
|
||||||
|
import {Injectable} from "@angular/core";
|
||||||
|
import {AuthService} from "@service/auth.service";
|
||||||
|
import {environment} from "@environment";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class TokenRefreshService {
|
||||||
|
private tokenRefreshSubscription: Subscription | undefined;
|
||||||
|
private tokenRefreshing$ = new BehaviorSubject<boolean>(false);
|
||||||
|
private refreshTokenExpireMs: number = environment.retryDelay;
|
||||||
|
|
||||||
|
constructor(private authService: AuthService) {
|
||||||
|
this.setRefreshTokenExpireMs(AuthService.tokenExpiresIn.getTime() - 1000 - Date.now());
|
||||||
|
|
||||||
|
authService.tokenChangeError.subscribe(_ => {
|
||||||
|
console.log('Token change error event received');
|
||||||
|
this.tokenRefreshing$.next(false);
|
||||||
|
this.stopTokenRefresh();
|
||||||
|
});
|
||||||
|
authService.expireTokenChange.subscribe(date => {
|
||||||
|
console.log('Expire token change event received:', date);
|
||||||
|
this.setRefreshTokenExpireMs(date.getTime() - 1000 - Date.now());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public startTokenRefresh(date: Date | null = null): void {
|
||||||
|
if (date)
|
||||||
|
this.refreshTokenExpireMs = new Date(date).getTime() - 1000 - Date.now();
|
||||||
|
|
||||||
|
if (!this.tokenRefreshSubscription || this.tokenRefreshSubscription.closed) {
|
||||||
|
this.tokenRefreshSubscription = interval(this.refreshTokenExpireMs).pipe(
|
||||||
|
switchMap(() => {
|
||||||
|
this.tokenRefreshing$.next(true);
|
||||||
|
return this.authService.refreshToken();
|
||||||
|
})
|
||||||
|
).subscribe({
|
||||||
|
next: (_) => {
|
||||||
|
this.tokenRefreshing$.next(false);
|
||||||
|
},
|
||||||
|
error: error => {
|
||||||
|
console.error('Token refresh error:', error);
|
||||||
|
this.tokenRefreshing$.next(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTokenRefreshing$(): BehaviorSubject<boolean> {
|
||||||
|
return this.tokenRefreshing$;
|
||||||
|
}
|
||||||
|
|
||||||
|
public stopTokenRefresh(): void {
|
||||||
|
if (this.tokenRefreshSubscription && !this.tokenRefreshSubscription.closed) {
|
||||||
|
this.tokenRefreshSubscription.unsubscribe();
|
||||||
|
this.tokenRefreshSubscription = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setRefreshTokenExpireMs(expireMs: number): void {
|
||||||
|
if (expireMs < environment.retryDelay)
|
||||||
|
expireMs = 3000;
|
||||||
|
|
||||||
|
console.log(expireMs);
|
||||||
|
this.refreshTokenExpireMs = expireMs;
|
||||||
|
|
||||||
|
this.stopTokenRefresh();
|
||||||
|
this.startTokenRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
25
src/shared/requests/v1/loginRequest.ts
Normal file
25
src/shared/requests/v1/loginRequest.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* MIREA Schedule Web API
|
||||||
|
* This API provides a convenient interface for retrieving data stored in the database. Special attention was paid to the lightweight and easy transfer of all necessary data. Made by the Winsomnia team.
|
||||||
|
*
|
||||||
|
* OpenAPI spec version: 1.0
|
||||||
|
* Contact: support@winsomnia.net
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by the swagger code generator program.
|
||||||
|
* https://github.com/swagger-api/swagger-codegen.git
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export interface LoginRequest {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
username: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
password: number;
|
||||||
|
}
|
25
src/shared/responses/v1/tokenResponse.ts
Normal file
25
src/shared/responses/v1/tokenResponse.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* MIREA Schedule Web API
|
||||||
|
* This API provides a convenient interface for retrieving data stored in the database. Special attention was paid to the lightweight and easy transfer of all necessary data. Made by the Winsomnia team.
|
||||||
|
*
|
||||||
|
* OpenAPI spec version: 1.0
|
||||||
|
* Contact: support@winsomnia.net
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by the swagger code generator program.
|
||||||
|
* https://github.com/swagger-api/swagger-codegen.git
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export interface TokenResponse {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
accessToken : string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
expiresIn: Date;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user