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",
|
||||
"version": "1.0.0-b0",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^18.0.4",
|
||||
"@angular/cdk": "~18.0.4",
|
||||
"@angular/cdk-experimental": "^18.0.4",
|
||||
"@angular/common": "^18.0.4",
|
||||
"@angular/compiler": "^18.0.4",
|
||||
"@angular/core": "^18.0.4",
|
||||
"@angular/forms": "^18.0.4",
|
||||
"@angular/material": "~18.0.4",
|
||||
"@angular/platform-browser": "^18.0.4",
|
||||
"@angular/platform-browser-dynamic": "^18.0.4",
|
||||
"@angular/router": "^18.0.4",
|
||||
"@angular/animations": "^18.0.5",
|
||||
"@angular/cdk": "~18.0.5",
|
||||
"@angular/cdk-experimental": "^18.0.5",
|
||||
"@angular/common": "^18.0.5",
|
||||
"@angular/compiler": "^18.0.5",
|
||||
"@angular/core": "^18.0.5",
|
||||
"@angular/forms": "^18.0.5",
|
||||
"@angular/material": "~18.0.5",
|
||||
"@angular/platform-browser": "^18.0.5",
|
||||
"@angular/platform-browser-dynamic": "^18.0.5",
|
||||
"@angular/router": "^18.0.5",
|
||||
"@progress/kendo-date-math": "^1.5.13",
|
||||
"rxjs": "~7.8.1",
|
||||
"tslib": "^2.6.3",
|
||||
@ -27,7 +27,7 @@
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^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",
|
||||
"jasmine-core": "~5.1.2",
|
||||
"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": {
|
||||
"version": "7.24.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz",
|
||||
@ -237,6 +306,23 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
@ -365,9 +451,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/animations": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.0.4.tgz",
|
||||
"integrity": "sha512-xbdtBUvpTGEmVQkCoOad26LBMRy9ddM9pvCidMZBWXiM7NEuc3dfVT99a1cU4MZFiJeiQEvOWQn03iXskbBMGQ==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.0.5.tgz",
|
||||
"integrity": "sha512-RYwlS+4I33beAWdzFFmaDPqXZN+r66qPzzMOk9LQguwF76eBJbykHniODalSLvjrY6Iz7CULavByYNpzq2TT7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
@ -376,143 +462,13 @@
|
||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "18.0.4"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
"@angular/core": "18.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cdk": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.0.4.tgz",
|
||||
"integrity": "sha512-OCG1EGv/nyZYGcSu7y6IAuarC5gZcZYhhvEQsgMUDrf1TGRSa+0dBN5W2HxRWKs6NsGgDjW1VcK+AC85PYLXPA==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.0.5.tgz",
|
||||
"integrity": "sha512-Yf94Udxip8xjVIJlxwh80h6fUpX5JFcBv3FCFer7DU/YzWdoTL+BTIYF8og+NjlDRt1nSbTxdyU2LVI0rTVkpg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
@ -527,15 +483,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cdk-experimental": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-18.0.4.tgz",
|
||||
"integrity": "sha512-e1MXfNFUPJmg34dlg6mXvs/wn2U66QyKWhdha5DdZ5QECozBtdBY7WwQcjGOb7KnQm+lk38WiWG5REwqJjnvzg==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/cdk-experimental/-/cdk-experimental-18.0.5.tgz",
|
||||
"integrity": "sha512-vrEDGbQ6afdrausjP9F9fYWszT/hUe+OWlZczFUqVwd/2TveRuPoF419OIGHwJKzFkZF08wBP2qqUsHj4AZevQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/cdk": "18.0.4",
|
||||
"@angular/cdk": "18.0.5",
|
||||
"@angular/core": "^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
@ -586,9 +542,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/common": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.0.4.tgz",
|
||||
"integrity": "sha512-7WxZKLzSu5QtyLGrtlZrtUQlP3WfDR++yHr5jF9DJZ3IY35UutwiPCegCcq4Qh5X2xWqnRKGm20TLlKVoj0t5Q==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.0.5.tgz",
|
||||
"integrity": "sha512-yItVQSu+Rx8gthWJDTOHwbzItY8/lqmmmYA1RMex0u3GkJoX3/3TZSGXbbBXl8GH8vmQOfp9yj3C02JmlwldRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
@ -597,14 +553,14 @@
|
||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "18.0.4",
|
||||
"@angular/core": "18.0.5",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/compiler": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.0.4.tgz",
|
||||
"integrity": "sha512-OVPXtJo5SkGQUCioCVxKcRfEw48tz8xCtJGDXjVKWtyOkXnmWl8Y/e54mteiJd1KybXHvPLW0LPtWZYB06Qy7g==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.0.5.tgz",
|
||||
"integrity": "sha512-U1/qjNDjxMukXwQrJZjmr87KVxQmHbD7fxVlg0+qafHLe+YDuCtyOfQSGEZrWhwktxvAYZbl3FK+m3Hnk/D3Nw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
@ -613,7 +569,7 @@
|
||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "18.0.4"
|
||||
"@angular/core": "18.0.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/core": {
|
||||
@ -622,9 +578,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/compiler-cli": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.0.4.tgz",
|
||||
"integrity": "sha512-pUv664JCZHKHsLDvO8iNjWXVHOB2ggKxVoxiowOMNpR4dqxrK/oOLGkPGltYUW/xF6Eajc7Zs0lK/R5uljoYQg==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.0.5.tgz",
|
||||
"integrity": "sha512-aFKDDTsRmc691EkNRj9OkrKNXDOaHdXB42MyUrj3WwJIJFMnSY/UDf6h+CRVF0U+CITszFyWhmeHQRA/3mJWNg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -646,14 +602,14 @@
|
||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/compiler": "18.0.4",
|
||||
"@angular/compiler": "18.0.5",
|
||||
"typescript": ">=5.4 <5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/core": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.0.4.tgz",
|
||||
"integrity": "sha512-k0AUZbJc0eyzRexvKlR1sR0qNhe54Om9ln6lRn7y1+gAsg+OwFDyF427fFuzqpZVe/MmpvX3CXWdl0twZAYEiA==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.0.5.tgz",
|
||||
"integrity": "sha512-0UuL+aMMWGYksz09YBsiHq1li7GmL8obB3IC3T5MwDqnn7FGRUBfBUOZEkM6B+pwgg+RAtNdJkbCfbh1z74bFQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
@ -667,9 +623,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/forms": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.0.4.tgz",
|
||||
"integrity": "sha512-LM2rVIuJa2fGxP0oCy0uFSGY6h9tyL64gtGp02QqKaVszG4oJ8wue0/VSbBtKyH0xEN4eOXDzOXbiahbtFhRZA==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.0.5.tgz",
|
||||
"integrity": "sha512-nO7bN+nO2/czgKSvPx6ewqpfb8xXOyns06uovWpAXSH4jYoiZ6CHTHhOKrOL/3SRkhUV9u+EUXTTAOSBkS+OBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
@ -678,16 +634,16 @@
|
||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "18.0.4",
|
||||
"@angular/core": "18.0.4",
|
||||
"@angular/platform-browser": "18.0.4",
|
||||
"@angular/common": "18.0.5",
|
||||
"@angular/core": "18.0.5",
|
||||
"@angular/platform-browser": "18.0.5",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/material": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-18.0.4.tgz",
|
||||
"integrity": "sha512-ES4peq3+tMEPKe9RgdQ3pp3CcjM0Cr+vi4f+0ruH2wu1NTBk522/1/ABHncg3A/eCurKS96JJdihqOAjMek4Ow==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-18.0.5.tgz",
|
||||
"integrity": "sha512-8xCyEs9yT7tp7vSBcVww+Adt2ue0oFG2yfISYRNweg0RXC9qk64DDhcIyYhn92xLSnFH1q6mPcjrNw8RmhA10A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@material/animation": "15.0.0-canary.7f224ddd4.0",
|
||||
@ -742,7 +698,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@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/core": "^18.0.0 || ^19.0.0",
|
||||
"@angular/forms": "^18.0.0 || ^19.0.0",
|
||||
@ -751,9 +707,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/platform-browser": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.0.4.tgz",
|
||||
"integrity": "sha512-8TJEPzIRV89s1ZP9T+7g9K7PFNfec+4Xyw5BLaTRBOqjXHmMzk+miRx0L18Lr66rp5r2vbNEE9vojMVHQRwhVA==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.0.5.tgz",
|
||||
"integrity": "sha512-hBKaGz7dhsjNhD0aWB8G2/YZQ/MaBhzFIQSAZMPs2ccAqH1Jx772/Y11k57seA3VaPpnL8WZ1apOSJgALUJ//w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
@ -762,9 +718,9 @@
|
||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/animations": "18.0.4",
|
||||
"@angular/common": "18.0.4",
|
||||
"@angular/core": "18.0.4"
|
||||
"@angular/animations": "18.0.5",
|
||||
"@angular/common": "18.0.5",
|
||||
"@angular/core": "18.0.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/animations": {
|
||||
@ -773,9 +729,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/platform-browser-dynamic": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.0.4.tgz",
|
||||
"integrity": "sha512-K36/gamqs8etGlmWew7IwZ/bDJdI5ZeUqvOUmkKjJ9F2I/g5P/zZrB1qExwN/zsxzxd9idkvEhwY+YDeiZEEJg==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.0.5.tgz",
|
||||
"integrity": "sha512-i8CXojKcjsKzD2JR2clIisqavlHCW1jw+F2hJVrf/JR9iu6kVpGpZOqb3yYHoQCsPa7hUzQnn0ewYwBvlWsDmw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
@ -784,16 +740,16 @@
|
||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "18.0.4",
|
||||
"@angular/compiler": "18.0.4",
|
||||
"@angular/core": "18.0.4",
|
||||
"@angular/platform-browser": "18.0.4"
|
||||
"@angular/common": "18.0.5",
|
||||
"@angular/compiler": "18.0.5",
|
||||
"@angular/core": "18.0.5",
|
||||
"@angular/platform-browser": "18.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/router": {
|
||||
"version": "18.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.0.4.tgz",
|
||||
"integrity": "sha512-nr1ZI3lynKBtr3a75APuVkIaiXRG5mEnW/RIyxwzxbKBB14901mby46o0jm9Y/CPb2rH5UpuwZhTKRE6QS/xLw==",
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.0.5.tgz",
|
||||
"integrity": "sha512-GmdzD5FZYPKCGP6mV3AZraAU6czfGcjjCym6mIsdJr3DyMwnQSwaaHAu8qlQbPDVfsP+gKVSPh1JxI1lzzarLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
@ -802,9 +758,9 @@
|
||||
"node": "^18.19.1 || ^20.11.1 || >=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "18.0.4",
|
||||
"@angular/core": "18.0.4",
|
||||
"@angular/platform-browser": "18.0.4",
|
||||
"@angular/common": "18.0.5",
|
||||
"@angular/core": "18.0.5",
|
||||
"@angular/platform-browser": "18.0.5",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
@ -4217,23 +4173,6 @@
|
||||
"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": {
|
||||
"version": "2.1.5",
|
||||
"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,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^18.0.4",
|
||||
"@angular/cdk": "~18.0.4",
|
||||
"@angular/cdk-experimental": "^18.0.4",
|
||||
"@angular/common": "^18.0.4",
|
||||
"@angular/compiler": "^18.0.4",
|
||||
"@angular/core": "^18.0.4",
|
||||
"@angular/forms": "^18.0.4",
|
||||
"@angular/material": "~18.0.4",
|
||||
"@angular/platform-browser": "^18.0.4",
|
||||
"@angular/platform-browser-dynamic": "^18.0.4",
|
||||
"@angular/router": "^18.0.4",
|
||||
"@angular/animations": "^18.0.5",
|
||||
"@angular/cdk": "~18.0.5",
|
||||
"@angular/cdk-experimental": "^18.0.5",
|
||||
"@angular/common": "^18.0.5",
|
||||
"@angular/compiler": "^18.0.5",
|
||||
"@angular/core": "^18.0.5",
|
||||
"@angular/forms": "^18.0.5",
|
||||
"@angular/material": "~18.0.5",
|
||||
"@angular/platform-browser": "^18.0.5",
|
||||
"@angular/platform-browser-dynamic": "^18.0.5",
|
||||
"@angular/router": "^18.0.5",
|
||||
"@progress/kendo-date-math": "^1.5.13",
|
||||
"rxjs": "~7.8.1",
|
||||
"tslib": "^2.6.3",
|
||||
@ -29,7 +29,7 @@
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^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",
|
||||
"jasmine-core": "~5.1.2",
|
||||
"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 {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
|
||||
import {environment} from "@environment";
|
||||
import {Router} from "@angular/router";
|
||||
import {Injectable} from "@angular/core";
|
||||
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> {
|
||||
return (source: Observable<T>) =>
|
||||
@ -35,13 +37,14 @@ export enum AvailableVersion {
|
||||
|
||||
@Injectable()
|
||||
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;
|
||||
protected abstract basePath: string;
|
||||
protected abstract version: AvailableVersion;
|
||||
private request: RequestData = RequestBuilder.getStandardRequestData();
|
||||
public static readonly tokenKey = 'auth_token';
|
||||
|
||||
public setRequestBuilder(request: RequestData): void {
|
||||
this.request = request;
|
||||
@ -128,6 +131,18 @@ export default abstract class ApiService implements SetRequestBuilderAfterBuild
|
||||
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 {
|
||||
// todo: change to Retry-After condition
|
||||
if (error.error.toString().includes("setup")) {
|
||||
@ -147,6 +162,7 @@ export default abstract class ApiService implements SetRequestBuilderAfterBuild
|
||||
message = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.';
|
||||
break;
|
||||
case 401:
|
||||
this.router.navigate(['/login/']).then();
|
||||
message = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.';
|
||||
break;
|
||||
case 403:
|
||||
|
@ -4,6 +4,7 @@ import {FooterComponent} from "@component/common/footer/footer.component";
|
||||
import localeRu from '@angular/common/locales/ru';
|
||||
import { registerLocaleData } from '@angular/common';
|
||||
import {FocusNextDirective} from "@/directives/focus-next.directive";
|
||||
import {TokenRefreshService} from "@service/token-refresh.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -14,7 +15,8 @@ import {FocusNextDirective} from "@/directives/focus-next.directive";
|
||||
<app-footer/>`
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor() {
|
||||
constructor(tokenRefreshService: TokenRefreshService) {
|
||||
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