Compare commits
	
		
			2082 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 9dd21c644c | ||
|   | 6713190a23 | ||
|   | 9f24533425 | ||
|   | ed1d9374eb | ||
|   | f14d27129e | ||
|   | 5b04f02fbe | ||
|   | 227080e332 | ||
|   | 1e87482a49 | ||
|   | 054d31c3ea | ||
|   | 3a068a7b03 | ||
|   | b12e923c99 | ||
|   | ab33eed8d3 | ||
|   | d930a9a8eb | ||
|   | af381fce12 | ||
|   | b64ac0539e | ||
|   | 541c60b363 | ||
|   | 824e95f7cb | ||
|   | 38f7850196 | ||
|   | bef9de88e2 | ||
|   | 48cd5e7c7f | ||
|   | 3b44fda51c | ||
|   | dbfc9a5bb4 | ||
|   | 1b758aa41a | ||
|   | 43bdc70899 | ||
|   | fadda000a6 | ||
|   | 45a8c91a5a | ||
|   | 8e938f18be | ||
|   | ab1b364c54 | ||
|   | 5ec65b2fb0 | ||
|   | 926eced724 | ||
|   | f7f8802272 | ||
|   | c6910dff02 | ||
|   | ad299d0dbb | ||
|   | 8b124d1050 | ||
|   | ff41080dbd | ||
|   | 0e28606e3d | ||
|   | 6a025ceee5 | ||
|   | 6b2e53d6dc | ||
|   | b989aa5561 | ||
|   | f5b0b7ebd2 | ||
|   | 16881ae076 | ||
|   | af04112656 | ||
|   | a2863112dc | ||
|   | f531e4dfc5 | ||
|   | 8db9b32ba7 | ||
|   | dd5691cbef | ||
|   | de48b32af3 | ||
|   | 600b5042a1 | ||
|   | aac77029da | ||
|   | e50205f557 | ||
|   | e227411d1f | ||
|   | 2de0ed793f | ||
|   | cb0276f273 | ||
|   | 562b3f17c9 | ||
|   | 0f78f81c1c | ||
|   | 6594937d0a | ||
|   | 64bc6084be | ||
|   | 20434d5bd2 | ||
|   | 9ecc9380e6 | ||
|   | a57c55080b | ||
|   | b8ca06c6be | ||
|   | 5aff6461a1 | ||
|   | 6cf53fefec | ||
|   | 45132f3503 | ||
|   | f1e78a0e8a | ||
|   | 0bf28ec275 | ||
|   | 41f8412c97 | ||
|   | c535974362 | ||
|   | 1860c5f215 | ||
|   | 6d778b2d39 | ||
|   | f48b99c259 | ||
|   | 3c73b93051 | ||
|   | 98f3f2d519 | ||
|   | b76b4e8d68 | ||
|   | 07285a7c61 | ||
|   | 03c0dfef37 | ||
|   | 6ef6929c35 | ||
|   | e3c0c173f0 | ||
|   | 7d64e058d4 | ||
|   | e97ee9b64b | ||
|   | 6a03e39eeb | ||
|   | 525ec740b5 | ||
|   | b790cf5f4e | ||
|   | d1248811fd | ||
|   | 022d016e8e | ||
|   | f73245e650 | ||
|   | 484461fa05 | ||
|   | 7e0b7aff2a | ||
|   | 6c450dcb09 | ||
|   | 227f44283f | ||
|   | 74f6e79625 | ||
|   | cec43e2ce8 | ||
|   | 7553b258bb | ||
|   | 8bdbdc117e | ||
|   | 0e206be296 | ||
|   | 00b7353433 | ||
|   | 44e7a83593 | ||
|   | dd68d555d4 | ||
|   | 0456296103 | ||
|   | a1b66277ff | ||
|   | 50758b79bc | ||
|   | 06a1f902ad | ||
|   | 58f8b23b7c | ||
|   | 9e7c348b15 | ||
|   | 5f5ff8b43b | ||
|   | f626b4e5fc | ||
|   | bc23200e66 | ||
|   | 95ab59fd5a | ||
|   | 0bbee003b0 | ||
|   | d20cb7a928 | ||
|   | 952c134aba | ||
|   | 5533d980e3 | ||
|   | aa0213818f | ||
|   | af88ffd57c | ||
|   | 87447b1c2a | ||
|   | 384a12880b | ||
|   | 791f91f377 | ||
|   | 4f8b405cc3 | ||
|   | e1b7af201a | ||
|   | a04b7817a9 | ||
|   | 21bf83aec7 | ||
|   | d060d5179d | ||
|   | e73dd58e46 | ||
|   | a34e4a6942 | ||
|   | a7ee6d78b5 | ||
|   | 0a6651ac11 | ||
|   | fa25053c13 | ||
|   | 61dd4edeb5 | ||
|   | dbaeff302b | ||
|   | 6cbe7ebcf2 | ||
|   | 2826e08c8c | ||
|   | e30e337efa | ||
|   | 39788e0357 | ||
|   | c47363df71 | ||
|   | 4199453038 | ||
|   | db991081c8 | ||
|   | 24f946a727 | ||
|   | 772dae047a | ||
|   | 020f440fdd | ||
|   | c88fc5ec09 | ||
|   | 475849fbef | ||
|   | 1d63228fb2 | ||
|   | c918c0c010 | ||
|   | 1028179983 | ||
|   | 686f61ff90 | ||
|   | ae43fadca3 | ||
|   | 1bf05780b3 | ||
|   | 3e30daa1d3 | ||
|   | 54ef1233b1 | ||
|   | 4413633de7 | ||
|   | c76e20d146 | ||
|   | 36d53b7ce4 | ||
|   | 0b0bce2761 | ||
|   | d47a9be03c | ||
|   | bc32d7ae7a | ||
|   | e6747a2c2c | ||
|   | 3fb48cb64e | ||
|   | 381db1cf53 | ||
|   | d3939aeb9d | ||
|   | 5956f90557 | ||
|   | 7f54ecfe5d | ||
|   | e7bda99c8b | ||
|   | ea7ab5a2bf | ||
|   | ee566d199a | ||
|   | 56eca454b1 | ||
|   | d057974c3e | ||
|   | 9514e7fdea | ||
|   | e546113873 | ||
|   | 60f1139355 | ||
|   | 1b18b59054 | ||
|   | afec0187e9 | ||
|   | d0d7e1ccf8 | ||
|   | d72376b4b7 | ||
|   | 3be60cb305 | ||
|   | 75b1f351a1 | ||
|   | 07185f8018 | ||
|   | 2f081b62a7 | ||
|   | bbce2a11e1 | ||
|   | 96ab609157 | ||
|   | d961d56a35 | ||
|   | 82a5335a92 | ||
|   | 729a23949c | ||
|   | a1b0419d7a | ||
|   | 7a23015b9d | ||
|   | 9cbe0dbae1 | ||
|   | 4f139b7d03 | ||
|   | 1e925e2560 | ||
|   | a42e1e3af3 | ||
|   | 799caec7ee | ||
|   | dd7d30498f | ||
|   | e9b1720e98 | ||
|   | 5f41371bdb | ||
|   | 5226bfae20 | ||
|   | d1eab168b7 | ||
|   | 8760518c10 | ||
|   | 6719a352c2 | ||
|   | 8449b6c6b8 | ||
|   | 0550eaf465 | ||
|   | bdc9eb44ec | ||
|   | 37b99cb1b0 | ||
|   | 492381e4fc | ||
|   | 27d65dd799 | ||
|   | 434724310c | ||
|   | e5ad5a8521 | ||
|   | 3942819fed | ||
|   | 1cc5f538aa | ||
|   | b0d8534ea4 | ||
|   | 5b2bc38670 | ||
|   | b36aa3557c | ||
|   | a1a525e6e3 | ||
|   | 14453cc437 | ||
|   | db760ba5d8 | ||
|   | 4ae692008f | ||
|   | f37449f9ac | ||
|   | 8b7348b3d4 | ||
|   | 709cbb7b2e | ||
|   | 187f1a4ce2 | ||
|   | 0f2ebd3a54 | ||
|   | 86b63c2792 | ||
|   | cec2e71b41 | ||
|   | 9380f3e1ec | ||
|   | 76c2934e12 | ||
|   | 9714c35d4b | ||
|   | 0d9beb713a | ||
|   | c5f8d0e0b7 | ||
|   | 5e8f4b075d | ||
|   | 4b53fca3db | ||
|   | 5d9c030a09 | ||
|   | bacbb9a28e | ||
|   | 7c287e0c03 | ||
|   | 10f675137d | ||
|   | 6bdb5328e3 | ||
|   | 14f529ac4e | ||
|   | ffe96d9ef1 | ||
|   | 335f264caa | ||
|   | b73e86ea9f | ||
|   | 2bc2c4c57b | ||
|   | 6f87c2f03a | ||
|   | 75e677af88 | ||
|   | 9ffe355396 | ||
|   | 60b0fb23f8 | ||
|   | 1c05ffc6ed | ||
|   | 8cd4e43c68 | ||
|   | 8486d28310 | ||
|   | e8aadda185 | ||
|   | fcfd0428ab | ||
|   | ddcc087800 | ||
|   | d574de54b4 | ||
|   | 1682ac03ac | ||
|   | 447dbece1c | ||
|   | dcca901b17 | ||
|   | a32c779074 | ||
|   | 187c0f9829 | ||
|   | 6c970c88ab | ||
|   | 28a4520c77 | ||
|   | 02d0dcdff7 | ||
|   | 427d4e31c9 | ||
|   | c60f7aac44 | ||
|   | 42a7758a34 | ||
|   | e0bade4670 | ||
|   | 6af83152de | ||
|   | 091a30045d | ||
|   | b2c8ae0c33 | ||
|   | 8b6e0982c6 | ||
|   | caa3ebd600 | ||
|   | d248e047b3 | ||
|   | 25a95a1de0 | ||
|   | d506b2c692 | ||
|   | 24c672c2b9 | ||
|   | c407e0e83e | ||
|   | b1346698d6 | ||
|   | 5c01129567 | ||
|   | cd7ce53bd0 | ||
|   | 4c95c20061 | ||
|   | 5339db5058 | ||
|   | 623e932f8c | ||
|   | 43659de4f4 | ||
|   | 2a661eda42 | ||
|   | c85a93a17b | ||
|   | 8ab36cb3e1 | ||
|   | 72820d0708 | ||
|   | 95b551d4ba | ||
|   | 68af9a0695 | ||
|   | 6953c3989b | ||
|   | cd4cb385d5 | ||
|   | f4f6d595fc | ||
|   | d2356f00e9 | ||
|   | e408136196 | ||
|   | c355968add | ||
|   | 783f878947 | ||
|   | 4faf41e482 | ||
|   | d8964b6ed2 | ||
|   | b5b999941c | ||
|   | deeba8c4de | ||
|   | 9e48729eb0 | ||
|   | 936e374c05 | ||
|   | 0f3524b114 | ||
|   | d62de3d72c | ||
|   | 957a80da4b | ||
|   | 1dccd70f7c | ||
|   | 006a5d4e61 | ||
|   | ce7cdcf54e | ||
|   | c1ebb4a79b | ||
|   | 41964ae1de | ||
|   | 66cf34adf7 | ||
|   | 9e9e0009cf | ||
|   | d5cd9660fc | ||
|   | 12a7803653 | ||
|   | 8f7bef6a20 | ||
|   | a0715b830c | ||
|   | f87ce10dc4 | ||
|   | ee4936b8c9 | ||
|   | 3cb2592660 | ||
|   | 574c901b40 | ||
|   | 7590ba4ad5 | ||
|   | 39ce18f8bb | ||
|   | 777a4f9d3f | ||
|   | b91a938787 | ||
|   | c903abdb1b | ||
|   | 3279bc0580 | ||
|   | bd32c9f49e | ||
|   | 5507088e3d | ||
|   | 37ad48ae4a | ||
|   | 120c6e6d87 | ||
|   | 866e907b7e | ||
|   | 2bd8c2cb10 | ||
|   | c6c9919178 | ||
|   | 575bf9d1e0 | ||
|   | 82a56e0285 | ||
|   | 25d5f7c132 | ||
|   | a3e9ecf30f | ||
|   | ec5ff0a07f | ||
|   | be836d30c5 | ||
|   | 42b1529a5f | ||
|   | 47708c4807 | ||
|   | d2a51e004c | ||
|   | ab14230101 | ||
|   | 4bcf8c1f78 | ||
|   | 44d00e9da3 | ||
|   | d7e6a4493c | ||
|   | 53e89d8c54 | ||
|   | 4497c13634 | ||
|   | 4a35fade46 | ||
|   | 6cba0601fd | ||
|   | ed4332ea78 | ||
|   | aba069cec5 | ||
|   | 8a82ac0a11 | ||
|   | 1cd1456d75 | ||
|   | b791a3eb10 | ||
|   | 3b22a8b170 | ||
|   | 419e8214ca | ||
|   | b9f8571f0f | ||
|   | c6d9a9d7f8 | ||
|   | 310aba6ccb | ||
|   | 9bd89ac4f6 | ||
|   | ea6a51dca9 | ||
|   | e701bcc50c | ||
|   | a1abf06e75 | ||
|   | 76ace394b0 | ||
|   | 4dac462f8f | ||
|   | 039672b1e7 | ||
|   | 1b26ecbbf5 | ||
|   | f4a7e96943 | ||
|   | a45bc2954f | ||
|   | 62f32467b7 | ||
|   | 600a1bf201 | ||
|   | 7e196e7aa6 | ||
|   | 9ad3507b66 | ||
|   | add1bdfcf6 | ||
|   | 7ea8a7c079 | ||
|   | 28e31f5165 | ||
|   | 10a6975c5d | ||
|   | b34ea87660 | ||
|   | 86ed69c50b | ||
|   | 02e824154c | ||
|   | dc973c8491 | ||
|   | 0a28e3a8d3 | ||
|   | 7a48c260e1 | ||
|   | c55c49a3a2 | ||
|   | 0fc9b06d12 | ||
|   | d71ad5a6bf | ||
|   | f0b3028306 | ||
|   | 752992c527 | ||
|   | 43c4476396 | ||
|   | f8172bed56 | ||
|   | c1f71b4cfc | ||
|   | 4799da15e7 | ||
|   | 4936e47c7a | ||
|   | cb965373aa | ||
|   | 654a91184a | ||
|   | 57997f7f4b | ||
|   | ca2f5be3d1 | ||
|   | 1b091073f1 | ||
|   | 85d2d3c442 | ||
|   | d77dfd63cf | ||
|   | db2bf52fca | ||
|   | 2c62d97440 | ||
|   | 53b2e64214 | ||
|   | ccf82a3ee5 | ||
|   | 9cb6f35e60 | ||
|   | da2e33a040 | ||
|   | f2d260bfa4 | ||
|   | 783c7244f9 | ||
|   | c9287e0e63 | ||
|   | 69b84d115c | ||
|   | 1745b898b1 | ||
|   | 5ba4c85249 | ||
|   | 03c87bb46d | ||
|   | dff5a76e9e | ||
|   | 50386f4ca4 | ||
|   | de6ba2ec80 | ||
|   | ce126015d9 | ||
|   | f2337c3d43 | ||
|   | 41b59d322a | ||
|   | dfea7c111e | ||
|   | 3d5d672297 | ||
|   | e4dcc6f342 | ||
|   | 9847249611 | ||
|   | 19742a54ec | ||
|   | 5af9ae7a80 | ||
|   | 78962a4a75 | ||
|   | 9a35d826aa | ||
|   | c9cdc89d35 | ||
|   | 33b75703ba | ||
|   | ca55aa3a24 | ||
|   | 95307c3902 | ||
|   | 89c8eae7e2 | ||
|   | 91614772dc | ||
|   | cd497ab4dc | ||
|   | f81895905e | ||
|   | d82fafaa7a | ||
|   | 0ea01acc46 | ||
|   | df55792a68 | ||
|   | 6a19c45269 | ||
|   | 23ee345441 | ||
|   | 0e8ff253f3 | ||
|   | 89bddb0bc4 | ||
|   | 77bbca9bb3 | ||
|   | 1acd4d8d58 | ||
|   | 4db72a712d | ||
|   | da2b7ba08e | ||
|   | f8b49411bf | ||
|   | a04b0d99b0 | ||
|   | 9e9735f617 | ||
|   | f906b36f01 | ||
|   | a9d1f4f854 | ||
|   | d017ae905f | ||
|   | bede280507 | ||
|   | ea130047bc | ||
|   | d77d8de63e | ||
|   | f5413d9202 | ||
|   | 559fc8f216 | ||
|   | 34b2df911d | ||
|   | 3d5a8a8cbd | ||
|   | 2b07097e14 | ||
|   | 372e943d8b | ||
|   | 95afcedc4f | ||
|   | c1dc8f017c | ||
|   | dbeddca263 | ||
|   | e65d133dec | ||
|   | b5e1dea1c2 | ||
|   | 3f19469939 | ||
|   | 87874b037a | ||
|   | adda6841d1 | ||
|   | a8092b921b | ||
|   | 930e4d69d1 | ||
|   | d9219df45b | ||
|   | a6d4ec28db | ||
|   | b29e13f777 | ||
|   | 9363b78ff6 | ||
|   | eba411fec7 | ||
|   | 9afe99e134 | ||
|   | 6678856178 | ||
|   | a1fea38c2c | ||
|   | 2d3265e66d | ||
|   | f6d4400db9 | ||
|   | 6d892a9266 | ||
|   | 356fa08da5 | ||
|   | 2225f8c890 | ||
|   | d1a89739f9 | ||
|   | 6ec6896763 | ||
|   | 9fb718d610 | ||
|   | c913b89a47 | ||
|   | 58b45693b7 | ||
|   | 29d3ece9b1 | ||
|   | 7e5db75230 | ||
|   | 283c0b9845 | ||
|   | 071a0f1651 | ||
|   | 1a116557a4 | ||
|   | 1468297626 | ||
|   | 87e94fdf52 | ||
|   | 64e15c33c8 | ||
|   | 7170f435d1 | ||
|   | 9942be0493 | ||
|   | eaad36277f | ||
|   | 37cc498568 | ||
|   | 8a3191340a | ||
|   | 0f4e998f0a | ||
|   | 0c93b649ba | ||
|   | 947d269877 | ||
|   | b4b9453948 | ||
|   | b99a4cd095 | ||
|   | a363d5aa46 | ||
|   | ae442dabb4 | ||
|   | 1477c88397 | ||
|   | 4172462938 | ||
|   | 27341ba4f8 | ||
|   | 7443076094 | ||
|   | a764bb4b1d | ||
|   | 57130e1af7 | ||
|   | 3f8a354c99 | ||
|   | 0b10d92929 | ||
|   | 1a1c55e661 | ||
|   | 87768f9e8b | ||
|   | d079f50364 | ||
|   | 72a89835c5 | ||
|   | 39d686810b | ||
|   | d936626001 | ||
|   | cdce1c5ea6 | ||
|   | d59530a54f | ||
|   | fe480811fb | ||
|   | 2e3e0d34c3 | ||
|   | 1e2984d0d9 | ||
|   | 3e9fe693c7 | ||
|   | 63161838d8 | ||
|   | 7c92951ae9 | ||
|   | 59a25854ef | ||
|   | 3ca5768974 | ||
|   | 9a2dce43dd | ||
|   | b7fce4317b | ||
|   | 97d9514b00 | ||
|   | 58f88708fa | ||
|   | d79c874aa5 | ||
|   | 58917c1ff8 | ||
|   | 13b171a62d | ||
|   | f0a601922a | ||
|   | d90090b35e | ||
|   | 094d1142e7 | ||
|   | eb09d1c925 | ||
|   | bd5d75361e | ||
|   | 5d7c0c635b | ||
|   | 606f538bf3 | ||
|   | 574c0eea27 | ||
|   | 4730cbc543 | ||
|   | 5c63359550 | ||
|   | ec3607f779 | ||
|   | 7efd0f8098 | ||
|   | 2a7c30a21a | ||
|   | 2639c3ee3f | ||
|   | 547e0153cb | ||
|   | 423664fcfa | ||
|   | c986c230ef | ||
|   | 7c6001f76e | ||
|   | d3620946ac | ||
|   | 479643221c | ||
|   | c9ef8bcf6a | ||
|   | d655a7c2f6 | ||
|   | 306861d3b4 | ||
|   | 7abab41eed | ||
|   | acc2691e33 | ||
|   | 21a65956b0 | ||
|   | 9738275ad1 | ||
|   | 7265034703 | ||
|   | 9bcc45591d | ||
|   | 38d657ba0e | ||
|   | 6a0d16c5bb | ||
|   | 490d4abb56 | ||
|   | a7d0f7fc99 | ||
|   | 0d2d3ff528 | ||
|   | 526169f0aa | ||
|   | 42940ff0e3 | ||
|   | e9b943363d | ||
|   | 01e98ff10c | ||
|   | d38981a17d | ||
|   | 7612e91691 | ||
|   | 76b0f20033 | ||
|   | b78c0f8a29 | ||
|   | ea93c7c8fc | ||
|   | 8ac1952974 | ||
|   | b4514a5bbf | ||
|   | 568053b06d | ||
|   | bb180bfc39 | ||
|   | 5ee635caed | ||
|   | 5dfd7c8597 | ||
|   | fe61b75d7e | ||
|   | e7bf565418 | ||
|   | 12071f6251 | ||
|   | 72ed2e3ed0 | ||
|   | d7c1f134fe | ||
|   | 5072d9f7ad | ||
|   | 5d42a1247a | ||
|   | ce64be22bd | ||
|   | 36f9386e8d | ||
|   | e9dc963805 | ||
|   | 8854322997 | ||
|   | 6018f1e8ca | ||
|   | 68f8ebb543 | ||
|   | 301f0218fd | ||
|   | a9c4a56893 | ||
|   | 3ea3d72f11 | ||
|   | 33da5bf4a7 | ||
|   | e3de01948a | ||
|   | 3cd92aaf54 | ||
|   | ab4e1996dd | ||
|   | b9ef082abf | ||
|   | dac7186da3 | ||
|   | 1fd6cc2b5e | ||
|   | 0b7da9790a | ||
|   | a9e0e0ead6 | ||
|   | 079754f1a9 | ||
|   | c45b5b1e86 | ||
|   | 2115cc685c | ||
|   | b99e56fc19 | ||
|   | 74c057eb84 | ||
|   | e8ececc0dc | ||
|   | ad120b7736 | ||
|   | 61c0711e79 | ||
|   | 81c3f6fce0 | ||
|   | 0a4416e633 | ||
|   | 6bb712854d | ||
|   | 46959e2407 | ||
|   | debbb049b0 | ||
|   | 375377fef9 | ||
|   | d5758dd6a3 | ||
|   | d5deffea58 | ||
|   | fadb43d9a1 | ||
|   | 044b641df0 | ||
|   | fbbacdb2ff | ||
|   | 4bfd7acd96 | ||
|   | ff03225cf4 | ||
|   | 8e820c0e47 | ||
|   | d718314324 | ||
|   | 1c653ec1b1 | ||
|   | fe16ddadce | ||
|   | 7a978054cc | ||
|   | 8ed3b5a747 | ||
|   | 6885150800 | ||
|   | 35d33a1c7d | ||
|   | 93a16bbd3c | ||
|   | d3cf6a4985 | ||
|   | 8ef7200c32 | ||
|   | e43da5ba86 | ||
|   | 10428b4340 | ||
|   | 43b7c05cd7 | ||
|   | 057f64b04c | ||
|   | 0971d783f3 | ||
|   | 54aabad3e8 | ||
|   | 606fbb5fec | ||
|   | 5093c27ba5 | ||
|   | 1d3661c946 | ||
|   | 304f7a068d | ||
|   | 7ea91be3fc | ||
|   | d15464b1ae | ||
|   | 150435f24e | ||
|   | e49507cd14 | ||
|   | ad3cc3ebb6 | ||
|   | ecaf53836d | ||
|   | 8fc36e7b24 | ||
|   | 1dbbac2aa3 | ||
|   | 4e5775fde4 | ||
|   | 9d81c7c366 | ||
|   | 2df0441f0f | ||
|   | 4216002ce5 | ||
|   | 1af6f4e151 | ||
|   | 05f284bc11 | ||
|   | 40c19b3ad4 | ||
|   | d1d972cca0 | ||
|   | 2a969921d6 | ||
|   | 4b3e1f5187 | ||
|   | 561963aa26 | ||
|   | 6c2c192836 | ||
|   | f0730f2295 | ||
|   | 9df91d2b63 | ||
|   | 09da1941fd | ||
|   | 613c8e4c28 | ||
|   | 7b407fd34d | ||
|   | fb7ff7a803 | ||
|   | 0c1f0e0522 | ||
|   | b5d771e9aa | ||
|   | 2f64255895 | ||
|   | 5b629a5c40 | ||
|   | b749cf52c3 | ||
|   | b9a36a9223 | ||
|   | 32debf6e6b | ||
|   | 06f6ae01c9 | ||
|   | db4d8ad802 | ||
|   | 9c82de9c9e | ||
|   | 8329835528 | ||
|   | 8e624aa31b | ||
|   | a2e8741517 | ||
|   | 3b7c107e8f | ||
|   | 558bc3f971 | ||
|   | 90a326f380 | ||
|   | 867dc1ae35 | ||
|   | 3f555464f7 | ||
|   | c0c0782258 | ||
|   | 377f57bcb6 | ||
|   | a51253f1e5 | ||
|   | e674e94da6 | ||
|   | 93a415f6d3 | ||
|   | 9364b97692 | ||
|   | 030915c9ea | ||
|   | 10882c309a | ||
|   | 8232fde160 | ||
|   | 45c3474d4d | ||
|   | c5a9e85343 | ||
|   | 8c7ef59583 | ||
|   | 36404b5c92 | ||
|   | 9949a117be | ||
|   | a3896db4f5 | ||
|   | f668062cab | ||
|   | bac0721078 | ||
|   | e29d7244d8 | ||
|   | 5a34b91a7a | ||
|   | eaac8b1428 | ||
|   | 6c6275dc88 | ||
|   | bd350ae679 | ||
|   | cadee28c68 | ||
|   | 3534828a99 | ||
|   | e1c5777e25 | ||
|   | 63553d4dd3 | ||
|   | 6033d779fc | ||
|   | 97db27e61f | ||
|   | 8ed861c05f | ||
|   | 4d416657bf | ||
|   | ede0399275 | ||
|   | 1a28ada04c | ||
|   | 8ac771cde4 | ||
|   | c6afbbfc73 | ||
|   | 5ba71e8109 | ||
|   | a0d2922aca | ||
|   | 446610b9df | ||
|   | 4905eea503 | ||
|   | 519fc128f6 | ||
|   | cc10955483 | ||
|   | e0bd47bb87 | ||
|   | fb0c3c4a06 | ||
|   | ceef1fdaf2 | ||
|   | e1ac287ed1 | ||
|   | 29cd3a5945 | ||
|   | cab082166b | ||
|   | 711dfd4da9 | ||
|   | 643948331d | ||
|   | 119030f1a5 | ||
|   | 3efee3dc52 | ||
|   | 70716d5121 | ||
|   | a7d11ead62 | ||
|   | 017830e433 | ||
|   | bceb175c75 | ||
|   | 98277c6b71 | ||
|   | b227fbe523 | ||
|   | ac6659a293 | ||
|   | 30a24ced1d | ||
|   | 3d1f4ae2ed | ||
|   | e86aff049c | ||
|   | 8ec0ba7c9e | ||
|   | c2898f0b2e | ||
|   | 0747374378 | ||
|   | a0c2fea833 | ||
|   | 811c1a445f | ||
|   | 72d3041e7e | ||
|   | 503180e7ec | ||
|   | 8ad548cad7 | ||
|   | 85d9acf605 | ||
|   | fc93c5ffcb | ||
|   | d6b0a9314c | ||
|   | 83bc07c3ff | ||
|   | 6ca680cd41 | ||
|   | 2b533dcb2b | ||
|   | 13a5a09f98 | ||
|   | 75ff7ebdbe | ||
|   | 6d8f3a1fc8 | ||
|   | a11b411912 | ||
|   | 2dba817c69 | ||
|   | d4df9a545f | ||
|   | cae21391e5 | ||
|   | 573b22d2c2 | ||
|   | 14d4112a9d | ||
|   | 28aa9db9c5 | ||
|   | d50c349e00 | ||
|   | 905362c59c | ||
|   | 97e7d44310 | ||
|   | bb71a1ef23 | ||
|   | de2771d008 | ||
|   | 02de34b65a | ||
|   | c6e14889f2 | ||
|   | f843732790 | ||
|   | 80e7ff0fc1 | ||
|   | 2964db6bdd | ||
|   | 5fbd94ec74 | ||
|   | 1d8d31d4f0 | ||
|   | 707c3eed99 | ||
|   | e33a81ad36 | ||
|   | 0f7d216379 | ||
|   | f0052e97a6 | ||
|   | 5c6076d48d | ||
|   | 883bd31249 | ||
|   | d7b5fde3c7 | ||
|   | f4273efef0 | ||
|   | 471dc966d9 | ||
|   | 50368654a4 | ||
|   | b1e1cc04ce | ||
|   | 554f67e295 | ||
|   | 278bd09a6c | ||
|   | 300d083038 | ||
|   | 34664f0025 | ||
|   | 7aaa803813 | ||
|   | b075b3ec1c | ||
|   | 0f3f4b0f5b | ||
|   | fd5ed78442 | ||
|   | d516e7dcb4 | ||
|   | 6a1f1ebf9d | ||
|   | 6ba5c02dd6 | ||
|   | c9b8f90859 | ||
|   | 70a0bf6d5c | ||
|   | 4caf8d6fab | ||
|   | aa90593158 | ||
|   | 9da5531483 | ||
|   | 9483459ba7 | ||
|   | ae57883770 | ||
|   | 8cb5a89d10 | ||
|   | 1a35fb0d4d | ||
|   | 1a2483c68c | ||
|   | 6c806238b2 | ||
|   | ea6e07a38b | ||
|   | ec6b07b275 | ||
|   | e85a03a1ca | ||
|   | 5d66b7677f | ||
|   | 3932834172 | ||
|   | d2e8294a1a | ||
|   | b06aa66ad8 | ||
|   | c45b48d949 | ||
|   | f779af1f65 | ||
|   | 78d039cd5f | ||
|   | fdbd5418c7 | ||
|   | 5df42466fd | ||
|   | ff2bed87f2 | ||
|   | 765e7d0b1e | ||
|   | 71ab6d42c8 | ||
|   | 2ea2a662ef | ||
|   | c2d45349a4 | ||
|   | 1b5cf410b7 | ||
|   | 3e79e2bed0 | ||
|   | 2a1f933702 | ||
|   | 251d3b487f | ||
|   | e55ec48c7d | ||
|   | 7ce2195adb | ||
|   | 537b79f1c2 | ||
|   | e4c4d22f56 | ||
|   | c4d244398c | ||
|   | 47260cdaf0 | ||
|   | 7ef13058c7 | ||
|   | 8066cb1804 | ||
|   | c5af6062fa | ||
|   | 908a2eba20 | ||
|   | 6f43957703 | ||
|   | 897ea28cf4 | ||
|   | d056ddbff1 | ||
|   | f157350059 | ||
|   | 7e3d99bda3 | ||
|   | c7c53e1d94 | ||
|   | b912ee3f6f | ||
|   | 06c05ad8e5 | ||
|   | 7f411400d5 | ||
|   | f24f234869 | ||
|   | 4e384e7811 | ||
|   | 5143448163 | ||
|   | 8e5e9f7cbc | ||
|   | 5ea7fd01bf | ||
|   | 8f3f6bc774 | ||
|   | 7f1524b42f | ||
|   | 4b7db4689d | ||
|   | 92831ecb43 | ||
|   | d486f3f306 | ||
|   | bfb7468f62 | ||
|   | 2e417d41db | ||
|   | b17f68cbfb | ||
|   | 8a22a523d6 | ||
|   | 97fe752a4e | ||
|   | cc4ea122a2 | ||
|   | db995d29e5 | ||
|   | c22dde8008 | ||
|   | 462d888894 | ||
|   | 9311b914ad | ||
|   | c5e0488210 | ||
|   | f548c27179 | ||
|   | 1f5f405b52 | ||
|   | cd923ab838 | ||
|   | aacdff92d2 | ||
|   | 32160d388a | ||
|   | 72e31929eb | ||
|   | 29014bb40e | ||
|   | e9d0a65536 | ||
|   | f7a3b68834 | ||
|   | dcc9d3b588 | ||
|   | b8f6f65943 | ||
|   | 3dacdda21a | ||
|   | 49b766120f | ||
|   | 2d7e1553f8 | ||
|   | d5d41213b0 | ||
|   | 328df00d2e | ||
|   | e4a5bf8234 | ||
|   | d6ee0f216c | ||
|   | 0246ef6b6a | ||
|   | 7fd40518fd | ||
|   | 95eb53edf6 | ||
|   | 4cdf2962cd | ||
|   | 5bf8ccd1fd | ||
|   | dd9fbbcfa7 | ||
|   | 72d8fd5be0 | ||
|   | b577229ed3 | ||
|   | 4ae870b799 | ||
|   | 0590e74b30 | ||
|   | b9956ba800 | ||
|   | fef6259d09 | ||
|   | e24f19e88b | ||
|   | cc6de1e71c | ||
|   | b0240a1c95 | ||
|   | 8acdec333b | ||
|   | eafcbb81f9 | ||
|   | 4b083463c8 | ||
|   | 78e834e04f | ||
|   | 2ed1893620 | ||
|   | 35f188a7dd | ||
|   | 07f577a9dd | ||
|   | 25c72b50c0 | ||
|   | e07346ebea | ||
|   | 699b466291 | ||
|   | f0f24ff96f | ||
|   | 1113997c71 | ||
|   | 487fb6e5c9 | ||
|   | 62908a323c | ||
|   | 021131271a | ||
|   | c20fa4dbd2 | ||
|   | b1826678da | ||
|   | 1630338f4e | ||
|   | 78af3d979c | ||
|   | 8346dfb1f5 | ||
|   | 5ecb4e4fe4 | ||
|   | ec9ff23b23 | ||
|   | 149e9931e7 | ||
|   | 58b62094bd | ||
|   | d357074ad4 | ||
|   | d9bacc129b | ||
|   | 28d869b099 | ||
|   | d04e87718a | ||
|   | 8d81bf60c9 | ||
|   | 4fe9094ff7 | ||
|   | 4cfde45755 | ||
|   | c28844dbeb | ||
|   | 837ea7c8d6 | ||
|   | 6c85ac8827 | ||
|   | c8eb8bf35d | ||
|   | 0f6cbddd57 | ||
|   | 530b4755bf | ||
|   | 679395b3fe | ||
|   | 9293442836 | ||
|   | 4841b412f3 | ||
|   | f03575ad89 | ||
|   | e0e5428ebd | ||
|   | 015f7edae2 | ||
|   | d5414f5d91 | ||
|   | 4bc3512d42 | ||
|   | 4c305994ca | ||
|   | 788e2cba04 | ||
|   | b79b79970e | ||
|   | 8b0f4de516 | ||
|   | a17000ad6d | ||
|   | c9f63c58df | ||
|   | c311b1c706 | ||
|   | 072451afae | ||
|   | 7349793463 | ||
|   | c0144eab86 | ||
|   | ba79b25944 | ||
|   | 3fb34b12a0 | ||
|   | b6c17e76c2 | ||
|   | 1201f20d79 | ||
|   | 98ae8e692e | ||
|   | 8342dbff4e | ||
|   | a22aa0f9a7 | ||
|   | b49d221ec1 | ||
|   | f1be315147 | ||
|   | 8aaac2a3d4 | ||
|   | bd07d30e47 | ||
|   | 54e103f00f | ||
|   | 5404d8f7ab | ||
|   | 1364e02c67 | ||
|   | b4818e2f9a | ||
|   | 734cf57d4a | ||
|   | 9236610ec1 | ||
|   | c33828a5d9 | ||
|   | faadb03e46 | ||
|   | 3a0c4c51d2 | ||
|   | 6c28c2b91e | ||
|   | 5226df6194 | ||
|   | 1ad23a3cbb | ||
|   | c4149ca304 | ||
|   | 68b02fe950 | ||
|   | d9408523a5 | ||
|   | 40ad0f063a | ||
|   | 40d85e8926 | ||
|   | e655f719e6 | ||
|   | ede0ea02c5 | ||
|   | e579575b21 | ||
|   | 06ffcc9fdf | ||
|   | 774d03e510 | ||
|   | adcac9dfe6 | ||
|   | 399e14c70c | ||
|   | ec8b51033b | ||
|   | ca5d167b6a | ||
|   | fcd1b001d1 | ||
|   | 1cabbefe04 | ||
|   | ecefe22c95 | ||
|   | 64db77b9a2 | ||
|   | 19d7360ad5 | ||
|   | 2f014cd827 | ||
|   | 7fb84205d9 | ||
|   | 47e1127c5f | ||
|   | e036b59306 | ||
|   | f4904c3b53 | ||
|   | 3002a63ba5 | ||
|   | b69717e6c3 | ||
|   | d07962953f | ||
|   | 857cf0d21e | ||
|   | 5f08c2615d | ||
|   | e87428ef33 | ||
|   | 03fd54fe70 | ||
|   | 086c2c8253 | ||
|   | 05c19a32ea | ||
|   | 99d174906a | ||
|   | 82e30a326a | ||
|   | 2963a9cdca | ||
|   | 288da75b2b | ||
|   | 0943a496dd | ||
|   | e5dd7cc2fa | ||
|   | 358836ef9f | ||
|   | aab4fac6c5 | ||
|   | 525540b603 | ||
|   | b30eeb4694 | ||
|   | 3faf0aa2fc | ||
|   | fd728dec5d | ||
|   | 08b14b72d4 | ||
|   | de2e005abf | ||
|   | 0fc75239a6 | ||
|   | 390fe30a0d | ||
|   | 633f49fcd2 | ||
|   | 30c0ba93b9 | ||
|   | e935fb9621 | ||
|   | 5ce8bb1d08 | ||
|   | 1acd12980a | ||
|   | 683235dd8a | ||
|   | 65fe183ad4 | ||
|   | f39d5d355c | ||
|   | eccc8e0ff0 | ||
|   | dd4d8e775c | ||
|   | 7a7f857b2f | ||
|   | 10882b7d93 | ||
|   | e669b81005 | ||
|   | 28a81d9539 | ||
|   | 6bf5e4a6b8 | ||
|   | a51eee93f4 | ||
|   | bece2555c2 | ||
|   | d98d405009 | ||
|   | 8d0881632c | ||
|   | e8b81da897 | ||
|   | acf4fbf750 | ||
|   | b4f1921d33 | ||
|   | 94b4816f53 | ||
|   | b6ddafde3e | ||
|   | b01036818f | ||
|   | ad7da1a0c3 | ||
|   | 1e0818d9d9 | ||
|   | a4686f01c3 | ||
|   | 84d76f9aab | ||
|   | b90bf5eb86 | ||
|   | fe258f3fe5 | ||
|   | d339494594 | ||
|   | adfaf13055 | ||
|   | 8abfeb5923 | ||
|   | c1a027a771 | ||
|   | 5f3094d79b | ||
|   | 925b81aca8 | ||
|   | c0c8437966 | ||
|   | 1d27b2fc4a | ||
|   | 5da43e7808 | ||
|   | 5edbb558ae | ||
|   | 7347cc1df2 | ||
|   | 20cac11b2a | ||
|   | 75c35c4ff8 | ||
|   | 9d573512d0 | ||
|   | 7921365853 | ||
|   | 43263fd3b9 | ||
|   | 59042a5ead | ||
|   | 91b14de807 | ||
|   | 81fab2be08 | ||
|   | 165b742782 | ||
|   | 76fef9c807 | ||
|   | e69ea0b9dc | ||
|   | 98d3183f2b | ||
|   | a29390a951 | ||
|   | 6291ce8617 | ||
|   | c76b1b50a0 | ||
|   | cc45e2aec0 | ||
|   | 17efebb8e8 | ||
|   | 5c94c733ee | ||
|   | 156b89dd9c | ||
|   | 34ba9f67e7 | ||
|   | 5ddaa6b872 | ||
|   | 9043fa7f56 | ||
|   | 4c8e487dc9 | ||
|   | d3b87179aa | ||
|   | 2166de8331 | ||
|   | f0bc3f001f | ||
|   | 50448e7085 | ||
|   | cd1d42353e | ||
|   | 5e588bf737 | ||
|   | 7ff777d178 | ||
|   | 861621189a | ||
|   | dcc00e08fd | ||
|   | a1b8a47d4b | ||
|   | 1fd6b5e239 | ||
|   | f91e45bf44 | ||
|   | 0675a45592 | ||
|   | 0c7c6ae451 | ||
|   | cf089e8c4c | ||
|   | 90928ac679 | ||
|   | 3bbcf71784 | ||
|   | 222734775d | ||
|   | f23ee7a6e0 | ||
|   | 5b075aa6d5 | ||
|   | 80cd6b693e | ||
|   | 04721a12b1 | ||
|   | 64e22c0e46 | ||
|   | d5a70c5b08 | ||
|   | eaac7b6bcf | ||
|   | b062a491cd | ||
|   | 1e868517bb | ||
|   | 7b2a93a2d7 | ||
|   | f57f0447c6 | ||
|   | 7126ff881e | ||
|   | e28da4b165 | ||
|   | 92d9b91f7c | ||
|   | 149c4a30c0 | ||
|   | 84e62062ec | ||
|   | dc1fb74850 | ||
|   | 00c6010789 | ||
|   | 5d35c058e0 | ||
|   | 1522a521f6 | ||
|   | 6e11b885f9 | ||
|   | 442ae6e0e8 | ||
|   | 6b49e83464 | ||
|   | f1ecf13fe1 | ||
|   | 83d1c8582b | ||
|   | 179f6cd454 | ||
|   | 91b1474ff0 | ||
|   | 15aabc88a1 | ||
|   | afc0d3017d | ||
|   | 9e0b1dc8aa | ||
|   | 55b482fd26 | ||
|   | 4f9c9a6566 | ||
|   | 4725120ee9 | ||
|   | a288f50fbb | ||
|   | 09cf2560a4 | ||
|   | 16353de7b1 | ||
|   | eb5834cb5c | ||
|   | 3ef5736aed | ||
|   | d2e7b77d16 | ||
|   | 8f794bce75 | ||
|   | c33196a50a | ||
|   | 6e8b5b431f | ||
|   | 8ca3ecf17f | ||
|   | 91f7db59ea | ||
|   | a0b4501352 | ||
|   | 345047820a | ||
|   | ac3525a953 | ||
|   | c8c3f5b134 | ||
|   | 51319c0718 | ||
|   | 43399b8b47 | ||
|   | a6596042b7 | ||
|   | 23ae85fc9c | ||
|   | 2da54862f1 | ||
|   | f272fb0559 | ||
|   | bd04e33586 | ||
|   | b09b9752ca | ||
|   | a810a48158 | ||
|   | b4f5792aa8 | ||
|   | fdf0330b4f | ||
|   | ca73743082 | ||
|   | df0cde2cfd | ||
|   | 5a8421e807 | ||
|   | 025ac95d81 | ||
|   | 71b5824fdc | ||
|   | 1a907c0be7 | ||
|   | 1635cabd32 | ||
|   | 27946a5b0f | ||
|   | e141822fd0 | ||
|   | 6326828a02 | ||
|   | 96b5362491 | ||
|   | 8ab91a51bc | ||
|   | 35e7e7af18 | ||
|   | 0e248a7302 | ||
|   | d5ce4d6600 | ||
|   | 2365bb5e87 | ||
|   | 2230216a7e | ||
|   | 54f896d25f | ||
|   | e47d239caf | ||
|   | ac259958d7 | ||
|   | 0505ad88a6 | ||
|   | b323de3383 | ||
|   | c0e023324b | ||
|   | 495806d5e3 | ||
|   | 3a9f745a2d | ||
|   | 7b15d114a3 | ||
|   | 6ac0f72169 | ||
|   | ff68030231 | ||
|   | d34361b6dd | ||
|   | 56c6ebe523 | ||
|   | 2c6d2e0eb4 | ||
|   | 8e64196a8f | ||
|   | 926d5faf21 | ||
|   | 18bad2af25 | ||
|   | 6be4dc4aac | ||
|   | 6398a59dda | ||
|   | 48158b1db6 | ||
|   | 47fe519e54 | ||
|   | 7e06f19bbc | ||
|   | 0b66c1f0d6 | ||
|   | ff2742ffd8 | ||
|   | 70c2992a5f | ||
|   | 4ac4ee6874 | ||
|   | 865164a404 | ||
|   | f9c8069ab2 | ||
|   | 2fbf5140e5 | ||
|   | 0126f8144a | ||
|   | 71383d7482 | ||
|   | 4d3bfc5f42 | ||
|   | c173d217de | ||
|   | d2da9a039f | ||
|   | c915edd458 | ||
|   | 504948452e | ||
|   | 3daa6c7a54 | ||
|   | 10cd1580a5 | ||
|   | 5b0876e106 | ||
|   | cfcdeaff01 | ||
|   | 7c34f233fd | ||
|   | 686146a7d9 | ||
|   | 44732d5977 | ||
|   | 05c3794cf7 | ||
|   | f0431a504a | ||
|   | eccec85ea6 | ||
|   | 16ff538804 | ||
|   | f81e07eb6f | ||
|   | 1cbd41ad87 | ||
|   | d8567b8427 | ||
|   | afe6a1923b | ||
|   | 0f60f4754f | ||
|   | 7e988d0401 | ||
|   | d85813c229 | ||
|   | 12eb56809d | ||
|   | d792172779 | ||
|   | 011562ae73 | ||
|   | 71c4e36933 | ||
|   | 6261982f6c | ||
|   | 4ac01e0ed0 | ||
|   | 2dfe07f13e | ||
|   | 7f4e4017ce | ||
|   | 635970f37b | ||
|   | 106e740220 | ||
|   | 3e6420ba6f | ||
|   | 79be8010d5 | ||
|   | 0b6e679f3b | ||
|   | 97c43ad2b9 | ||
|   | cbf978745c | ||
|   | 63dceada90 | ||
|   | 366aade8b4 | ||
|   | aeb505e3d9 | ||
|   | f9482e4773 | ||
|   | 57dfc22ff7 | ||
|   | adc489b6ed | ||
|   | 0623684315 | ||
|   | 68afccd859 | ||
|   | 99eef8fb28 | ||
|   | 47e7685d39 | ||
|   | b6ea596ade | ||
|   | 44d60b469b | ||
|   | 51087408df | ||
|   | 96226d9e6e | ||
|   | 28f0f62424 | ||
|   | 2e772a8cd4 | ||
|   | 3fd192f0cf | ||
|   | d16ae81961 | ||
|   | 211a8e093e | ||
|   | 84010a51d6 | ||
|   | e9700ea19b | ||
|   | e81ec68072 | ||
|   | 6096e97708 | ||
|   | 6f302b66b4 | ||
|   | b6e41890f7 | ||
|   | be5332a048 | ||
|   | 4d72aeb0cd | ||
|   | c7c3c35100 | ||
|   | 2f0df3c552 | ||
|   | e6effcd921 | ||
|   | cd462b9523 | ||
|   | 7e2e17d38b | ||
|   | 665d616a06 | ||
|   | ced90f5bb9 | ||
|   | ca0ddb6cb4 | ||
|   | 3d733813e1 | ||
|   | 45ddc0b154 | ||
|   | 87294497f3 | ||
|   | 5f45f9c0d0 | ||
|   | 7f78c06793 | ||
|   | 8ea5b7eebc | ||
|   | e49cd511af | ||
|   | 15c88646df | ||
|   | f47919543d | ||
|   | a95291d9cd | ||
|   | 033deb3d29 | ||
|   | 923994c1f4 | ||
|   | 3d45b839b4 | ||
|   | 55d1efa212 | ||
|   | 21d794a0e5 | ||
|   | 614022a78c | ||
|   | c00688e23a | ||
|   | 5c69917d19 | ||
|   | 6a5eb75b6c | ||
|   | 727c55eaa2 | ||
|   | 9b03173ec5 | ||
|   | ab95855d6d | ||
|   | bb84594c6b | ||
|   | 97392c76b1 | ||
|   | 53aec2b306 | ||
|   | a0a381dc63 | ||
|   | 4fa95edeec | ||
|   | b944f1d70e | ||
|   | 0f1a5d0085 | ||
|   | e7e10222e7 | ||
|   | 87c4fda588 | ||
|   | 0ff820de6f | ||
|   | 0dcf55e6d9 | ||
|   | a69842c910 | ||
|   | 318f635e4d | ||
|   | abf0d72316 | ||
|   | 5cd89c8844 | ||
|   | 501c1af38c | ||
|   | 8526e130d9 | ||
|   | 76fd08eade | ||
|   | bfe48ae9d2 | ||
|   | 8547cdba7c | ||
|   | 00d0cd631d | ||
|   | 04460d13cd | ||
|   | a17a15e5d7 | ||
|   | bfe2465658 | ||
|   | 4568987785 | ||
|   | c0e5a1419d | ||
|   | 23ac7ab748 | ||
|   | 5622dc74a8 | ||
|   | 528986f4f0 | ||
|   | e6b8ff3f91 | ||
|   | 3fc3c5296f | ||
|   | f4f2a14a31 | ||
|   | fa3f464cb6 | ||
|   | bed20538ae | ||
|   | 3da461c6e6 | ||
|   | 9c73e8452a | ||
|   | 750dab69d3 | ||
|   | 702d0da47c | ||
|   | 3a955b1e53 | ||
|   | d408bd93e8 | ||
|   | 63fd9eb1de | ||
|   | 4718ab6ddf | ||
|   | affd4b3a26 | ||
|   | 6a660dbcda | ||
|   | 5ff3af8b48 | ||
|   | 9d49070f93 | ||
|   | 5b3994a1dd | ||
|   | bb1e62c580 | ||
|   | 29cc776908 | ||
|   | d2ef60b2ec | ||
|   | 41ea496d41 | ||
|   | c69bb38929 | ||
|   | 23b3f8494e | ||
|   | 0ff94eec1b | ||
|   | f67a638565 | ||
|   | 778cf71c61 | ||
|   | e2da083b48 | ||
|   | dcc48c0d4b | ||
|   | 2446b4a591 | ||
|   | a973d633c8 | ||
|   | c3b11e6e0f | ||
|   | 44bec2d99e | ||
|   | 0d59d0a3f6 | ||
|   | ddc8372b09 | ||
|   | ea7325f4e0 | ||
|   | 6048f95415 | ||
|   | 5682705b8d | ||
|   | 6c3b82607d | ||
|   | 486c9f4ebf | ||
|   | 7c8adebf2d | ||
|   | ce98bb0dc1 | ||
|   | 60b49a4296 | ||
|   | 5d0a2ffae0 | ||
|   | bbcf7c722a | ||
|   | 66574eec6d | ||
|   | 305df3a42c | ||
|   | 323c6f4270 | ||
|   | c056b2b14c | ||
|   | 02ebb07fd8 | ||
|   | 59db0890c3 | ||
|   | 3ea0d9ed3a | ||
|   | 0371d11dd5 | ||
|   | 12f1ebaee9 | ||
|   | 5202cc22d1 | ||
|   | 07a34bdcbf | ||
|   | 27806da4be | ||
|   | a15dd1a3ce | ||
|   | 6cc7451c7c | ||
|   | 652aeee782 | ||
|   | 8e1e887b8f | ||
|   | 6601a2de64 | ||
|   | 75a42ffc32 | ||
|   | 8a80a52f5f | ||
|   | b373a434ba | ||
|   | cd78fdafe6 | ||
|   | 7a1b4c6db7 | ||
|   | f2c7664033 | ||
|   | 12b295f067 | ||
|   | 3ee46da40f | ||
|   | beadaa7212 | ||
|   | 16ef09426f | ||
|   | b8c1f1f5a9 | ||
|   | e027b5cbd6 | ||
|   | bf1bd73ad1 | ||
|   | 86fc95119a | ||
|   | b3a76ea17b | ||
|   | bababd9d53 | ||
|   | 8cd5180531 | ||
|   | f1ccbade8c | ||
|   | a66f4b0417 | ||
|   | ff495b2261 | ||
|   | 90e5824212 | ||
|   | 3d64021062 | ||
|   | fb8ed7428d | ||
|   | bdc273c10a | ||
|   | 2d96cffdc7 | ||
|   | 4356fccbcd | ||
|   | a169fd4ce7 | ||
|   | dcd418139e | ||
|   | 0cac2062d3 | ||
|   | 854c5d4ade | ||
|   | 0f96c6ec84 | ||
|   | b219bd66c1 | ||
|   | cb52f2c0b3 | ||
|   | d3273e03ef | ||
|   | afbfd963f3 | ||
|   | 2cd101c5f3 | ||
|   | b10bc24dee | ||
|   | 51a23df861 | ||
|   | 03a0a9dad9 | ||
|   | 362c0affbe | ||
|   | d5b523479f | ||
|   | 7719b8f6d7 | ||
|   | 3656c0d524 | ||
|   | 8b9e0dd6ea | ||
|   | b3921b1037 | ||
|   | d7d96e5dbf | ||
|   | 3934395b7b | ||
|   | 04fbe9a529 | ||
|   | badd6dd9a2 | ||
|   | 059082456d | ||
|   | 405b68e22f | ||
|   | c90bf6692c | ||
|   | b5ea5d0cc5 | ||
|   | 243617a1e1 | ||
|   | 9e703edd59 | ||
|   | d6c2cf2810 | ||
|   | ae7811acfa | ||
|   | e55731c099 | ||
|   | 9df5c74da4 | ||
|   | 8b75f9f785 | ||
|   | bf12428cf4 | ||
|   | b551df978b | ||
|   | c91d85d2f0 | ||
|   | 965237fa1f | ||
|   | 24cd9afc06 | ||
|   | 602fdefebd | ||
|   | 7e7818aa17 | ||
|   | 27a6023a6a | ||
|   | 3861879900 | ||
|   | 807eeb8351 | ||
|   | 045fa53d58 | ||
|   | 69d405ece7 | ||
|   | d3dc4d0c5b | ||
|   | 7beba1188e | ||
|   | 9779ebe12c | ||
|   | f72cfaa093 | ||
|   | cf8ccafb4a | ||
|   | 7fe281b0b8 | ||
|   | 5f70e9574f | ||
|   | 4be51923ba | ||
|   | a72c78f4a4 | ||
|   | b4d32a6de1 | ||
|   | 8a6b2d5daa | ||
|   | bebadeef14 | ||
|   | 3551be67f0 | ||
|   | 1103950b25 | ||
|   | 9d06e01cec | ||
|   | 24be946b7e | ||
|   | f56d2fc60f | ||
|   | 9f8356f409 | ||
|   | 8e6345d938 | ||
|   | 0eac78bd1c | ||
|   | 21db2502d0 | ||
|   | 38c3492123 | ||
|   | 6376bff476 | ||
|   | 38495a3ebc | ||
|   | cf00897be2 | ||
|   | da640f24ec | ||
|   | ed6a777c42 | ||
|   | 81af4485d3 | ||
|   | 94d8f0237d | ||
|   | c5e579dd38 | ||
|   | 7865e76ee2 | ||
|   | 4d37212cc0 | ||
|   | 9ca43f0bb4 | ||
|   | fe8d262175 | ||
|   | 493d6e7165 | ||
|   | 17b630bcd8 | ||
|   | 018c5ad3b4 | ||
|   | a14b4da977 | ||
|   | 01b4597cc7 | ||
|   | 0b083ccc1c | ||
|   | ca22cb0eae | ||
|   | bcbb8e5f8c | ||
|   | bea286cdd4 | ||
|   | 892d2ee8d2 | ||
|   | 7440608c14 | ||
|   | 6246f0295b | ||
|   | 53c39d9b43 | ||
|   | 9a50b60031 | ||
|   | fd74527320 | ||
|   | f91fb522ac | ||
|   | 7019c02b18 | ||
|   | 94f8422b9b | ||
|   | b25c01567a | ||
|   | b9329885de | ||
|   | ac88c4cff9 | ||
|   | 9674f2d238 | ||
|   | 38c9ecb6f1 | ||
|   | be9e9cdd5a | ||
|   | afdaf07446 | ||
|   | 5caaf10225 | ||
|   | daffc3a776 | ||
|   | 34c9748ce5 | ||
|   | f17acbf4d1 | ||
|   | 229f56ce2f | ||
|   | 0847bedc51 | ||
|   | 9e4546c305 | ||
|   | 97b01bf26a | ||
|   | e7011628d0 | ||
|   | 044ffdecbd | ||
|   | 1a06a3543b | ||
|   | 5fedc0bf1e | ||
|   | 7a881f867e | ||
|   | dfe73a6cfb | ||
|   | 45d0fb27ad | ||
|   | 31890af61d | ||
|   | a54b3ecc15 | ||
|   | c37cd1bd51 | ||
|   | 0e3ee89cbf | ||
|   | a049176c14 | ||
|   | 5ef2b0089e | ||
|   | 1cc4899593 | ||
|   | 26d1390be9 | ||
|   | dc65584fb3 | ||
|   | b3d5a708d5 | ||
|   | 904a5d296b | ||
|   | cc82e6a47a | ||
|   | a9d02ec854 | ||
|   | 6b5a2d3767 | ||
|   | 10d9060b94 | ||
|   | 8441839c01 | ||
|   | b669f5838f | ||
|   | 1a942f9ce1 | ||
|   | ae840758f7 | ||
|   | 082c08e5f9 | ||
|   | 74a43557ab | ||
|   | 184271789d | ||
|   | fa61b0cad8 | ||
|   | 7b197a90d1 | ||
|   | 62384af2f6 | ||
|   | b466202a1a | ||
|   | 7ab38c18d8 | ||
|   | 39841ee43e | ||
|   | e419800d98 | ||
|   | 63c99ab69a | ||
|   | e2a8fd2279 | ||
|   | bbde520471 | ||
|   | 45fb12f98e | ||
|   | 4be3dcce50 | ||
|   | d12a41c769 | ||
|   | ed9a58c9ed | ||
|   | 4389cea5a1 | ||
|   | d24854920b | ||
|   | 9902443bee | ||
|   | 8040b2ef16 | ||
|   | 4533680b10 | ||
|   | 3b28175135 | ||
|   | 8e22812265 | ||
|   | 409bc91b9e | ||
|   | 61fe543a2a | ||
|   | 8a949a7e64 | ||
|   | b0dc9fb97a | ||
|   | 83114d1002 | ||
|   | 94a25a903f | ||
|   | 4f402f9e55 | ||
|   | 27bb2e3dcc | ||
|   | 66cc52f6ec | ||
|   | ed1367b116 | ||
|   | 5c055352e4 | ||
|   | 8dcff5ada1 | ||
|   | 4b8b23d7d5 | ||
|   | e576f6aed3 | ||
|   | 97ab04da91 | ||
|   | c6361bb36e | ||
|   | b0b3dc225d | ||
|   | 9cbaba192a | ||
|   | f511598e13 | ||
|   | 5ffff3b7aa | ||
|   | 24c4fa1c4f | ||
|   | 7f312c1273 | ||
|   | ef5c226c81 | ||
|   | 3f029cf799 | ||
|   | 5bf64a8bec | ||
|   | 89f9c537f8 | ||
|   | e8d7f57818 | ||
|   | 9b42fa78e7 | ||
|   | 96d98e8f39 | ||
|   | 1f3a281e78 | ||
|   | dab4b66ee0 | ||
|   | 64dd192ddb | ||
|   | b0a5c383d0 | ||
|   | 4836cc99eb | ||
|   | ba33873354 | ||
|   | 12c838cab0 | ||
|   | a83a1c83a7 | ||
|   | 2234f47170 | ||
|   | 4bae1b17fc | ||
|   | 6a7a0e0fa6 | ||
|   | bff987c55f | ||
|   | e467006913 | ||
|   | 1d646d4a6d | ||
|   | 70bd4936a1 | ||
|   | 5ae7add6d2 | ||
|   | 02b206ce7c | ||
|   | 2ea626a567 | ||
|   | 653909ec11 | ||
|   | 1d7c148eaf | ||
|   | 94a7e130ca | ||
|   | 609abacdf0 | ||
|   | 033ff2e49a | ||
|   | 00a9bf0641 | ||
|   | 100f322456 | ||
|   | 2251e08d30 | ||
|   | 14e9beef53 | ||
|   | c7cce23d54 | ||
|   | 7becf32352 | ||
|   | 741eda9f4e | ||
|   | 473f51481c | ||
|   | 684f9bd11b | ||
|   | b4b7f0c360 | ||
|   | cf42d02628 | ||
|   | cc9a9eab7b | ||
|   | 784d6abc9b | ||
|   | 0eafc07184 | ||
|   | 7e9499bd2e | ||
|   | d01a52aa95 | ||
|   | 6bcd492980 | ||
|   | 96b15da04b | ||
|   | e0f1801693 | ||
|   | d195ad85c5 | ||
|   | 773f7b6c1b | ||
|   | 40f29fe399 | ||
|   | b19eb117c1 | ||
|   | f930ba5eff | ||
|   | fbbda6c230 | ||
|   | 47db4968e5 | ||
|   | 1aff2e518c | ||
|   | 24134ca49e | ||
|   | aadbe05a5e | ||
|   | 18eddf4700 | ||
|   | 0869ba7e70 | ||
|   | cba5db1c53 | ||
|   | 0aa5430e23 | ||
|   | 88a91828cb | ||
|   | 27af89f5fe | ||
|   | 6f6d483bfe | ||
|   | 915072c191 | ||
|   | 76077b92ae | ||
|   | f0d1a527a5 | ||
|   | fdb2a96d1c | ||
|   | 849e3ac187 | ||
|   | 5c4cf21c59 | ||
|   | 40ee3e5d9b | ||
|   | 77a7cc034e | ||
|   | 82b7881765 | ||
|   | 3515775267 | ||
|   | 10e12aa1c1 | ||
|   | 7fb4e62aa3 | ||
|   | 2605eaa614 | ||
|   | 8e97b4820d | ||
|   | 89af4cdb68 | ||
|   | 6099c1a25a | ||
|   | fd23758aea | ||
|   | 8ad0b89db1 | ||
|   | e7dffa3ff4 | ||
|   | 2772b5d2e2 | ||
|   | df6d9b5f70 | ||
|   | c9622d6d57 | ||
|   | 1f0b7c3c7e | ||
|   | c12245037e | ||
|   | fe6d2a50a5 | ||
|   | 7be84950f8 | ||
|   | ba212da222 | ||
|   | 122b833256 | ||
|   | 7c73479041 | ||
|   | 151f2e99ae | ||
|   | da367e813f | ||
|   | 9143dfe336 | ||
|   | 29914d6a72 | ||
|   | 0dbef72a97 | ||
|   | 3bb118bfe1 | ||
|   | 85ce3be077 | ||
|   | 064c2bf0a8 | ||
|   | c96d06ccc5 | ||
|   | a658dbb753 | ||
|   | bf5d5b629e | ||
|   | 241d576519 | ||
|   | 7807e9bdd0 | ||
|   | fcbf15fed6 | ||
|   | cd90a11209 | ||
|   | ff65707ea2 | ||
|   | b16026070c | ||
|   | 1b5fbf86d8 | ||
|   | 5eb1be8101 | ||
|   | 6a863cd26a | ||
|   | 87ebb7b6c7 | ||
|   | 1233a74307 | ||
|   | 221890acfb | ||
|   | 687c7e766e | ||
|   | ba22c407d3 | ||
|   | a8b9ed56b5 | ||
|   | 3004869e19 | ||
|   | 1145853fe1 | ||
|   | f1617a25b1 | ||
|   | 932be558d9 | ||
|   | ee3a37d3b9 | ||
|   | 5ac412a582 | ||
|   | 1625290bc3 | ||
|   | 1ec169ad49 | ||
|   | 53be552e44 | ||
|   | d3a75d46b9 | ||
|   | 256512c961 | ||
|   | 5c12ac5bcc | ||
|   | 02a2bcb113 | ||
|   | 1f0a01c725 | ||
|   | 6ea164ede1 | ||
|   | 65bae85ecc | ||
|   | 2fd7dcf4d0 | ||
|   | aa3bbbe038 | ||
|   | 6cb09a6f95 | ||
|   | 45868f05d3 | ||
|   | aac7401e20 | ||
|   | c2fc290edd | ||
|   | 330357fc36 | ||
|   | f4a3d6a64e | ||
|   | 2e0963ec81 | ||
|   | 897cb6e62a | ||
|   | 80868bd48e | ||
|   | b3df78c56f | ||
|   | 783a4259e3 | ||
|   | fcf19b8dc8 | ||
|   | 1f9f89817d | ||
|   | 7b94da7d85 | ||
|   | 164406f6c2 | ||
|   | 90ef2adc6b | ||
|   | d65f10f88b | ||
|   | dca0ece9e0 | ||
|   | baabc155c8 | ||
|   | 7eb94412d6 | ||
|   | 0fc8b24f85 | ||
|   | 88f6ef5b96 | ||
|   | 7442483419 | ||
|   | 9c61933c04 | ||
|   | 2b36a99720 | ||
|   | c2cfc42ba4 | ||
|   | 4f32704e08 | ||
|   | fccf43685f | ||
|   | 39135d81ad | ||
|   | ff4b10681e | ||
|   | 10b7908fc2 | ||
|   | 31790da8c6 | ||
|   | 4621201c47 | ||
|   | 692ac31dbf | ||
|   | ed1d163d55 | ||
|   | e931c9040c | ||
|   | f23cb48ea4 | ||
|   | c3b2b6b07b | ||
|   | f89bf590ba | ||
|   | fea17dc00b | ||
|   | dbb1069920 | ||
|   | afc4ccfaa9 | ||
|   | 38d55a1c07 | ||
|   | 5fca29f103 | ||
|   | 78f34b2ca4 | ||
|   | 8e8028e809 | ||
|   | 8dc70687f8 | ||
|   | fa22f9ee64 | ||
|   | b33c0d3f81 | ||
|   | 339009add4 | ||
|   | 798c823c4b | ||
|   | 14ad86f2a3 | ||
|   | a3a714dc17 | ||
|   | e8d8b0d41d | ||
|   | 17daad8f89 | ||
|   | e178263b3b | ||
|   | 4febbc261e | ||
|   | ef3a6942fd | ||
|   | f8ac2be62b | ||
|   | 25447c34e5 | ||
|   | 37cdf8fd48 | ||
|   | ac8e7dc959 | ||
|   | 8293382840 | ||
|   | d85a3e7743 | ||
|   | f3f5ffb5c8 | ||
|   | 99e6702c62 | ||
|   | e143c25078 | ||
|   | 3fb67972be | ||
|   | 61c04d4e09 | ||
|   | b22f728958 | ||
|   | 112be7e383 | ||
|   | 6a7f83fed5 | ||
|   | 4b9698a735 | ||
|   | ec3d203a6d | ||
|   | e77b8f5475 | ||
|   | da8bbd321f | ||
|   | 98fd011def | ||
|   | 9dccbc5316 | ||
|   | 03fa26daf9 | ||
|   | fbc41e3895 | ||
|   | f1a6d0c02c | ||
|   | 67f6bb7155 | ||
|   | 86282f596c | ||
|   | e0f24c795c | ||
|   | 7d44ed860c | ||
|   | 091094a24c | ||
|   | ba18ee518c | ||
|   | 513a031691 | ||
|   | cc79de1106 | ||
|   | bd90e8efb2 | ||
|   | 4bd88ff11d | ||
|   | d27ee61292 | ||
|   | 0c20b3345f | ||
|   | 0f3c8c7193 | ||
|   | 16761ec605 | ||
|   | 1069440cda | ||
|   | 60f5702f17 | ||
|   | ee70133e47 | ||
|   | 06a9fdeb2e | ||
|   | f14bd62004 | ||
|   | 67cbaf22b7 | ||
|   | 60b166dba2 | ||
|   | 66e1647ede | ||
|   | a1a35c00a5 | ||
|   | b02e3361c4 | ||
|   | 2b9ceaa25a | ||
|   | 109b5a9755 | ||
|   | cc4df86c10 | ||
|   | e93532c395 | ||
|   | 6ff688326a | ||
|   | eda5d2872f | ||
|   | dc88394f5f | ||
|   | ec85c9a2c6 | ||
|   | 1341638556 | ||
|   | 4875dfee11 | ||
|   | d834ba8bd4 | ||
|   | 6191067771 | ||
|   | 6c3a571163 | ||
|   | 5efb07e10e | ||
|   | b25ec18ce6 | ||
|   | f3cd281241 | ||
|   | 58b93cbf4c | ||
|   | 57fc0349ff | ||
|   | d0a2cea772 | ||
|   | 58d5801fb5 | ||
|   | e5f2e59798 | ||
|   | 1166921057 | ||
|   | aeb49576f2 | ||
|   | 7ef1fecef8 | ||
|   | fd630373b5 | ||
|   | d365d8f170 | ||
|   | dfa5e1172f | ||
|   | daf195898a | ||
|   | c61c0a39cf | ||
|   | 6137e6baa5 | ||
|   | fd16fd9ffe | ||
|   | 2b8bd5f2cc | ||
|   | d312c2e9e7 | ||
|   | 578b0d2268 | ||
|   | ffb41b0109 | ||
|   | c63fb5d796 | ||
|   | 8caee732e8 | ||
|   | df5381adce | ||
|   | f34836b7fa | ||
|   | 44b459883a | ||
|   | 14a8592ae3 | ||
|   | 787d0dce4a | ||
|   | 8be05ff93d | ||
|   | 3014af565c | ||
|   | 315252bdc4 | ||
|   | c36c3b4607 | ||
|   | 7d5e939bab | ||
|   | f75c9d1eed | ||
|   | 38cb9855ea | ||
|   | ed8b56d624 | ||
|   | bca48b13ae | ||
|   | 29426edb05 | ||
|   | 33a2dc687f | ||
|   | e2398a21b2 | ||
|   | f19530276e | ||
|   | 2b9b92a78c | ||
|   | 99c55dac10 | ||
|   | 25667e46f9 | ||
|   | 2f4e8f2399 | ||
|   | 9169183769 | ||
|   | c420f50831 | ||
|   | c6c9279ef4 | ||
|   | c125e2991d | ||
|   | 5348b19d6a | ||
|   | afd426daac | ||
|   | 202c511cfa | ||
|   | 651eb295a4 | ||
|   | d798aaed33 | ||
|   | 63ef347cc9 | ||
|   | 3139b2d5a0 | ||
|   | 2c80bbb244 | ||
|   | af06755ada | ||
|   | 97d6dbaa6c | ||
|   | 74cf82a1c7 | ||
|   | 74634889ab | ||
|   | dbe30fcd77 | ||
|   | 24a7f3a320 | ||
|   | d3650f1145 | ||
|   | 4801db9050 | ||
|   | 6445281658 | ||
|   | 87719f5938 | ||
|   | 5ba1ec433b | ||
|   | 7c5b382458 | ||
|   | 8960426128 | ||
|   | e1d47d5a92 | ||
|   | 9407c272aa | ||
|   | bccbd7b400 | ||
|   | a24dc010ec | ||
|   | c5e5d50fb8 | ||
|   | be1c520320 | ||
|   | 3d18d0f893 | ||
|   | c3351a38a6 | ||
|   | 0b86340a8d | ||
|   | 829371b032 | ||
|   | b342207bc7 | ||
|   | 58f4fdced3 | ||
|   | 253844cbcf | ||
|   | e2e2b9ffb4 | ||
|   | 2568042f5f | ||
|   | 722edf4b9a | ||
|   | e406d364e4 | ||
|   | 31c37f41b2 | ||
|   | 6cd879bbc5 | ||
|   | b7974050fe | ||
|   | e9c9d0816e | ||
|   | a437692e1a | ||
|   | 2a14d2f3c8 | ||
|   | 559e2d1889 | ||
|   | a1aa919f80 | ||
|   | cbccb27a5a | ||
|   | bbad36d576 | ||
|   | df6c9d55b5 | ||
|   | 36a1d9c364 | ||
|   | 1f3681d5ac | ||
|   | 0159f8e53f | ||
|   | 0432be64fc | ||
|   | 481e062961 | ||
|   | 72b8abdeb6 | ||
|   | 7ec3ee41d1 | ||
|   | b765fa4769 | ||
|   | 06685b162e | ||
|   | 7bcad7c424 | ||
|   | 6e054b3cc6 | ||
|   | e643f6b0f8 | ||
|   | e44c0f85c2 | ||
|   | ea66e968eb | ||
|   | f7e73d804e | ||
|   | 2ff4df56a1 | ||
|   | 346c45fe0c | ||
|   | f98b42a36e | ||
|   | f77a0a266c | ||
|   | 0be672788f | ||
|   | e2e3d11d42 | ||
|   | bb127bb567 | ||
|   | 8eb4f89db8 | ||
|   | 4a87ea3e70 | ||
|   | 61115fce99 | ||
|   | 4f31120394 | ||
|   | b085cd65ce | ||
|   | f76ec0721a | ||
|   | ef3944fbbf | ||
|   | b158fbc0d8 | ||
|   | 68f2d66e97 | ||
|   | 0cdd1735bd | ||
|   | 70f5ead20b | ||
|   | 574e5616f8 | ||
|   | 94fa810590 | ||
|   | 37f69da701 | ||
|   | f2f8448ade | ||
|   | b357d3fff2 | ||
|   | b417194905 | ||
|   | d8741da20a | ||
|   | c469be9a62 | ||
|   | ff4eb339ef | ||
|   | 78489383c0 | ||
|   | f67c1b415f | ||
|   | 570c16d921 | ||
|   | c8bc60568a | ||
|   | 73de3ba856 | ||
|   | 6a69be8537 | ||
|   | 3ee381d505 | ||
|   | 7ce744e2e4 | ||
|   | b81d21c991 | ||
|   | e9fe0992c6 | ||
|   | d5ef9018fa | ||
|   | c2737a7c51 | ||
|   | 4b6d8733c6 | ||
|   | f22a3e0955 | ||
|   | 4b8144a2f7 | ||
|   | abac52e23c | ||
|   | 9aa089313e | ||
|   | b18f2c481a | ||
|   | ec83b9f77b | ||
|   | fad5495e02 | ||
|   | 70dec1171e | ||
|   | 0673a6fce3 | ||
|   | 8f525b1407 | ||
|   | e83a991a4b | ||
|   | c000432a52 | ||
|   | c06d2d6927 | ||
|   | aa29653a8f | ||
|   | 3f7e4ad486 | ||
|   | 0eab546b2f | ||
|   | 8830d216d1 | ||
|   | 9fa1e3d449 | ||
|   | fe5dce7159 | ||
|   | 952fa31548 | ||
|   | cc058ccc61 | ||
|   | bc26ed9701 | ||
|   | 26135fc1a0 | ||
|   | 0bd14672ff | ||
|   | 7c7150cde8 | ||
|   | b08282b0c1 | ||
|   | 3bf6e1befc | ||
|   | 52692371ac | ||
|   | 159ac1d8bb | ||
|   | 7f1ffdbc79 | ||
|   | 5fe64931dc | ||
|   | ee67855b48 | ||
|   | 594a97f43f | ||
|   | 7fb435a8b4 | ||
|   | 01e74d6116 | ||
|   | b321d75b39 | ||
|   | 1e47a45723 | ||
|   | 88c609e5ef | ||
|   | 775d1a424e | 
							
								
								
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -362,14 +362,8 @@ MigrationBackup/ | ||||
| # Fody - auto-generated XML schema | ||||
| FodyWeavers.xsd | ||||
|  | ||||
| /src/Plugins/Other | ||||
| /src/ThingsGateway.Web.Server/*.db | ||||
| /src/PluginPro*/ | ||||
| /src/*Pro*/ | ||||
| /src/*Pro* | ||||
| /src/TestResults*/ | ||||
| /src/ThingsGateway.Web.Server/ThingsGateway.db | ||||
|  | ||||
| /handbook/ | ||||
|  | ||||
|  | ||||
| /test | ||||
| /src/*pro* | ||||
| /src/*pro*/ | ||||
| /src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								Image/1.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 135 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/2.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 125 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/3.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 217 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/4.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 119 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/5.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 189 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/6.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 151 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/7.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 187 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/8.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 164 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/9.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 149 KiB | 
| Before Width: | Height: | Size: 14 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/pay.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 326 KiB | 
							
								
								
									
										202
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,202 @@ | ||||
|  | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
|  | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
|  | ||||
|    1. Definitions. | ||||
|  | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
|  | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
|  | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
|  | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
|  | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
|  | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
|  | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
|  | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
|  | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
|  | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
|  | ||||
|    4. Cachetribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
|  | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
|  | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
|  | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
|  | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
|  | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
|  | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
|  | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
|  | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
|  | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
|  | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
|  | ||||
|    END OF TERMS AND CONDITIONS | ||||
|  | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
|  | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "[]" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
|  | ||||
|    Copyright 2023-present Diego | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
							
								
								
									
										201
									
								
								LICENSE.txt
									
									
									
									
									
								
							
							
						
						| @@ -1,201 +0,0 @@ | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
|  | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
|  | ||||
|    1. Definitions. | ||||
|  | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
|  | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
|  | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
|  | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
|  | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
|  | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
|  | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
|  | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
|  | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
|  | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
|  | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
|  | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
|  | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
|  | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
|  | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
|  | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
|  | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
|  | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
|  | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
|  | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
|  | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
|  | ||||
|    END OF TERMS AND CONDITIONS | ||||
|  | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
|  | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "[]" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
|  | ||||
|    Copyright [yyyy] [name of copyright owner] | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
							
								
								
									
										184
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,131 +1,101 @@ | ||||
| # ThingsGateway | ||||
|  | ||||
|  | ||||
| <div align='center'> | ||||
| <img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/gitLogo.png" height=100 /> | ||||
| </div> | ||||
|  | ||||
| #### 介绍 | ||||
| ## Introduction | ||||
|  | ||||
| 基于Net6/7+Blazor Server的跨平台边缘采集网关,支持南北端插件式开发, | ||||
| 并拥有较完善的北端Rpc权限管理。 | ||||
|  | ||||
| A cross-platform, high-performance edge data collection gateway based on net9. | ||||
|  | ||||
|  | ||||
| [Github地址](https://github.com/kimdiego2098/ThingsGateway) | ||||
| ## Documentation | ||||
|  | ||||
| <div > | ||||
| 如果对您有帮助,请点击右上角⭐Star关注,感谢支持开源! | ||||
| </div> | ||||
|  | ||||
| #### 开源说明 | ||||
|  | ||||
| Apache 2.0+[附加协议](https://diego2098.gitee.io/thingsgateway-docs/docs/) | ||||
|  | ||||
| Apache 2.0 开源协议的核心内容是以保护和尊重原作者的著作权为主要目的。对使用,复制,修改,商用不做过多限制,但必须包含原著的License信息。 | ||||
|  | ||||
| ####  功能亮点 | ||||
|  | ||||
| - Blazor Server架构,开发部署更简单 | ||||
| - 采集/上传配置完全支持Excel导入导出 | ||||
| - 插件式驱动,方便驱动二次开发 | ||||
| - 支持采集通道冗余,上传离线缓存 | ||||
| - 时序数据库存储 | ||||
| - 实时/历史报警(Sql转储),支持布尔/高低限值 | ||||
|  | ||||
| #### 演示 | ||||
|  | ||||
| http://120.24.62.140:5000/ | ||||
|  | ||||
| 默认账户密码:superAdmin 111111 | ||||
|  | ||||
| [Documentation](https://thingsgateway.cn/). | ||||
|  | ||||
| [NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22) | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 社区版采集插件 | ||||
| > 支持分包解析/订阅 | ||||
| - Modbus(Rtu/Tcp/Udp) | ||||
| - OPCDAClient(支持导入节点) | ||||
| - OPCUAClient(支持导入节点,动态类型) | ||||
| - 西门子S7协议 | ||||
| ## Demo | ||||
|  | ||||
| #### 社区版上传插件 | ||||
| > 支持Rpc写入 | ||||
| - Modbus Server | ||||
| - OPCUA Server (支持历史查询) | ||||
| - Mqtt Server (支持自定义json) | ||||
| - Mqtt Client (支持自定义json) | ||||
| - IotSharp Client (IotSharp网关插件,Rpc待测试) | ||||
|  | ||||
| [Demo](https://demo.thingsgateway.cn/) | ||||
|  | ||||
| > 不支持Rpc | ||||
| - RabbitMQ (支持自定义json) | ||||
| - Kafka | ||||
|  | ||||
| Account: **SuperAdmin** | ||||
|  | ||||
| #### nuget | ||||
|  | ||||
| Password: **111111** | ||||
|  | ||||
| - Modbus库,支持ModbusTcp、ModbusRtu、ModbusRtuOverTcp、ModbusUdp、ModbusServer等 | ||||
| ``` powershell | ||||
|  dotnet add package ThingsGateway.Foundation.Adapter.Modbus | ||||
| ``` | ||||
| - OPCDA客户端库,支持X64,支持NetCore,支持检测重连 | ||||
| ``` powershell | ||||
|  dotnet add package ThingsGateway.Foundation.Adapter.OPCDA | ||||
| ``` | ||||
| - OPCUA客户端库 | ||||
| ``` powershell | ||||
|  dotnet add package ThingsGateway.Foundation.Adapter.OPCUA | ||||
|  | ||||
| **In the upper-right corner, switch to the IoT Gateway module in the personal popup box** | ||||
|  | ||||
| ## Docker | ||||
|  | ||||
| ```shell | ||||
|  | ||||
| docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway | ||||
|  | ||||
| docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64 | ||||
| ``` | ||||
|  | ||||
| - S7库 | ||||
| ``` powershell | ||||
|  dotnet add package ThingsGateway.Foundation.Adapter.Siemens | ||||
| ``` | ||||
|  | ||||
| ####  效果图 | ||||
|  <table> | ||||
|     <tr> | ||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/1.png"/></td> | ||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/2.png"/></td> | ||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/3.png"/></td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/4.png"/></td> | ||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/5.png"/></td> | ||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/6.png"/></td> | ||||
|     </tr> | ||||
|         <tr> | ||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/7.png"/></td> | ||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/8.png"/></td> | ||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/9.png"/></td> | ||||
|     </tr> | ||||
|  </table> | ||||
|  | ||||
|  | ||||
|  ####  文档 | ||||
| ### Plugin List | ||||
|  | ||||
|  使用前请查看Gitee Pages [文档站点](https://diego2098.gitee.io/thingsgateway-docs/) | ||||
|  | ||||
|  | ||||
|  #### 特别鸣谢 | ||||
| -  Furion:[https://dotnetchina.gitee.io/furion](https://dotnetchina.gitee.io/furion) | ||||
| -  SqlSugar:[https://gitee.com/dotnetchina/SqlSugar](https://gitee.com/dotnetchina/SqlSugar) | ||||
| -  Simple.Admin:[https://gitee.com/zxzyjs/SimpleAdmin](https://gitee.com/zxzyjs/SimpleAdmin) | ||||
| -  Masa.Blazor:[https://www.masastack.com/blazor](https://www.masastack.com/blazor) | ||||
| -  MiniExcel:[https://gitee.com/dotnetchina/MiniExcel](https://gitee.com/dotnetchina/MiniExcel) | ||||
| -  TouchSocket:[https://gitee.com/rrqm_home/touchsocket](https://gitee.com/rrqm_home/touchsocket) | ||||
| -  IdGenerator:[https://github.com/yitter/idgenerator](https://github.com/yitter/idgenerator) | ||||
| -  CodingSeb.ExpressionEvaluator:[https://github.com/codingseb/ExpressionEvaluator](https://github.com/codingseb/ExpressionEvaluator) | ||||
| -  Hardware.Info:[https://github.com/Jinjinov/Hardware.Info](https://github.com/Jinjinov/Hardware.Info) | ||||
| -  UAParser:[https://github.com/ua-parser/uap-csharp](https://github.com/ua-parser/uap-csharp) | ||||
| -  OPCUAWebPlatformUniCT:[https://github.com/OPCUAUniCT/OPCUAWebPlatformUniCT](https://github.com/OPCUAUniCT/OPCUAWebPlatformUniCT) | ||||
|  | ||||
| #### 补充说明 | ||||
| * 使用OPC相关插件时请遵循OPC基金会的授权规则 | ||||
| * 使用OPCDA插件时,需安装OPC核心库,[文件地址](https://gitee.com/diego2098/ThingsGateway/attach_files) | ||||
| #### Data Collection Plugins | ||||
|  | ||||
|  | ||||
| | Plugin Name | Remarks                                                       | | ||||
| | ----------- | ------------------------------------------------------------- | | ||||
| | Modbus      | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links   | | ||||
| | SiemensS7   | Siemens PLC S7 series                                         | | ||||
| | Dlt6452007  | Supports Serial/Tcp/Udp links                                 | | ||||
| | OpcDaMaster | Compiled for 64-bit                                           | | ||||
| | OpcUaMaster | Supports certificate login, object extension, Json read/write | | ||||
| | Webhook          | Webhook                                             | | ||||
|  | ||||
| ####  支持作者 | ||||
|  如果对您有帮助,请点击右上角⭐Star关注,感谢支持开源! | ||||
| #### Business Plugins | ||||
|  | ||||
|  若希望捐赠项目,请查看以下捐赠码或使用Gitee捐赠功能 | ||||
|  | ||||
| <img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/pay.png" height=180 /> | ||||
| | Plugin Name      | Remarks                                                                                           | | ||||
| | ---------------- | ------------------------------------------------------------------------------------------------- | | ||||
| | ModbusSlave      | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links, supports Rpc reverse writing         | | ||||
| | OpcUaServer      | OpcUa server, supports Rpc reverse writing                                                        | | ||||
| | MqttClient       | Mqtt client, supports Rpc reverse writing, script-customizable upload content                     | | ||||
| | MqttServer       | Mqtt server, supports WebSocket, supports Rpc reverse writing, script-customizable upload content | | ||||
| | KafkaProducer    | Script-customizable upload content                                                                | | ||||
| | RabbitMQProducer | Script-customizable upload content                                                                | | ||||
| | SqlDB            | Relational database storage, supports historical storage and real-time data updates               | | ||||
| | SqlHistoryAlarm      | Alarm historical data relational database storage                                                 | | ||||
| | TDengineDB       | Time-series database storage                                                                      | | ||||
| | QuestDB          | Time-series database storage                                                                      | | ||||
|  | ||||
| ####  联系作者 | ||||
|  * QQ群:605534569 | ||||
|  * 邮箱:2248356998@qq.com | ||||
|  | ||||
|  | ||||
| ## License | ||||
|  | ||||
|  | ||||
| [License](https://thingsgateway.cn/docs/1) | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Sponsorship | ||||
|  | ||||
|  | ||||
| [Sponsorship Approach](https://thingsgateway.cn/docs/1000) | ||||
|  | ||||
|  | ||||
| ## Community | ||||
|  | ||||
|  | ||||
| QQ Group: 605534569 [Jump](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569) | ||||
|  | ||||
|  | ||||
| ## Pro Plugins | ||||
|  | ||||
|  | ||||
| [Plugin List](https://thingsgateway.cn/docs/1001) | ||||
|   | ||||
							
								
								
									
										86
									
								
								README.zh-CN.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,86 @@ | ||||
| # ThingsGateway | ||||
|  | ||||
| ## 介绍 | ||||
|  | ||||
| 基于net9的跨平台高性能边缘采集网关 | ||||
|  | ||||
| ## 文档 | ||||
|  | ||||
| [文档](https://thingsgateway.cn/) | ||||
|  | ||||
| [NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22) | ||||
|  | ||||
|  | ||||
| ## 演示 | ||||
|  | ||||
| [ThingsGateway演示地址](https://demo.thingsgateway.cn/) | ||||
|  | ||||
| 账户	:  **SuperAdmin** | ||||
|  | ||||
| 密码 : **111111** | ||||
|  | ||||
| **右上角个人弹出框中,切换到物联网关模块** | ||||
|  | ||||
| ## Docker | ||||
|  | ||||
| ```shell | ||||
|  | ||||
| docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway | ||||
|  | ||||
| docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64 | ||||
| ``` | ||||
|  | ||||
|  | ||||
| ### 插件列表 | ||||
|  | ||||
| #### 采集插件 | ||||
|  | ||||
|  | ||||
| | 插件名称    | 备注                                  | | ||||
| | ----------- | ------------------------------------- | | ||||
| | Modbus      | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路 | | ||||
| | SiemensS7   | 西门子PLC S7系列                      | | ||||
| | Dlt6452007  | 支持串口/Tcp/Udp链路                  | | ||||
| | OpcDaMaster | 64位编译                              | | ||||
| | OpcUaMaster | 支持证书登录,扩展对象,Json读写      | | ||||
|  | ||||
| #### 业务插件 | ||||
|  | ||||
|  | ||||
| | 插件名称         | 备注                                                       | | ||||
| | ---------------- | ---------------------------------------------------------- | | ||||
| | ModbusSlave      | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路,支持Rpc反写         | | ||||
| | OpcUaServer      | OpcUa服务端,支持Rpc反写                                   | | ||||
| | MqttClient       | Mqtt客户端,支持Rpc反写,脚本自定义上传内容                | | ||||
| | MqttServer       | Mqtt服务端,支持WebSocket,支持Rpc反写,脚本自定义上传内容 | | ||||
| | KafkaProducer    | 脚本自定义上传内容                                         | | ||||
| | RabbitMQProducer | 脚本自定义上传内容                                         | | ||||
| | SqlDB            | 关系数据库存储,支持历史存储和实时数据更新                 | | ||||
| | SqlHistoryAlarm      | 报警历史数据关系数据库存储                                 | | ||||
| | TDengineDB       | 时序数据库存储                                             | | ||||
| | QuestDB          | 时序数据库存储                                             | | ||||
| | Webhook          | Webhook                                             | | ||||
|  | ||||
| ## 协议 | ||||
|  | ||||
| [版权声明](https://thingsgateway.cn/docs/1) | ||||
|  | ||||
|  | ||||
| ## 赞助 | ||||
|  | ||||
| [赞助途径](https://thingsgateway.cn/docs/1000) | ||||
|  | ||||
| ## 社区 | ||||
|  | ||||
| QQ群:605534569 [跳转](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569) | ||||
|  | ||||
| ## Pro插件 | ||||
|  | ||||
| [插件列表](https://thingsgateway.cn/docs/1001) | ||||
|  | ||||
|  | ||||
| ## 特别声明 | ||||
|  | ||||
| ThingsGateway 项目已加入 [dotNET China](https://gitee.com/dotnetchina)  组织。<br/> | ||||
|  | ||||
|  | ||||
							
								
								
									
										133
									
								
								src/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,133 @@ | ||||
| root = true | ||||
|  | ||||
| # To learn more about .editorconfig see https://aka.ms/editorconfigdocs | ||||
| ############################### | ||||
| # Core EditorConfig Options   # | ||||
| ############################### | ||||
|  | ||||
| [*.cs] | ||||
| #### 命名样式 #### | ||||
|  | ||||
| # 命名规则 | ||||
|  | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i | ||||
|  | ||||
| dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.types_should_be_pascal_case.symbols = types | ||||
| dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| # 符号规范 | ||||
|  | ||||
| dotnet_naming_symbols.interface.applicable_kinds = interface | ||||
| dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.interface.required_modifiers = | ||||
|  | ||||
| dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum | ||||
| dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.types.required_modifiers = | ||||
|  | ||||
| dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method | ||||
| dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.non_field_members.required_modifiers = | ||||
|  | ||||
| # 命名样式 | ||||
|  | ||||
| dotnet_naming_style.begins_with_i.required_prefix = I | ||||
| dotnet_naming_style.begins_with_i.required_suffix = | ||||
| dotnet_naming_style.begins_with_i.word_separator = | ||||
| dotnet_naming_style.begins_with_i.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.pascal_case.required_prefix = | ||||
| dotnet_naming_style.pascal_case.required_suffix = | ||||
| dotnet_naming_style.pascal_case.word_separator = | ||||
| dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.pascal_case.required_prefix = | ||||
| dotnet_naming_style.pascal_case.required_suffix = | ||||
| dotnet_naming_style.pascal_case.word_separator = | ||||
| dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||
| csharp_using_directive_placement = outside_namespace:silent | ||||
| csharp_style_expression_bodied_methods = false:silent | ||||
| csharp_style_expression_bodied_constructors = false:silent | ||||
| csharp_style_expression_bodied_operators = false:silent | ||||
| csharp_style_expression_bodied_properties = true:silent | ||||
| csharp_style_expression_bodied_indexers = true:silent | ||||
| csharp_style_expression_bodied_accessors = true:silent | ||||
| csharp_style_expression_bodied_lambdas = true:silent | ||||
| csharp_style_expression_bodied_local_functions = false:silent | ||||
| csharp_style_conditional_delegate_call = true:suggestion | ||||
| csharp_style_var_for_built_in_types = false:silent | ||||
| csharp_style_var_when_type_is_apparent = false:silent | ||||
| csharp_style_var_elsewhere = false:silent | ||||
| csharp_prefer_simple_using_statement = true:suggestion | ||||
| csharp_prefer_braces = true:silent | ||||
| csharp_style_namespace_declarations = block_scoped:silent | ||||
|  | ||||
| [*.vb] | ||||
| #### 命名样式 #### | ||||
|  | ||||
| # 命名规则 | ||||
|  | ||||
| dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion | ||||
| dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface | ||||
| dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始 | ||||
|  | ||||
| dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion | ||||
| dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型 | ||||
| dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 | ||||
|  | ||||
| dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion | ||||
| dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员 | ||||
| dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 | ||||
|  | ||||
| # 符号规范 | ||||
|  | ||||
| dotnet_naming_symbols.interface.applicable_kinds = interface | ||||
| dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||
| dotnet_naming_symbols.interface.required_modifiers = | ||||
|  | ||||
| dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum | ||||
| dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||
| dotnet_naming_symbols.类型.required_modifiers = | ||||
|  | ||||
| dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method | ||||
| dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||
| dotnet_naming_symbols.非字段成员.required_modifiers = | ||||
|  | ||||
| # 命名样式 | ||||
|  | ||||
| dotnet_naming_style.以_i_开始.required_prefix = I | ||||
| dotnet_naming_style.以_i_开始.required_suffix = | ||||
| dotnet_naming_style.以_i_开始.word_separator = | ||||
| dotnet_naming_style.以_i_开始.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_prefix = | ||||
| dotnet_naming_style.帕斯卡拼写法.required_suffix = | ||||
| dotnet_naming_style.帕斯卡拼写法.word_separator = | ||||
| dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_prefix = | ||||
| dotnet_naming_style.帕斯卡拼写法.required_suffix = | ||||
| dotnet_naming_style.帕斯卡拼写法.word_separator = | ||||
| dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case | ||||
|  | ||||
| file_header_template = ------------------------------------------------------------------------------\n此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充\n此代码版权(除特别声明外的代码)归作者本人Diego所有\n源代码使用协议遵循本仓库的开源协议及附加协议\nGitee源代码仓库:https://gitee.com/diego2098/ThingsGateway\nGithub源代码仓库:https://github.com/kimdiego2098/ThingsGateway\n使用文档:https://thingsgateway.cn/\nQQ群:605534569\n------------------------------------------------------------------------------ | ||||
|  | ||||
| [*.{cs,vb}] | ||||
| end_of_line = crlf | ||||
| dotnet_style_qualification_for_field = false:silent | ||||
| dotnet_style_qualification_for_property = false:silent | ||||
| dotnet_style_qualification_for_method = false:silent | ||||
| dotnet_style_qualification_for_event = false:silent | ||||
|  | ||||
| dotnet_diagnostic.RCS1146.severity = warning | ||||
| dotnet_diagnostic.RCS1059.severity = none | ||||
| dotnet_diagnostic.RCS1138.severity = suggestion | ||||
|  | ||||
| dotnet_code_quality.CA1822.api_surface = private, internal | ||||
							
								
								
									
										21
									
								
								src/Admin/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
|  | ||||
| <div align="center"><h1 align="center">ThingsBlazor</a></h1></div> | ||||
| <div align="center"><h3 align="center">权限管理框架</h3></div> | ||||
|  | ||||
|  | ||||
| ### 🎁 框架介绍 | ||||
|  一个通用的AspNetCore小型权限框架,BlazorServer实现RBAC权限管理,在线会话管理。 | ||||
|  | ||||
|  | ||||
|  ### 📙 衍生作品 | ||||
|   - 👉[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) | ||||
|     跨平台边缘采集网关 | ||||
|  | ||||
| ### 💐 特别鸣谢/源码参考(引用) | ||||
|  - 👉[Simple.Admin](https://gitee.com/zxzyjs/SimpleAdmin) | ||||
|  - 👉[Furion](https://gitee.com/dotnetchina/Furion) | ||||
|  - 👉[Bootstrap Blazor](https://www.blazor.zone/) | ||||
|  | ||||
| ### 🍎 联系作者 | ||||
|  * QQ群:605534569 | ||||
|  * 邮箱:2248356998@qq.com | ||||
							
								
								
									
										21
									
								
								src/Admin/README.zh-CN.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
|  | ||||
| <div align="center"><h1 align="center">ThingsBlazor</a></h1></div> | ||||
| <div align="center"><h3 align="center">权限管理框架</h3></div> | ||||
|  | ||||
|  | ||||
| ### 🎁 框架介绍 | ||||
|  一个通用的AspNetCore小型权限框架,BlazorServer实现RBAC权限管理,在线会话管理。 | ||||
|  | ||||
|  | ||||
|  ### 📙 衍生作品 | ||||
|   - 👉[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) | ||||
|     跨平台边缘采集网关 | ||||
|  | ||||
| ### 💐 特别鸣谢/源码参考(引用) | ||||
|  - 👉[Simple.Admin](https://gitee.com/zxzyjs/SimpleAdmin) | ||||
|  - 👉[Furion](https://gitee.com/dotnetchina/Furion) | ||||
|  - 👉[Bootstrap Blazor](https://www.blazor.zone/) | ||||
|  | ||||
| ### 🍎 联系作者 | ||||
|  * QQ群:605534569 | ||||
|  * 邮箱:2248356998@qq.com | ||||
| @@ -0,0 +1,171 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Hosting; | ||||
|  | ||||
| using Rougamo; | ||||
| using Rougamo.Context; | ||||
| using Rougamo.Metadatas; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| using ThingsGateway.Extension; | ||||
| using ThingsGateway.FriendlyException; | ||||
| using ThingsGateway.NewLife.Json.Extension; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// Aop拦截器 | ||||
| /// </summary> | ||||
| [Pointcut(AccessFlags.Public | AccessFlags.Method)] | ||||
| [Advice(Feature.OnException | Feature.OnSuccess)] | ||||
| public sealed class OperDescAttribute : MoAttribute | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志消息队列(线程安全) | ||||
|     /// </summary> | ||||
|     private static readonly ConcurrentQueue<SysOperateLog> _logMessageQueue = new(); | ||||
|     private static readonly IAppService AppService; | ||||
|  | ||||
|     static OperDescAttribute() | ||||
|     { | ||||
|         // 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中 | ||||
|         Task.Factory.StartNew(ProcessQueue, TaskCreationOptions.LongRunning); | ||||
|         AppService = App.RootServices.GetService<IAppService>(); | ||||
|     } | ||||
|  | ||||
|     public OperDescAttribute(string description, bool isRecordPar = true, object localizerType = null) | ||||
|     { | ||||
|         Description = description; | ||||
|         IsRecordPar = isRecordPar; | ||||
|         LocalizerType = (Type)localizerType; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 说明,需配置本地化json文件 | ||||
|     /// </summary> | ||||
|     public string Description { get; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 是否记录进出参数 | ||||
|     /// </summary> | ||||
|     public bool IsRecordPar { get; } | ||||
|  | ||||
|     public Type? LocalizerType { get; } | ||||
|  | ||||
|     public override void OnException(MethodContext context) | ||||
|     { | ||||
|         if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true) | ||||
|         { | ||||
|             //插入异常日志 | ||||
|             SysOperateLog log = GetOperLog(LocalizerType, context); | ||||
|  | ||||
|             log.Category = LogCateGoryEnum.Exception;//操作类型为异常 | ||||
|             log.ExeStatus = false;//操作状态为失败 | ||||
|             if (context.Exception is AppFriendlyException exception) | ||||
|                 log.ExeMessage = exception?.Message; | ||||
|             else | ||||
|                 log.ExeMessage = context.Exception?.ToString(); | ||||
|  | ||||
|             OperDescAttribute.WriteToQueue(log); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public override void OnSuccess(MethodContext context) | ||||
|     { | ||||
|         if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true) | ||||
|         { | ||||
|  | ||||
|             //插入操作日志 | ||||
|             SysOperateLog log = GetOperLog(LocalizerType, context); | ||||
|             OperDescAttribute.WriteToQueue(log); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 将日志消息写入数据库中 | ||||
|     /// </summary> | ||||
|     private static async Task ProcessQueue() | ||||
|     { | ||||
|         var db = DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew(); | ||||
|         var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!; | ||||
|         while (!appLifetime.ApplicationStopping.IsCancellationRequested) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var data = _logMessageQueue.ToListWithDequeue(); // 从日志队列中获取数据 | ||||
|                 if (data.Count > 0) | ||||
|                 { | ||||
|                     await db.InsertableWithAttr(data).ExecuteCommandAsync(appLifetime.ApplicationStopping).ConfigureAwait(false);//入库 | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 NewLife.Log.XTrace.WriteException(ex); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 await Task.Delay(3000, appLifetime.ApplicationStopping).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private SysOperateLog GetOperLog(Type? localizerType, MethodContext context) | ||||
|     { | ||||
|         var methodBase = context.Method; | ||||
|         var userAgent = AppService.UserAgent; | ||||
|         string? paramJson = null; | ||||
|         if (IsRecordPar) | ||||
|         { | ||||
|             var args = context.Arguments; | ||||
|             var parametersInfo = methodBase.GetParameters(); | ||||
|             var parametersDict = new Dictionary<string, object>(); | ||||
|  | ||||
|             for (int i = 0; i < parametersInfo.Length; i++) | ||||
|             { | ||||
|                 parametersDict[parametersInfo[i].Name!] = args[i]; | ||||
|             } | ||||
|             paramJson = parametersDict.ToSystemTextJsonString(); | ||||
|         } | ||||
|         var result = context.ReturnValue; | ||||
|         var resultJson = IsRecordPar ? result?.ToSystemTextJsonString() : null; | ||||
|         //操作日志表实体 | ||||
|         var log = new SysOperateLog | ||||
|         { | ||||
|             Name = (localizerType == null ? App.CreateLocalizerByType(typeof(OperDescAttribute)) : App.CreateLocalizerByType(localizerType))![Description], | ||||
|             Category = LogCateGoryEnum.Operate, | ||||
|             ExeStatus = true, | ||||
|             OpIp = AppService?.RemoteIpAddress ?? string.Empty, | ||||
|             OpBrowser = userAgent?.Browser, | ||||
|             OpOs = userAgent?.Platform, | ||||
|             OpTime = DateTime.Now, | ||||
|             OpAccount = UserManager.UserAccount, | ||||
|             ReqUrl = null, | ||||
|             ReqMethod = "browser", | ||||
|             ResultJson = resultJson, | ||||
|             ClassName = methodBase.ReflectedType!.Name, | ||||
|             MethodName = methodBase.Name, | ||||
|             ParamJson = paramJson, | ||||
|             VerificatId = UserManager.VerificatId, | ||||
|         }; | ||||
|         return log; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 将日志消息写入队列中等待后台任务出队写入数据库 | ||||
|     /// </summary> | ||||
|     /// <param name="logMsg">结构化日志消息</param> | ||||
|     private static void WriteToQueue(SysOperateLog logMsg) | ||||
|     { | ||||
|         _logMessageQueue.Enqueue(logMsg); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 需要角色授权权限 | ||||
| /// </summary> | ||||
| [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] | ||||
| public sealed class RolePermissionAttribute : Attribute | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
| /// <summary> | ||||
| /// 忽略角色授权权限 | ||||
| /// </summary> | ||||
| [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] | ||||
| public sealed class IgnoreRolePermissionAttribute : Attribute | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
|  | ||||
| [AttributeUsage(AttributeTargets.Method)] | ||||
| public sealed class LoginLogAttribute : Attribute | ||||
| { | ||||
| } | ||||
| [AttributeUsage(AttributeTargets.Method)] | ||||
| public sealed class LogoutLogAttribute : Attribute | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 管理员才能访问 | ||||
| /// </summary> | ||||
| [AttributeUsage(AttributeTargets.Class)] | ||||
| public sealed class SuperAdminAttribute : Attribute | ||||
| { | ||||
| } | ||||
|  | ||||
| /// <summary> | ||||
| /// 忽略超级管理员才能访问特性 | ||||
| /// </summary> | ||||
| [AttributeUsage(AttributeTargets.Method)] | ||||
| public sealed class IgnoreSuperAdminAttribute : Attribute | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,85 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||
| public static class CacheConst | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Token表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_HardwareInfo = $"{CacheConst.Cache_Prefix_Admin}Cache_HardwareInfo:"; | ||||
|  | ||||
|     public const string Cache_Prefix_Admin = "ThingsGatewayAdmin:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 系统字典表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysDict = $"{CacheConst.Cache_Prefix_Admin}SysDict:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 关系表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysRelation = $"{CacheConst.Cache_Prefix_Admin}SysRelation:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 资源表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysResource = $"{CacheConst.Cache_Prefix_Admin}SysResource:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 角色表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysRole = $"{CacheConst.Cache_Prefix_Admin}SysRole:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 用户表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysUser = $"{CacheConst.Cache_Prefix_Admin}SysUser:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 用户账号关系缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysUserAccount = $"{CacheConst.Cache_Prefix_Admin}SysUserAccount:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 职位表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysPosition = $"{CacheConst.Cache_Prefix_Admin}SysPosition:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 机构表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysOrg = $"{CacheConst.Cache_Prefix_Admin}SysOrg:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 公司表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysTenant = $"{CacheConst.Cache_Prefix_Admin}Tenant:"; | ||||
|     /// <summary> | ||||
|     /// 公司表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_SysOrgTenant = $"{CacheConst.Cache_Prefix_Admin}OrgTenant:"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Token表缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_Token = $"{CacheConst.Cache_Prefix_Admin}Token:"; | ||||
|  | ||||
|     #region 登录错误次数 | ||||
|  | ||||
|     /// <summary> | ||||
|     ///  登录错误次数缓存Key | ||||
|     /// </summary> | ||||
|     public const string Cache_LoginErrorCount = $"{CacheConst.Cache_Prefix_Admin}LoginErrorCount:"; | ||||
|  | ||||
|     #endregion 登录错误次数 | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/Admin/ThingsGateway.Admin.Application/Const/HubConst.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,22 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 通讯器常量 | ||||
| /// </summary> | ||||
| public static class HubConst | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 系统HubUrl | ||||
|     /// </summary> | ||||
|     public const string SysHubUrl = "/hubs/thingsgateway"; | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 资源表常量 | ||||
| /// </summary> | ||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||
| public static class ResourceConst | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 系统内置编码 | ||||
|     /// </summary> | ||||
|     public const string System = "System"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 系统管理内置ID 1 | ||||
|     /// </summary> | ||||
|     public const long SystemId = 2; | ||||
| } | ||||
							
								
								
									
										50
									
								
								src/Admin/ThingsGateway.Admin.Application/Const/RoleConst.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,50 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 角色常量 | ||||
| /// </summary> | ||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||
| public static class RoleConst | ||||
| { | ||||
|     /// <summary> | ||||
|     /// api角色 | ||||
|     /// </summary> | ||||
|     public const string ApiRole = "ApiRole"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 业务管理员 | ||||
|     /// </summary> | ||||
|     public const string BizAdmin = "BizAdmin"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 超级管理员 | ||||
|     /// </summary> | ||||
|     public const string SuperAdmin = "SuperAdmin"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 超级管理员Id | ||||
|     /// </summary> | ||||
|     public const long SuperAdminId = 212725263002001; | ||||
|     /// <summary> | ||||
|     /// 默认租户Id | ||||
|     /// </summary> | ||||
|     public const long DefaultTenantId = 252885263003720; | ||||
|     /// <summary> | ||||
|     /// 默认岗位Id | ||||
|     /// </summary> | ||||
|     public const long DefaultPositionId = 212725263003001; | ||||
|     /// <summary> | ||||
|     /// 超级管理员Id | ||||
|     /// </summary> | ||||
|     public const long SuperAdminRoleId = 212725263001001; | ||||
| } | ||||
| @@ -0,0 +1,43 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// SqlSugar系统常量 | ||||
| /// </summary> | ||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||
| public static class SqlSugarConst | ||||
| { | ||||
|     /// <summary> | ||||
|     /// DB_Admin | ||||
|     /// </summary> | ||||
|     public const string DB_Admin = "DB_Admin"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DB_Custom | ||||
|     /// </summary> | ||||
|     public const string DB_Custom = "DB_Custom"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DB_HardwareInfo | ||||
|     /// </summary> | ||||
|     public const string DB_HardwareInfo = "DB_HardwareInfo"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DB_Log | ||||
|     /// </summary> | ||||
|     public const string DB_Log = "DB_Log"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DB_TokenCache | ||||
|     /// </summary> | ||||
|     public const string DB_TokenCache = "DB_TokenCache"; | ||||
| } | ||||
| @@ -0,0 +1,60 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Authentication; | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| [ApiDescriptionSettings(false)] | ||||
| [Route("api/auth")] | ||||
| [RequestAudit] | ||||
| public class AuthController : ControllerBase | ||||
| { | ||||
|     private readonly IAuthService _authService; | ||||
|  | ||||
|     public AuthController(IAuthService authService) | ||||
|     { | ||||
|         _authService = authService; | ||||
|     } | ||||
|  | ||||
|     [HttpPost("login")] | ||||
|     [AllowAnonymous] | ||||
|     [LoginLog] | ||||
|     public Task<LoginOutput> LoginAsync([FromBody] LoginInput input) | ||||
|     { | ||||
|  | ||||
|         return _authService.LoginAsync(input); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     [HttpGet("oauth-login")] | ||||
|     [AllowAnonymous] | ||||
|     [SuppressRequestAudit] | ||||
|     public IActionResult OAuthLogin(string scheme = "Gitee", string returnUrl = "/") | ||||
|     { | ||||
|         var props = new AuthenticationProperties | ||||
|         { | ||||
|             RedirectUri = returnUrl | ||||
|         }; | ||||
|         return Challenge(props, scheme); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     [HttpPost("logout")] | ||||
|     [Authorize] | ||||
|     [IgnoreRolePermission] | ||||
|     [LogoutLog] | ||||
|     public Task LogoutAsync() | ||||
|     { | ||||
|         return _authService.LoginOutAsync(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,72 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.AspNetCore.Localization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
|  | ||||
| using RouteAttribute = Microsoft.AspNetCore.Mvc.RouteAttribute; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 文化 Controller | ||||
| /// </summary> | ||||
| [ApiDescriptionSettings(false)] | ||||
| [Route("[controller]/[action]")] | ||||
| public class CultureController : Controller | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 重置文化方法 | ||||
|     /// </summary> | ||||
|     /// <param name="redirectUri"></param> | ||||
|     /// <returns></returns> | ||||
|     [HttpGet] | ||||
|     public IActionResult ResetCulture(string redirectUri) | ||||
|     { | ||||
|         HttpContext.Response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName); | ||||
|  | ||||
|         return LocalRedirect(redirectUri); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 设置文化方法 | ||||
|     /// </summary> | ||||
|     /// <param name="culture"></param> | ||||
|     /// <param name="redirectUri"></param> | ||||
|     /// <returns></returns> | ||||
|     [HttpGet] | ||||
|     public IActionResult SetCulture(string culture, string redirectUri) | ||||
|     { | ||||
|         if (string.IsNullOrEmpty(culture)) | ||||
|         { | ||||
|             HttpContext.Response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             HttpContext.Response.Cookies.Append( | ||||
|                 CookieRequestCultureProvider.DefaultCookieName, | ||||
|                 CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture, culture)), new CookieOptions() | ||||
|                 { | ||||
|                     Expires = DateTimeOffset.Now.AddYears(1) | ||||
|                 }); | ||||
|  | ||||
|             //更改全局文化,采集后台也会变化 | ||||
|             //var cultureInfo = new CultureInfo(culture); | ||||
|             //CultureInfo.DefaultThreadCurrentCulture = cultureInfo; | ||||
|             //CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; | ||||
|  | ||||
|             //CultureInfo.CurrentCulture = cultureInfo; | ||||
|             //CultureInfo.CurrentUICulture = cultureInfo; | ||||
|         } | ||||
|  | ||||
|         return LocalRedirect(redirectUri); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,50 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 文件下载 | ||||
| /// </summary> | ||||
| [ApiDescriptionSettings(false)] | ||||
| [Route("api/file")] | ||||
| public class FileController : ControllerBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 下载wwwroot文件夹下的文件 | ||||
|     /// </summary> | ||||
|     /// <param name="fileName">相对路径</param> | ||||
|     /// <returns></returns> | ||||
|     [HttpGet("download")] | ||||
|     public IActionResult Download(string fileName) | ||||
|     { | ||||
|         // Validate the fileName to prevent path injection | ||||
|         if (string.IsNullOrEmpty(fileName)) | ||||
|         { | ||||
|             return BadRequest("Invalid file name."); | ||||
|         } | ||||
|  | ||||
|         var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", fileName); | ||||
|  | ||||
|         if (!System.IO.File.Exists(filePath)) | ||||
|         { | ||||
|             return NotFound(); | ||||
|         } | ||||
|  | ||||
|         var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); | ||||
|  | ||||
|         Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition"); | ||||
|  | ||||
|         return File(fileStream, "application/octet-stream", (fileName.Replace('/', '_'))); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,59 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
|  | ||||
| using System.ComponentModel; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
|  | ||||
| /// <summary> | ||||
| /// 登录 | ||||
| /// </summary> | ||||
| [ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)] | ||||
| [Description("登录")] | ||||
| [Route("openapi/auth")] | ||||
| [Authorize(AuthenticationSchemes = "Bearer")] | ||||
| [RequestAudit] | ||||
| [ApiController] | ||||
| public class OpenApiController : ControllerBase | ||||
| { | ||||
|     private readonly IAuthService _authService; | ||||
|  | ||||
|     public OpenApiController(IAuthService authService) | ||||
|     { | ||||
|         _authService = authService; | ||||
|     } | ||||
|  | ||||
|     [HttpPost("login")] | ||||
|     [DisplayName("登录")] | ||||
|     [AllowAnonymous] | ||||
|     public async Task<OpenApiLoginOutput> LoginAsync([FromBody] OpenApiLoginInput input) | ||||
|     { | ||||
|         var output = await _authService.LoginAsync(input.Adapt<LoginInput>(), false).ConfigureAwait(false); | ||||
|  | ||||
|         var openApiLoginOutput = output.Adapt<OpenApiLoginOutput>(); | ||||
|  | ||||
|         return openApiLoginOutput; | ||||
|     } | ||||
|  | ||||
|     [HttpPost("logout")] | ||||
|     [Authorize] | ||||
|     [DisplayName("登出")] | ||||
|     [IgnoreRolePermission] | ||||
|     public Task LogoutAsync() | ||||
|     { | ||||
|         return _authService.LoginOutAsync(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| // 此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| // 源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| // 使用文档:https://thingsgateway.cn/ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| [Route("api/[controller]/[action]")] | ||||
| [AllowAnonymous] | ||||
| [ApiController] | ||||
| public class TestController : ControllerBase | ||||
| { | ||||
|     [HttpGet] | ||||
|     public void Test() | ||||
|     { | ||||
|         GC.Collect(); | ||||
|         GC.WaitForPendingFinalizers(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										59
									
								
								src/Admin/ThingsGateway.Admin.Application/Entity/SysDict.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,59 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.ComponentModel.DataAnnotations; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| [SugarTable("sys_dict", TableDescription = "字典表")] | ||||
| [Tenant(SqlSugarConst.DB_Admin)] | ||||
| public class SysDict : BaseEntity | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 类型 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "类型")] | ||||
|     [AutoGenerateColumn(Ignore = true, Filterable = true, Sortable = true)] | ||||
|     public virtual DictTypeEnum DictType { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 分类 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "分类", Length = 200)] | ||||
|     [Required] | ||||
|     [AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)] | ||||
|     public string Category { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 名称 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "名称", Length = 200)] | ||||
|     [Required] | ||||
|     [AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)] | ||||
|     public virtual string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 代码 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "代码", ColumnDataType = StaticConfig.CodeFirst_BigString)] | ||||
|     [Required] | ||||
|     [AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)] | ||||
|     public virtual string Code { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 描述 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "描述", Length = 200, IsNullable = true)] | ||||
|     public string Remark { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,136 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 操作日志表 | ||||
| ///</summary> | ||||
| [SugarTable("sys_operatelog", TableDescription = "操作日志表")] | ||||
| [Tenant(SqlSugarConst.DB_Log)] | ||||
| public class SysOperateLog | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志分类 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "日志分类")] | ||||
|     [AutoGenerateColumn(Order = 1, Filterable = true, Sortable = true)] | ||||
|     public LogCateGoryEnum Category { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 日志名称 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "日志名称", Length = 200)] | ||||
|     [AutoGenerateColumn(Order = 2, Filterable = true, Sortable = true)] | ||||
|     public string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 类名称 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "类名称", Length = 200)] | ||||
|     [AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)] | ||||
|     public string ClassName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 方法名称 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "方法名称", Length = 200)] | ||||
|     [AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)] | ||||
|     public string MethodName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求参数 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "请求参数", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)] | ||||
|     [AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)] | ||||
|     public string? ParamJson { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求方式 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "请求方式", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)] | ||||
|     public string? ReqMethod { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求地址 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "请求地址", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)] | ||||
|     public string? ReqUrl { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 返回结果 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "返回结果", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)] | ||||
|     [AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)] | ||||
|     public string? ResultJson { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 具体消息 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "具体消息", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)] | ||||
|     [AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)] | ||||
|     public string? ExeMessage { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 执行状态 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "执行状态")] | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true)] | ||||
|     public bool ExeStatus { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 操作账号 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "操作账号", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true)] | ||||
|     public string? OpAccount { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 操作浏览器 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "操作浏览器", Length = 200)] | ||||
|     [AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)] | ||||
|     public string OpBrowser { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 操作ip | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "操作ip", Length = 200)] | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true)] | ||||
|     public string? OpIp { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 操作系统 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "操作系统", Length = 200)] | ||||
|     [AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)] | ||||
|     public string OpOs { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 操作时间 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "操作时间")] | ||||
|     [AutoGenerateColumn(Visible = true, DefaultSort = true, Sortable = true, DefaultSortOrder = SortOrder.Desc)] | ||||
|     public DateTime OpTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 验证Id | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "验证Id")] | ||||
|     [IgnoreExcel] | ||||
|     [AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)] | ||||
|     public long VerificatId { get; set; } | ||||
| } | ||||
							
								
								
									
										81
									
								
								src/Admin/ThingsGateway.Admin.Application/Entity/SysOrg.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,81 @@ | ||||
| // ------------------------------------------------------------------------------ | ||||
| // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| // 此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| // 源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| // 使用文档:https://thingsgateway.cn/ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.ComponentModel.DataAnnotations; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 组织表 | ||||
| ///</summary> | ||||
| [SugarTable("sys_org", TableDescription = "组织表")] | ||||
| [Tenant(SqlSugarConst.DB_Admin)] | ||||
| public class SysOrg : BaseEntity | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 父id | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "ParentId", ColumnDescription = "父id")] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public long ParentId { get; set; } | ||||
|  | ||||
|     [SugarColumn(ColumnName = "ParentIdList", ColumnDescription = "父id列表", IsNullable = true, IsJson = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public List<long> ParentIdList { get; set; } = new List<long>(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 主管ID | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "DirectorId", ColumnDescription = "主管ID", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public long? DirectorId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 名称 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "Name", ColumnDescription = "名称", Length = 200)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     [Required] | ||||
|     public string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 全称 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "Names", ColumnDescription = "全称", Length = 500)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public string Names { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 编码 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "Code", ColumnDescription = "编码", Length = 200)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public string Code { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 分类 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "Category", ColumnDescription = "分类")] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public OrgEnum Category { get; set; } | ||||
|  | ||||
|  | ||||
|     [SugarColumn(ColumnName = "Status", ColumnDescription = "启用")] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public bool Status { get; set; } = true; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,61 @@ | ||||
| // ------------------------------------------------------------------------------ | ||||
| // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| // 此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| // 源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| // 使用文档:https://thingsgateway.cn/ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.ComponentModel.DataAnnotations; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 职位表 | ||||
| ///</summary> | ||||
| [SugarTable("sys_position", TableDescription = "职位表")] | ||||
| [Tenant(SqlSugarConst.DB_Admin)] | ||||
| public class SysPosition : BaseEntity | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 组织id | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "OrgId", ColumnDescription = "组织id")] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     [MinValue(1)] | ||||
|     public virtual long OrgId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 名称 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "Name", ColumnDescription = "名称", Length = 200)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     [Required] | ||||
|     public virtual string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 编码 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "Code", ColumnDescription = "编码", Length = 200)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public string Code { get; set; } | ||||
|  | ||||
|  | ||||
|     [SugarColumn(ColumnName = "Status", ColumnDescription = "启用")] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public bool Status { get; set; } = true; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 分类 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "Category", ColumnDescription = "分类")] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public virtual PositionCategoryEnum Category { get; set; } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,39 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 系统关系表 | ||||
| ///</summary> | ||||
| [SugarTable("sys_relation", TableDescription = "系统关系表")] | ||||
| [Tenant(SqlSugarConst.DB_Admin)] | ||||
| public class SysRelation : PrimaryKeyEntity | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 分类 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "分类")] | ||||
|     public RelationCategoryEnum Category { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 对象ID | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "对象ID")] | ||||
|     public long ObjectId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 目标ID | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "目标ID", IsNullable = true)] | ||||
|     public string? TargetId { get; set; } | ||||
| } | ||||
							
								
								
									
										101
									
								
								src/Admin/ThingsGateway.Admin.Application/Entity/SysResource.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,101 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using Microsoft.AspNetCore.Components.Routing; | ||||
|  | ||||
| using Newtonsoft.Json; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.ComponentModel.DataAnnotations; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 系统资源表 | ||||
| ///</summary> | ||||
| [SugarTable("sys_resource", TableDescription = "系统资源表")] | ||||
| [Tenant(SqlSugarConst.DB_Admin)] | ||||
| public class SysResource : BaseEntity | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 父id | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "父id")] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public virtual long ParentId { get; set; } = 0; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 模块 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "模块")] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public virtual long Module { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 标题 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "标题", Length = 200)] | ||||
|     [Required] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true, Searchable = true)] | ||||
|     public virtual string Title { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 图标 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "图标", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = false, Filterable = false)] | ||||
|     public virtual string? Icon { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 编码 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "编码", Length = 200)] | ||||
|     [Required] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)] | ||||
|     public virtual string Code { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 分类 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "分类")] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public ResourceCategoryEnum Category { get; set; } = ResourceCategoryEnum.Menu; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 目标类型 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "目标类型", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public virtual TargetEnum? Target { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 菜单匹配类型 | ||||
|     /// </summary> | ||||
|     [SugarColumn(ColumnDescription = "菜单匹配类型", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public virtual NavLinkMatch? NavLinkMatch { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 路径 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "路径", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true, Searchable = true)] | ||||
|     public virtual string Href { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 子节点 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public List<SysResource>? Children { get; set; } | ||||
| } | ||||
							
								
								
									
										94
									
								
								src/Admin/ThingsGateway.Admin.Application/Entity/SysRole.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,94 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.ComponentModel.DataAnnotations; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 系统角色表 | ||||
| ///</summary> | ||||
| [SugarTable("sys_role", TableDescription = "系统角色表")] | ||||
| [Tenant(SqlSugarConst.DB_Admin)] | ||||
| public class SysRole : BaseEntity | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 名称 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "名称", Length = 200)] | ||||
|     [Required] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public virtual string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 编码 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "编码", Length = 200)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public string Code { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 分类 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "分类", IsNullable = false)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public virtual RoleCategoryEnum Category { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 组织id | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "OrgId", ColumnDescription = "组织id", IsNullable = false)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public long OrgId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 默认数据范围 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "DefaultDataScope", ColumnDescription = "默认数据范围", IsJson = true, ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = false)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public virtual DefaultDataScope DefaultDataScope { get; set; } = new(); | ||||
|  | ||||
|     public override bool Equals(object? obj) | ||||
|     { | ||||
|         if (obj == null || !(obj is SysRole)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return Id == ((SysRole)obj).Id; | ||||
|     } | ||||
|  | ||||
|     public override int GetHashCode() | ||||
|     { | ||||
|         return Id.GetHashCode(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| /// <summary> | ||||
| /// 默认数据范围 | ||||
| /// </summary> | ||||
| public class DefaultDataScope | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 数据范围 | ||||
|     /// </summary> | ||||
|     public DataScopeEnum ScopeCategory { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 自定义机构范围列表 | ||||
|     /// </summary> | ||||
|     public List<long> ScopeDefineOrgIdList { get; set; } = new List<long>(); | ||||
| } | ||||
							
								
								
									
										253
									
								
								src/Admin/ThingsGateway.Admin.Application/Entity/SysUser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,253 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 系统用户表 | ||||
| ///</summary> | ||||
| [SugarTable("sys_user", TableDescription = "系统用户表")] | ||||
| [Tenant(SqlSugarConst.DB_Admin)] | ||||
| public class SysUser : BaseEntity | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 头像 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "头像", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = false, Filterable = false)] | ||||
|     [AdaptIgnore] | ||||
|     public virtual string? Avatar { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 账号 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "账号", Length = 200)] | ||||
|     [Required] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public virtual string Account { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 密码 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "密码", ColumnDataType = StaticConfig.CodeFirst_BigString)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public string Password { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 状态 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "状态")] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public bool Status { get; set; } = true; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 手机 | ||||
|     /// 这里使用了SM4自动加密解密 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "手机", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public string? Phone { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 邮箱 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "邮箱", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)] | ||||
|     public string? Email { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 上次登录ip | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "上次登录ip", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)] | ||||
|     public string? LastLoginIp { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 上次登录设备 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "上次登录设备", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)] | ||||
|     public string? LastLoginDevice { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 上次登录时间 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "上次登录时间", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)] | ||||
|     public DateTime? LastLoginTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 上次登录地点 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "上次登录地点", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)] | ||||
|     public string LastLoginAddress { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 最新登录ip | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "最新登录ip", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)] | ||||
|     public string? LatestLoginIp { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 最新登录时间 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "最新登录时间", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)] | ||||
|     public DateTime? LatestLoginTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 最新登录设备 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "最新登录设备", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)] | ||||
|     public string? LatestLoginDevice { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 最新登录地点 | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnDescription = "最新登录地点", Length = 200, IsNullable = true)] | ||||
|     [AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)] | ||||
|     public string LatestLoginAddress { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 机构id | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "OrgId", ColumnDescription = "机构id", IsNullable = false)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public virtual long OrgId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 职位id | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "PositionId", ColumnDescription = "职位id", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     [Required] | ||||
|     [NotNull] | ||||
|     public virtual long? PositionId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 主管id | ||||
|     ///</summary> | ||||
|     [SugarColumn(ColumnName = "DirectorId", ColumnDescription = "主管id", IsNullable = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public long? DirectorId { get; set; } | ||||
|  | ||||
|     #region other | ||||
|     /// <summary> | ||||
|     /// 机构信息 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public string OrgName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 机构信息全称 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     public string OrgNames { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 职位信息 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     public string PositionName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 组织和机构ID列表,组织ID从上到下最后是职位 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true, IsJson = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public List<long> OrgAndPosIdList { get; set; } = new List<long>(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 主管信息 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public UserSelectorOutput DirectorInfo { get; set; } | ||||
|  | ||||
|     #endregion | ||||
|  | ||||
|     #region other | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 按钮码集合 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public Dictionary<string, List<string>> ButtonCodeList { get; set; } = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 权限码集合 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public HashSet<string> PermissionCodeList { get; set; } = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 角色ID集合 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public HashSet<long> RoleIdList { get; set; } = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 机构及以下机构ID集合 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public HashSet<long> ScopeOrgChildList { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 模块集合 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public List<SysResource> ModuleList { get; set; } = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 租户Id | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public long? TenantId { get; set; } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 全局用戶 | ||||
|     /// </summary> | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public bool IsGlobal { get; set; } | ||||
|  | ||||
|     #endregion other | ||||
| } | ||||
|  | ||||
| /// <summary> | ||||
| /// 数据范围类 | ||||
| /// </summary> | ||||
| public class DataScope | ||||
| { | ||||
|     /// <summary> | ||||
|     /// API接口 | ||||
|     /// </summary> | ||||
|     public string ApiUrl { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,88 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using ThingsGateway.List; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 会话信息 | ||||
| /// </summary> | ||||
|  | ||||
| [SugarTable("verificatinfo", TableDescription = "验证缓存表")] | ||||
| [Tenant(SqlSugarConst.DB_TokenCache)] | ||||
| public class VerificatInfo : PrimaryIdEntity | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 客户端ID列表 | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     [SugarColumn(ColumnDescription = "客户端ID列表", IsNullable = true, IsJson = true)] | ||||
|     public ConcurrentList<string> ClientIds { get; set; } = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 验证Id | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public long UserId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 验证Id | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true)] | ||||
|     [SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)] | ||||
|     [IgnoreExcel] | ||||
|     public override long Id { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 登录IP | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true, Width = 200)] | ||||
|     public string LoginIp { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 登录时间 | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true, Width = 200)] | ||||
|     public DateTime LoginTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 在线状态 | ||||
|     /// </summary> | ||||
|     [Newtonsoft.Json.JsonIgnore] | ||||
|     [System.Text.Json.Serialization.JsonIgnore] | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true, Width = 120)] | ||||
|     [SugarColumn(IsIgnore = true)] | ||||
|     public bool Online => ClientIds.Count > 0; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 过期时间 | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true)] | ||||
|     public int Expire { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 超时时间 | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true)] | ||||
|     public DateTime VerificatTimeout { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 登录设备 | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(Filterable = true, Sortable = true, Width = 100)] | ||||
|     public string Device { get; set; } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,41 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public enum DataScopeEnum | ||||
| { | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 仅自己 | ||||
|     /// </summary> | ||||
|     SCOPE_SELF, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 所有 | ||||
|     /// </summary> | ||||
|     SCOPE_ALL, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 仅所属组织 | ||||
|     /// </summary> | ||||
|     SCOPE_ORG, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 所属组织及以下 | ||||
|     /// </summary> | ||||
|     SCOPE_ORG_CHILD, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 自定义 | ||||
|     /// </summary> | ||||
|     SCOPE_ORG_DEFINE, | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 字典表类型 | ||||
| /// </summary> | ||||
| public enum DictTypeEnum | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 系统使用 | ||||
|     /// </summary> | ||||
|     System, | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 用户自定义 | ||||
|     /// </summary> | ||||
|     Define, | ||||
| } | ||||
| @@ -0,0 +1,19 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public enum LogCateGoryEnum | ||||
| { | ||||
|     Login, | ||||
|     Logout, | ||||
|     Operate, | ||||
|     Exception | ||||
| } | ||||
							
								
								
									
										17
									
								
								src/Admin/ThingsGateway.Admin.Application/Enum/OrgEnum.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,17 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public enum OrgEnum | ||||
| { | ||||
|     DEPT, | ||||
|     COMPANY, | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public enum PositionCategoryEnum | ||||
| { | ||||
|     HIGH, | ||||
|     MIDDLE, | ||||
|     LOW, | ||||
| } | ||||
| @@ -0,0 +1,25 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public enum RelationCategoryEnum | ||||
| { | ||||
|     UserHasRole, | ||||
|     UserHasResource, | ||||
|     UserHasPermission, | ||||
|     UserHasOpenApiPermission, | ||||
|     UserHasModule, | ||||
|     UserWorkbenchData, | ||||
|     RoleHasResource, | ||||
|     RoleHasPermission, | ||||
|     RoleHasOpenApiPermission, | ||||
|     RoleHasModule, | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public enum ResourceCategoryEnum | ||||
| { | ||||
|     Module, | ||||
|     Menu, | ||||
|     Button, | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public enum RoleCategoryEnum | ||||
| { | ||||
|     Global = 0, | ||||
|     Org = 2, | ||||
| } | ||||
							
								
								
									
										19
									
								
								src/Admin/ThingsGateway.Admin.Application/Enum/TargetEnum.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,19 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public enum TargetEnum | ||||
| { | ||||
|     _self, | ||||
|     _blank, | ||||
|     _parent, | ||||
|     _top | ||||
| } | ||||
| @@ -0,0 +1,55 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Http; | ||||
|  | ||||
| using System.Net; | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace Microsoft.AspNetCore.Builder; | ||||
|  | ||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||
| public static class ApplicationBuilderExtensions | ||||
| { | ||||
|     public static IApplicationBuilder UseBootstrapBlazor(this IApplicationBuilder builder) | ||||
|     { | ||||
|         // 获得客户端 IP 地址 | ||||
|         builder.UseWhen(context => context.Request.Path.StartsWithSegments("/ip.axd"), app => app.Run(async context => | ||||
|         { | ||||
|             var ip = ""; | ||||
|             var headers = context.Request.Headers; | ||||
|             if (headers.TryGetValue("X-Forwarded-For", out var value)) | ||||
|             { | ||||
|                 var ips = new List<string>(); | ||||
|                 foreach (var xf in value) | ||||
|                 { | ||||
|                     if (!string.IsNullOrEmpty(xf)) | ||||
|                     { | ||||
|                         ips.Add(xf); | ||||
|                     } | ||||
|                 } | ||||
|                 ip = string.Join(";", ips); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ip = context.Connection.RemoteIpAddress.ToIPv4String(); | ||||
|             } | ||||
|  | ||||
|             context.Response.Headers.TryAdd("Content-Type", new Microsoft.Extensions.Primitives.StringValues("application/json; charset=utf-8")); | ||||
|             await context.Response.WriteAsync(JsonSerializer.Serialize(new { Id = context.TraceIdentifier, Ip = ip })).ConfigureAwait(false); | ||||
|         })); | ||||
|         return builder; | ||||
|     } | ||||
|     public static string ToIPv4String(this IPAddress? address) | ||||
|     { | ||||
|         var ipv4Address = (address ?? IPAddress.IPv6Loopback).ToString(); | ||||
|         return ipv4Address.StartsWith("::ffff:") ? (address ?? IPAddress.IPv6Loopback).MapToIPv4().ToString() : ipv4Address; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,88 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.StaticFiles; | ||||
| using Microsoft.AspNetCore.WebUtilities; | ||||
| using Microsoft.Extensions.Configuration; | ||||
| using Microsoft.Net.Http.Headers; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| [ThingsGateway.DependencyInjection.SuppressSniffer] | ||||
| public static class CacheExtensions | ||||
| { | ||||
|     private static int? _age; | ||||
|  | ||||
|     private static List<string>? _files; | ||||
|  | ||||
|     public static void ProcessCache(this StaticFileResponseContext context, IConfiguration configuration) | ||||
|     { | ||||
|         if (context.CanCache(configuration, out var age)) | ||||
|         { | ||||
|             context.Context.Response.Headers[HeaderNames.CacheControl] = $"public, max-age={age}"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static bool CanCache(this StaticFileResponseContext context, IConfiguration configuration, out int age) | ||||
|     { | ||||
|         var ret = false; | ||||
|         age = 0; | ||||
|  | ||||
|         var files = configuration.GetFiles(); | ||||
|         if (files.Any(i => context.CanCache(i))) | ||||
|         { | ||||
|             ret = true; | ||||
|             age = configuration.GetAge(); | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     private static bool CanCache(this StaticFileResponseContext context, string file) | ||||
|     { | ||||
|         var ext = Path.GetExtension(context.File.PhysicalPath) ?? ""; | ||||
|         bool ret = file.Equals(ext, StringComparison.OrdinalIgnoreCase); | ||||
|         if (ret && ext.Equals(".js", StringComparison.OrdinalIgnoreCase)) | ||||
|         { | ||||
|             // process javascript file | ||||
|             ret = false; | ||||
|             if (context.Context.Request.QueryString.HasValue) | ||||
|             { | ||||
|                 var paras = QueryHelpers.ParseQuery(context.Context.Request.QueryString.Value); | ||||
|                 ret = paras.ContainsKey("v"); | ||||
|             } | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     private static int GetAge(this IConfiguration configuration) | ||||
|     { | ||||
|         _age ??= GetAge(); | ||||
|         return _age.Value; | ||||
|  | ||||
|         int GetAge() | ||||
|         { | ||||
|             var cacheSection = configuration.GetSection("Cache-Control"); | ||||
|             return cacheSection.GetValue<int>("Max-Age", 1000 * 60 * 10); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static List<string> GetFiles(this IConfiguration configuration) | ||||
|     { | ||||
|         _files ??= GetFiles(); | ||||
|         return _files; | ||||
|  | ||||
|         List<string> GetFiles() | ||||
|         { | ||||
|             var cacheSection = configuration.GetSection("Cache-Control"); | ||||
|             return cacheSection.GetSection("Files").Get<List<string>>() ?? new(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,15 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace System.Logging; | ||||
|  | ||||
| public class RequestAudit | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,10 @@ | ||||
|  | ||||
| using ThingsGateway.DependencyInjection; | ||||
|  | ||||
| namespace System; | ||||
|  | ||||
| [SuppressSniffer, AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)] | ||||
| public sealed class RequestAuditAttribute : Attribute | ||||
| { | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,101 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using System.Reflection; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public class RequestAuditData | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 分类 | ||||
|     /// </summary> | ||||
|     public string CateGory { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 客户端信息 | ||||
|     /// </summary> | ||||
|     public UserAgent Client { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求方法:POST/GET | ||||
|     /// </summary> | ||||
|     public string Method { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 操作名称 | ||||
|     /// </summary> | ||||
|     public string Operation { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求地址 | ||||
|     /// </summary> | ||||
|     public string Path { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 方法名称 | ||||
|     /// </summary> | ||||
|     public string ActionName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 认证信息 | ||||
|     /// </summary> | ||||
|     public List<AuthorizationClaims> AuthorizationClaims { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 控制器名 | ||||
|     /// </summary> | ||||
|     public string ControllerName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 异常信息 | ||||
|     /// </summary> | ||||
|     public LogException Exception { get; set; } | ||||
|  | ||||
|     public long TimeOperationElapsedMilliseconds { get; set; } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 服务端 | ||||
|     /// </summary> | ||||
|     public string LocalIPv4 { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 日志时间 | ||||
|     /// </summary> | ||||
|     public DateTimeOffset LogDateTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 参数列表 | ||||
|     /// </summary> | ||||
|     public List<Parameters> Parameters { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 客户端IPV4地址 | ||||
|     /// </summary> | ||||
|     public string RemoteIPv4 { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求地址 | ||||
|     /// </summary> | ||||
|     public string RequestUrl { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 返回信息 | ||||
|     /// </summary> | ||||
|     public object ReturnInformation { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 验证错误信息 | ||||
|     /// </summary> | ||||
|     public Validation Validation { get; set; } | ||||
|     public MethodInfo MethodInfo { get; set; } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,301 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.Controllers; | ||||
| using Microsoft.AspNetCore.Mvc.Filters; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using System.Diagnostics; | ||||
| using System.Logging; | ||||
|  | ||||
| using ThingsGateway.FriendlyException; | ||||
| using ThingsGateway.Logging; | ||||
| using ThingsGateway.NewLife.Json.Extension; | ||||
| using ThingsGateway.UnifyResult; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter | ||||
| { | ||||
|     private const int FilterOrder = -3000; | ||||
|     public int Order => FilterOrder; | ||||
|  | ||||
|     public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) | ||||
|     { | ||||
|         var timeOperation = Stopwatch.StartNew(); | ||||
|         var resultContext = await next().ConfigureAwait(false); | ||||
|         // 计算接口执行时间 | ||||
|         timeOperation.Stop(); | ||||
|  | ||||
|         var controllerActionDescriptor = (context.ActionDescriptor as ControllerActionDescriptor); | ||||
|         // 获取动作方法描述器 | ||||
|         var actionMethod = controllerActionDescriptor?.MethodInfo; | ||||
|  | ||||
|  | ||||
|         // 处理 Blazor Server | ||||
|         if (actionMethod == null) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 排除 WebSocket 请求处理 | ||||
|         if (context.HttpContext.IsWebSocketRequest()) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // 如果贴了 [SuppressMonitor] 特性则跳过 | ||||
|         if (actionMethod.IsDefined(typeof(SuppressRequestAuditAttribute), true) | ||||
|             || actionMethod.DeclaringType.IsDefined(typeof(SuppressRequestAuditAttribute), true)) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // 只有方法贴有特性才进行审计 | ||||
|         if ( | ||||
|             !actionMethod.DeclaringType.IsDefined(typeof(RequestAuditAttribute), true) | ||||
|             && | ||||
|             !actionMethod.IsDefined(typeof(RequestAuditAttribute), true)) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|         var logData = new RequestAuditData(); | ||||
|  | ||||
|  | ||||
|         logData.TimeOperationElapsedMilliseconds = timeOperation.ElapsedMilliseconds; | ||||
|  | ||||
|         var resultHttpContext = (resultContext as FilterContext).HttpContext; | ||||
|  | ||||
|         // 获取 HttpContext 和 HttpRequest 对象 | ||||
|         var httpContext = context.HttpContext; | ||||
|         var httpRequest = httpContext.Request; | ||||
|  | ||||
|         // 获取客户端 Ipv4 地址 | ||||
|         var remoteIPv4 = httpContext.GetRemoteIpAddressToIPv4(); | ||||
|         logData.RemoteIPv4 = remoteIPv4; | ||||
|         var requestUrl = Uri.UnescapeDataString(httpRequest.GetRequestUrlAddress()); | ||||
|         logData.RequestUrl = requestUrl; | ||||
|  | ||||
|         object returnValue = null; | ||||
|         Type finalReturnType; | ||||
|         var result = resultContext.Result as IActionResult; | ||||
|         // 解析返回值 | ||||
|         if (UnifyContext.CheckVaildResult(result, out var data)) | ||||
|         { | ||||
|             returnValue = data; | ||||
|             finalReturnType = data?.GetType(); | ||||
|         } | ||||
|         // 处理文件类型 | ||||
|         else if (result is FileResult fresult) | ||||
|         { | ||||
|             returnValue = new | ||||
|             { | ||||
|                 FileName = fresult.FileDownloadName, | ||||
|                 fresult.ContentType, | ||||
|                 Length = fresult is FileContentResult cresult ? (object)cresult.FileContents.Length : null | ||||
|             }; | ||||
|             finalReturnType = fresult?.GetType(); | ||||
|         } | ||||
|         else finalReturnType = result?.GetType(); | ||||
|  | ||||
|         logData.ReturnInformation = returnValue; | ||||
|  | ||||
|         //获取客户端信息 | ||||
|         var client = App.GetService<IAppService>().UserAgent; | ||||
|         //操作名称默认是控制器名加方法名,自定义操作名称要在action上加Description特性 | ||||
|         var option = $"{controllerActionDescriptor.ControllerName}/{controllerActionDescriptor.ActionName}"; | ||||
|  | ||||
|         var desc = App.CreateLocalizerByType(controllerActionDescriptor.ControllerTypeInfo.AsType())[actionMethod.Name]; | ||||
|         //获取特性 | ||||
|  | ||||
|         logData.CateGory = desc.Value;//传操作名称 | ||||
|         logData.Operation = desc.Value;//传操作名称 | ||||
|         logData.Client = client; | ||||
|         logData.Path = httpContext.Request.Path.Value;//请求地址 | ||||
|         logData.Method = httpContext.Request.Method;//请求方法 | ||||
|         logData.MethodInfo = actionMethod;//请求方法 | ||||
|  | ||||
|         logData.ControllerName = controllerActionDescriptor.ControllerName; | ||||
|         logData.ActionName = controllerActionDescriptor.ActionName; | ||||
|  | ||||
|         logData.AuthorizationClaims = new(); | ||||
|         // 获取授权用户 | ||||
|         var user = httpContext.User; | ||||
|         foreach (var claim in user.Claims) | ||||
|         { | ||||
|             logData.AuthorizationClaims.Add(new AuthorizationClaims | ||||
|             { | ||||
|                 Type = claim.Type, | ||||
|                 Value = claim.Value, | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         logData.LocalIPv4 = httpContext.GetLocalIpAddressToIPv4(); | ||||
|         logData.LogDateTime = DateTimeOffset.Now; | ||||
|         var parameterValues = context.ActionArguments; | ||||
|  | ||||
|         logData.Parameters = new(); | ||||
|         var parameters = actionMethod.GetParameters(); | ||||
|  | ||||
|         foreach (var parameter in parameters) | ||||
|         { | ||||
|             // 判断是否禁用记录特定参数 | ||||
|             if (parameter.IsDefined(typeof(SuppressRequestAuditAttribute), false)) continue; | ||||
|  | ||||
|             // 排除标记 [FromServices] 的解析 | ||||
|             if (parameter.IsDefined(typeof(FromServicesAttribute), false)) continue; | ||||
|  | ||||
|             var name = parameter.Name; | ||||
|             var parameterType = parameter.ParameterType; | ||||
|  | ||||
|             _ = parameterValues.TryGetValue(name, out var value); | ||||
|  | ||||
|  | ||||
|             var par = new Parameters() | ||||
|             { | ||||
|                 Name = name, | ||||
|             }; | ||||
|             logData.Parameters.Add(par); | ||||
|  | ||||
|             object rawValue = default; | ||||
|  | ||||
|             // 文件类型参数 | ||||
|             if (value is IFormFile || value is List<IFormFile>) | ||||
|             { | ||||
|                 // 单文件 | ||||
|                 if (value is IFormFile formFile) | ||||
|                 { | ||||
|                     var fileSize = Math.Round(formFile.Length / 1024D); | ||||
|                     rawValue = new | ||||
|                     { | ||||
|                         name = formFile.Name, | ||||
|                         fileName = formFile.FileName, | ||||
|                         length = formFile.Length, | ||||
|                         contentType = formFile.ContentType | ||||
|                     }; | ||||
|                 } | ||||
|                 // 多文件 | ||||
|                 else if (value is List<IFormFile> formFiles) | ||||
|                 { | ||||
|                     var rawValues1 = new List<object>(); | ||||
|                     for (var i = 0; i < formFiles.Count; i++) | ||||
|                     { | ||||
|                         var file = formFiles[i]; | ||||
|                         var size = Math.Round(file.Length / 1024D); | ||||
|                         var rawValue1 = new | ||||
|                         { | ||||
|                             name = file.Name, | ||||
|                             fileName = file.FileName, | ||||
|                             length = file.Length, | ||||
|                             contentType = file.ContentType | ||||
|                         }; | ||||
|                         rawValues1.Add(rawValue1); | ||||
|                     } | ||||
|                     rawValue = rawValues1; | ||||
|                 } | ||||
|             } | ||||
|             // 处理 byte[] 参数类型 | ||||
|             else if (value is byte[] byteArray) | ||||
|             { | ||||
|                 rawValue = new | ||||
|                 { | ||||
|                     length = byteArray.Length, | ||||
|                 }; | ||||
|             } | ||||
|             // 处理基元类型,字符串类型和空值 | ||||
|             else if (parameterType.IsPrimitive || value is string || value == null) | ||||
|             { | ||||
|                 rawValue = value; | ||||
|             } | ||||
|             // 其他类型统一进行序列化 | ||||
|             else | ||||
|             { | ||||
|                 rawValue = value; | ||||
|             } | ||||
|  | ||||
|             par.Value = rawValue; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // 获取异常对象情况 | ||||
|         Exception exception = resultContext.Exception; | ||||
|         if (exception is AppFriendlyException friendlyException) | ||||
|         { | ||||
|             logData.Validation = new(); | ||||
|             logData.Validation.Message = friendlyException.Message; | ||||
|         } | ||||
|         else if (exception != null) | ||||
|         { | ||||
|             logData.Exception = new(); | ||||
|             logData.Exception.Message = exception.Message; | ||||
|             logData.Exception.StackTrace = exception.StackTrace; | ||||
|             logData.Exception.Type = HandleGenericType(exception.GetType()); | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|         // 创建日志记录器 | ||||
|         var logger = httpContext.RequestServices.GetRequiredService<ILogger<RequestAudit>>(); | ||||
|  | ||||
|         var logContext = new LogContext(); | ||||
|  | ||||
|         logContext.Set(nameof(RequestAuditData), logData); | ||||
|  | ||||
|         // 设置日志上下文 | ||||
|         using var scope = logger.ScopeContext(logContext); | ||||
|  | ||||
|         if (exception == null) | ||||
|         { | ||||
|             logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}"); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception.ToSystemTextJsonString()}"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 处理泛型类型转字符串打印问题 | ||||
|     /// </summary> | ||||
|     /// <param name="type"></param> | ||||
|     /// <returns></returns> | ||||
|     private static string HandleGenericType(Type type) | ||||
|     { | ||||
|         if (type == null) return string.Empty; | ||||
|  | ||||
|         var typeName = type.FullName ?? (!string.IsNullOrEmpty(type.Namespace) ? type.Namespace + "." : string.Empty) + type.Name; | ||||
|  | ||||
|         // 处理泛型类型问题 | ||||
|         if (type.IsConstructedGenericType) | ||||
|         { | ||||
|             var prefix = type.GetGenericArguments() | ||||
|                 .Select(genericArg => HandleGenericType(genericArg)) | ||||
|                 .Aggregate((previous, current) => previous + ", " + current); | ||||
|  | ||||
|             typeName = typeName.Split('`').First() + "<" + prefix + ">"; | ||||
|         } | ||||
|  | ||||
|         return typeName; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,10 @@ | ||||
|  | ||||
| using ThingsGateway.DependencyInjection; | ||||
|  | ||||
| namespace System; | ||||
|  | ||||
| [SuppressSniffer, AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)] | ||||
| public sealed class SuppressRequestAuditAttribute : Attribute | ||||
| { | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,3 @@ | ||||
| <Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> | ||||
|   <Rougamo /> | ||||
| </Weavers> | ||||
| @@ -0,0 +1,277 @@ | ||||
| using Microsoft.AspNetCore.Authentication; | ||||
| using Microsoft.AspNetCore.Authentication.OAuth; | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.AspNetCore.WebUtilities; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Hosting; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Microsoft.Extensions.Options; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.Net.Http.Headers; | ||||
| using System.Security.Claims; | ||||
| using System.Text; | ||||
| using System.Text.Encodings.Web; | ||||
| using System.Text.Json; | ||||
|  | ||||
| using ThingsGateway.Extension; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 只适合 Demo 登录,会直接授权超管的权限 | ||||
| /// </summary> | ||||
| public class AdminOAuthHandler<TOptions>( | ||||
|    IVerificatInfoService verificatInfoService, | ||||
|    IAppService appService, | ||||
|    ISysUserService sysUserService, | ||||
|    ISysDictService configService, | ||||
|     IOptionsMonitor<TOptions> options, | ||||
|     ILoggerFactory logger, | ||||
|     IUserAgentService userAgentService, | ||||
|     UrlEncoder encoder | ||||
| ) : OAuthHandler<TOptions>(options, logger, encoder) | ||||
|     where TOptions : AdminOAuthOptions, new() | ||||
| { | ||||
|  | ||||
|  | ||||
|     static AdminOAuthHandler() | ||||
|     { | ||||
|         Task.Factory.StartNew(Insertable, TaskCreationOptions.LongRunning); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 日志消息队列(线程安全) | ||||
|     /// </summary> | ||||
|     protected static readonly ConcurrentQueue<SysOperateLog> _operateLogMessageQueue = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 创建访问日志 | ||||
|     /// </summary> | ||||
|     private static async Task Insertable() | ||||
|     { | ||||
|         var db = DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew(); | ||||
|         var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!; | ||||
|         while (!appLifetime.ApplicationStopping.IsCancellationRequested) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var data = _operateLogMessageQueue.ToListWithDequeue(); // 从日志队列中获取数据 | ||||
|                 if (data.Count > 0) | ||||
|                 { | ||||
|                     await db.InsertableWithAttr(data).ExecuteCommandAsync(appLifetime.ApplicationStopping).ConfigureAwait(false);//入库 | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 NewLife.Log.XTrace.WriteException(ex); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 await Task.Delay(3000, appLifetime.ApplicationStopping).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     protected override async Task<AuthenticationTicket> CreateTicketAsync( | ||||
|         ClaimsIdentity identity, | ||||
|         AuthenticationProperties properties, | ||||
|         OAuthTokenResponse tokens) | ||||
|     { | ||||
|         properties.RedirectUri = Options.HomePath; | ||||
|         properties.IsPersistent = true; | ||||
|         var appConfig = await configService.GetAppConfigAsync().ConfigureAwait(false); | ||||
|  | ||||
|         int expire = appConfig.LoginPolicy.VerificatExpireTime; | ||||
|         if (!string.IsNullOrEmpty(tokens.ExpiresIn) && int.TryParse(tokens.ExpiresIn, out var result)) | ||||
|         { | ||||
|             properties.ExpiresUtc = TimeProvider.System.GetUtcNow().AddSeconds(result); | ||||
|             expire = (int)(result / 60.0); | ||||
|         } | ||||
|         var user = await HandleUserInfoAsync(tokens).ConfigureAwait(false); | ||||
|  | ||||
|         var loginEvent = await GetLogin(expire).ConfigureAwait(false); | ||||
|         await UpdateUser(loginEvent).ConfigureAwait(false); | ||||
|         identity.AddClaim(new Claim(ClaimConst.VerificatId, loginEvent.VerificatId.ToString())); | ||||
|         identity.AddClaim(new Claim(ClaimConst.UserId, RoleConst.SuperAdminId.ToString())); | ||||
|  | ||||
|         identity.AddClaim(new Claim(ClaimConst.SuperAdmin, "true")); | ||||
|         identity.AddClaim(new Claim(ClaimConst.OrgId, RoleConst.DefaultTenantId.ToString())); | ||||
|         identity.AddClaim(new Claim(ClaimConst.TenantId, RoleConst.DefaultTenantId.ToString())); | ||||
|  | ||||
|  | ||||
|         var context = new OAuthCreatingTicketContext( | ||||
|             new ClaimsPrincipal(identity), | ||||
|             properties, | ||||
|             Context, | ||||
|             Scheme, | ||||
|             Options, | ||||
|             Backchannel, | ||||
|             tokens, | ||||
|             user | ||||
|         ); | ||||
|  | ||||
|         context.RunClaimActions(); | ||||
|         await Events.CreatingTicket(context).ConfigureAwait(false); | ||||
|  | ||||
|         var httpContext = context.HttpContext; | ||||
|         UserAgent? userAgent = null; | ||||
|         var str = httpContext?.Request?.Headers?.UserAgent; | ||||
|         if (!string.IsNullOrEmpty(str)) | ||||
|         { | ||||
|             userAgent = userAgentService.Parse(str); | ||||
|         } | ||||
|  | ||||
|         var sysOperateLog = new SysOperateLog() | ||||
|         { | ||||
|             Name = this.Scheme.Name, | ||||
|             Category = LogCateGoryEnum.Login, | ||||
|             ExeStatus = true, | ||||
|             OpIp = httpContext.GetRemoteIpAddressToIPv4(), | ||||
|             OpBrowser = userAgent?.Browser, | ||||
|             OpOs = userAgent?.Platform, | ||||
|             OpTime = DateTime.Now, | ||||
|             VerificatId = loginEvent.VerificatId, | ||||
|             OpAccount = Options.GetName(user), | ||||
|  | ||||
|             ReqMethod = "OAuth", | ||||
|             ReqUrl = string.Empty, | ||||
|             ResultJson = string.Empty, | ||||
|             ClassName = nameof(AdminOAuthHandler<TOptions>), | ||||
|             MethodName = string.Empty, | ||||
|             ParamJson = string.Empty, | ||||
|         }; | ||||
|         _operateLogMessageQueue.Enqueue(sysOperateLog); | ||||
|         return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary>处理用户信息方法</summary> | ||||
|     protected virtual async Task<JsonElement> HandleUserInfoAsync(OAuthTokenResponse tokens) | ||||
|     { | ||||
|         var request = new HttpRequestMessage(HttpMethod.Get, BuildUserInfoUrl(tokens)); | ||||
|         request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); | ||||
|  | ||||
|         var response = await Backchannel.SendAsync(request, Context.RequestAborted).ConfigureAwait(false); | ||||
|  | ||||
|         var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); | ||||
|  | ||||
|         if (response.IsSuccessStatusCode) | ||||
|         { | ||||
|             return JsonDocument.Parse(content).RootElement; | ||||
|         } | ||||
|  | ||||
|         throw new OAuthTokenException($"OAuth user info endpoint failure: {await Display(response).ConfigureAwait(false)}"); | ||||
|     } | ||||
|  | ||||
|     /// <summary>生成用户信息请求地址方法</summary> | ||||
|     protected virtual string BuildUserInfoUrl(OAuthTokenResponse tokens) | ||||
|     { | ||||
|         return QueryHelpers.AddQueryString(Options.UserInformationEndpoint, new Dictionary<string, string> | ||||
|         { | ||||
|             { "access_token", tokens.AccessToken } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /// <summary>生成错误信息方法</summary> | ||||
|     protected static async Task<string> Display(HttpResponseMessage response) | ||||
|     { | ||||
|         var output = new StringBuilder(); | ||||
|         output.Append($"Status: {response.StatusCode}; "); | ||||
|         output.Append($"Headers: {response.Headers}; "); | ||||
|         output.Append($"Body: {await response.Content.ReadAsStringAsync().ConfigureAwait(false)};"); | ||||
|  | ||||
|         return output.ToString(); | ||||
|     } | ||||
|  | ||||
|     private async Task<LoginEvent> GetLogin(int expire) | ||||
|     { | ||||
|         var sysUser = await sysUserService.GetUserByIdAsync(RoleConst.SuperAdminId).ConfigureAwait(false);//获取用户信息 | ||||
|  | ||||
|         var loginEvent = new LoginEvent | ||||
|         { | ||||
|             Ip = appService.RemoteIpAddress, | ||||
|             Device = appService.UserAgent?.Platform, | ||||
|             Expire = expire, | ||||
|             SysUser = sysUser, | ||||
|             VerificatId = CommonUtils.GetSingleId() | ||||
|         }; | ||||
|  | ||||
|         //获取verificat列表 | ||||
|         var tokenTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire); | ||||
|         //生成verificat信息 | ||||
|         var verificatInfo = new VerificatInfo | ||||
|         { | ||||
|             Device = loginEvent.Device, | ||||
|             Expire = loginEvent.Expire, | ||||
|             VerificatTimeout = tokenTimeout, | ||||
|             Id = loginEvent.VerificatId, | ||||
|             UserId = loginEvent.SysUser.Id, | ||||
|             LoginIp = loginEvent.Ip, | ||||
|             LoginTime = loginEvent.DateTime | ||||
|         }; | ||||
|  | ||||
|  | ||||
|         //添加到verificat列表 | ||||
|         verificatInfoService.Add(verificatInfo); | ||||
|         return loginEvent; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 登录事件 | ||||
|     /// </summary> | ||||
|     /// <param name="loginEvent"></param> | ||||
|     /// <returns></returns> | ||||
|     private async Task UpdateUser(LoginEvent loginEvent) | ||||
|     { | ||||
|         var sysUser = loginEvent.SysUser; | ||||
|  | ||||
|         #region 登录/密码策略 | ||||
|  | ||||
|         var key = CacheConst.Cache_LoginErrorCount + sysUser.Account;//获取登录错误次数Key值 | ||||
|         App.CacheService.Remove(key);//移除登录错误次数 | ||||
|  | ||||
|         //获取用户verificat列表 | ||||
|         var userToken = verificatInfoService.GetOne(loginEvent.VerificatId); | ||||
|  | ||||
|         #endregion 登录/密码策略 | ||||
|  | ||||
|         #region 重新赋值属性,设置本次登录信息为最新的信息 | ||||
|  | ||||
|         sysUser.LastLoginIp = sysUser.LatestLoginIp; | ||||
|         sysUser.LastLoginTime = sysUser.LatestLoginTime; | ||||
|         sysUser.LatestLoginIp = loginEvent.Ip; | ||||
|         sysUser.LatestLoginTime = loginEvent.DateTime; | ||||
|  | ||||
|         #endregion 重新赋值属性,设置本次登录信息为最新的信息 | ||||
|  | ||||
|         using var db = DbContext.Db.GetConnectionScopeWithAttr<SysUser>().CopyNew(); | ||||
|         //更新用户登录信息 | ||||
|         if (await db.Updateable(sysUser).UpdateColumns(it => new | ||||
|         { | ||||
|             it.LastLoginIp, | ||||
|             it.LastLoginTime, | ||||
|             it.LatestLoginIp, | ||||
|             it.LatestLoginTime, | ||||
|         }).ExecuteCommandAsync().ConfigureAwait(false) > 0) | ||||
|             App.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);//更新Cache信息 | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// <summary>自定义 Token 异常</summary> | ||||
| public class OAuthTokenException : Exception | ||||
| { | ||||
|     public OAuthTokenException() : base() | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     public OAuthTokenException(string? message, Exception? innerException) : base(message, innerException) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     public OAuthTokenException(string? message) : base(message) | ||||
|     { | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| using Microsoft.AspNetCore.Authentication.OAuth; | ||||
|  | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary>OAuthOptions 配置类</summary> | ||||
| public abstract class AdminOAuthOptions : OAuthOptions | ||||
| { | ||||
|     /// <summary>默认构造函数</summary> | ||||
|     protected AdminOAuthOptions() | ||||
|     { | ||||
|         ConfigureClaims(); | ||||
|         this.Events.OnRemoteFailure = context => | ||||
|         { | ||||
|             var redirectUri = string.IsNullOrEmpty(HomePath) ? "/" : HomePath; | ||||
|             context.Response.Redirect(redirectUri); | ||||
|             context.HandleResponse(); | ||||
|             return Task.CompletedTask; | ||||
|         }; | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary>配置 Claims 映射</summary> | ||||
|     protected virtual void ConfigureClaims() | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public virtual string GetName(JsonElement element) | ||||
|     { | ||||
|         JsonElement.ObjectEnumerator target = element.EnumerateObject(); | ||||
|         return target.TryGetValue("name"); | ||||
|     } | ||||
|  | ||||
|     /// <summary>获得/设置 登陆后首页</summary> | ||||
|     public string HomePath { get; set; } = "/"; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,114 @@ | ||||
| using Microsoft.AspNetCore.Authentication; | ||||
| using Microsoft.AspNetCore.Authentication.OAuth; | ||||
| using Microsoft.AspNetCore.WebUtilities; | ||||
|  | ||||
| using System.Net.Http.Headers; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public class GiteeOAuthOptions : AdminOAuthOptions | ||||
| { | ||||
|  | ||||
|     public GiteeOAuthOptions() : base() | ||||
|     { | ||||
|         this.SignInScheme = ClaimConst.Scheme; | ||||
|         this.AuthorizationEndpoint = "https://gitee.com/oauth/authorize"; | ||||
|         this.TokenEndpoint = "https://gitee.com/oauth/token"; | ||||
|         this.UserInformationEndpoint = "https://gitee.com/api/v5/user"; | ||||
|         this.HomePath = "/"; | ||||
|         this.CallbackPath = "/signin-gitee"; | ||||
|         Scope.Add("user_info"); | ||||
|         Scope.Add("projects"); | ||||
|  | ||||
|         Events.OnCreatingTicket = async context => | ||||
|         { | ||||
|             await HandlerGiteeStarredUrl(context).ConfigureAwait(false); | ||||
|         }; | ||||
|  | ||||
|         Events.OnRedirectToAuthorizationEndpoint = context => | ||||
|         { | ||||
|             //context.RedirectUri = context.RedirectUri.Replace("http%3A%2F%2F", "https%3A%2F%2F"); // 强制替换 | ||||
|             context.Response.Redirect(context.RedirectUri); | ||||
|             return Task.CompletedTask; | ||||
|         }; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary>刷新 Token 方法</summary> | ||||
|     protected virtual async Task<OAuthTokenResponse> RefreshTokenAsync(TicketReceivedContext ticketReceivedContext, string refreshToken) | ||||
|     { | ||||
|         var query = new Dictionary<string, string> | ||||
|         { | ||||
|             { "refresh_token", refreshToken }, | ||||
|             { "grant_type", "refresh_token" } | ||||
|         }; | ||||
|  | ||||
|         var request = new HttpRequestMessage(HttpMethod.Post, QueryHelpers.AddQueryString(TokenEndpoint, query)); | ||||
|         request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); | ||||
|  | ||||
|         var response = await Backchannel.SendAsync(request, ticketReceivedContext.HttpContext.RequestAborted).ConfigureAwait(false); | ||||
|  | ||||
|         var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); | ||||
|  | ||||
|         if (response.IsSuccessStatusCode) | ||||
|         { | ||||
|             return OAuthTokenResponse.Success(JsonDocument.Parse(content)); | ||||
|         } | ||||
|  | ||||
|         return OAuthTokenResponse.Failed(new OAuthTokenException($"OAuth token endpoint failure: {await Display(response).ConfigureAwait(false)}")); | ||||
|     } | ||||
|  | ||||
|     /// <summary>生成错误信息方法</summary> | ||||
|     protected static async Task<string> Display(HttpResponseMessage response) | ||||
|     { | ||||
|         var output = new StringBuilder(); | ||||
|         output.Append($"Status: {response.StatusCode}; "); | ||||
|         output.Append($"Headers: {response.Headers}; "); | ||||
|         output.Append($"Body: {await response.Content.ReadAsStringAsync().ConfigureAwait(false)};"); | ||||
|  | ||||
|         return output.ToString(); | ||||
|     } | ||||
|  | ||||
|     public override string GetName(JsonElement element) | ||||
|     { | ||||
|         JsonElement.ObjectEnumerator target = element.EnumerateObject(); | ||||
|         return target.TryGetValue("name"); | ||||
|     } | ||||
|  | ||||
|     private static async Task HandlerGiteeStarredUrl(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway") | ||||
|     { | ||||
|         if (string.IsNullOrWhiteSpace(context.AccessToken)) | ||||
|             throw new InvalidOperationException("Access token is missing."); | ||||
|  | ||||
|         var uri = $"https://gitee.com/api/v5/user/starred/{repoFullName}"; | ||||
|  | ||||
|         var queryString = new Dictionary<string, string> | ||||
|         { | ||||
|             { "access_token", context.AccessToken } | ||||
|         }; | ||||
|  | ||||
|         var request = new HttpRequestMessage(HttpMethod.Put, QueryHelpers.AddQueryString(uri, queryString)) | ||||
|         { | ||||
|             Headers = { Accept = { new MediaTypeWithQualityHeaderValue("application/json") } } | ||||
|         }; | ||||
|  | ||||
|         var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted).ConfigureAwait(false); | ||||
|  | ||||
|         if (!response.IsSuccessStatusCode) | ||||
|         { | ||||
|             var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); | ||||
|             throw new Exception($"Failed to star repository: {response.StatusCode}, {content}"); | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
|     protected override void ConfigureClaims() | ||||
|     { | ||||
|         ClaimActions.MapJsonKey(ClaimConst.AvatarUrl, "avatar_url"); | ||||
|         ClaimActions.MapJsonKey(ClaimConst.Account, "name"); | ||||
|  | ||||
|         base.ConfigureClaims(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public class GiteeOAuthSettings | ||||
| { | ||||
|     public string ClientId { get; set; } | ||||
|     public string ClientSecret { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public class GiteeOAuthUser | ||||
| { | ||||
|     public string Id { get; set; } | ||||
|  | ||||
|     public string Login { get; set; } | ||||
|  | ||||
|     public string Name { get; set; } | ||||
|  | ||||
|     public string Avatar_Url { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,22 @@ | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public static class OAuthUserExtensions | ||||
| { | ||||
|     public static GiteeOAuthUser ToAuthUser(this JsonElement element) | ||||
|     { | ||||
|         GiteeOAuthUser authUser = new GiteeOAuthUser(); | ||||
|         JsonElement.ObjectEnumerator target = element.EnumerateObject(); | ||||
|         authUser.Id = target.TryGetValue("id"); | ||||
|         authUser.Login = target.TryGetValue("login"); | ||||
|         authUser.Name = target.TryGetValue("name"); | ||||
|         authUser.Avatar_Url = target.TryGetValue("avatar_url"); | ||||
|         return authUser; | ||||
|     } | ||||
|  | ||||
|     public static string TryGetValue(this JsonElement.ObjectEnumerator target, string propertyName) | ||||
|     { | ||||
|         return target.FirstOrDefault<JsonProperty>((Func<JsonProperty, bool>)(t => t.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase))).Value.ToString() ?? string.Empty; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/Admin/ThingsGateway.Admin.Application/GlobalUsings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,12 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| global using ThingsGateway; | ||||
| global using ThingsGateway.NewLife.Extension; | ||||
| @@ -0,0 +1,60 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using ThingsGateway.NewLife; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class HardwareInfo | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 当前磁盘信息 | ||||
|     /// </summary> | ||||
|     public DriveInfo DriveInfo { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 硬件信息获取 | ||||
|     /// </summary> | ||||
|     public MachineInfo? MachineInfo { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 主机环境 | ||||
|     /// </summary> | ||||
|     public string Environment { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// NET框架 | ||||
|     /// </summary> | ||||
|     public string FrameworkDescription { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 系统架构 | ||||
|     /// </summary> | ||||
|     public string OsArchitecture { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 唯一编码 | ||||
|     /// </summary> | ||||
|     public string UUID { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 进程占用内存 | ||||
|     /// </summary> | ||||
|     [AutoGenerateColumn(Ignore = true)] | ||||
|     public int WorkingSet { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 更新时间 | ||||
|     /// </summary> | ||||
|     public string UpdateTime { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,161 @@ | ||||
| // ------------------------------------------------------------------------------ | ||||
| // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| // 此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| // 源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| // 使用文档:https://thingsgateway.cn/ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.Extensions.Hosting; | ||||
| using Microsoft.Extensions.Localization; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Microsoft.Extensions.Options; | ||||
|  | ||||
| using System.Runtime.InteropServices; | ||||
|  | ||||
| using ThingsGateway.Extension; | ||||
| using ThingsGateway.NewLife; | ||||
| using ThingsGateway.NewLife.Caching; | ||||
| using ThingsGateway.NewLife.Threading; | ||||
| using ThingsGateway.Schedule; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 获取硬件信息作业任务 | ||||
| /// </summary> | ||||
| [JobDetail("hardware_log", Description = "获取硬件信息", GroupName = "Hardware", Concurrent = false)] | ||||
| [PeriodSeconds(30, TriggerId = "trigger_hardware", Description = "获取硬件信息", RunOnStart = true)] | ||||
| public class HardwareJob : IJob, IHardwareJob | ||||
| { | ||||
|     private readonly ILogger _logger; | ||||
|     private readonly IStringLocalizer _localizer; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public HardwareJob(ILogger<HardwareJob> logger, IStringLocalizer<HardwareJob> localizer, IOptions<HardwareInfoOptions> options) | ||||
|     { | ||||
|         _logger = logger; | ||||
|         _localizer = localizer; | ||||
|         HardwareInfoOptions = options.Value; | ||||
|     } | ||||
|     #region 属性 | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 运行信息获取 | ||||
|     /// </summary> | ||||
|     public HardwareInfo HardwareInfo { get; } = new(); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public HardwareInfoOptions HardwareInfoOptions { get; private set; } | ||||
|  | ||||
|     #endregion 属性 | ||||
|  | ||||
|     private MemoryCache MemoryCache = new() { }; | ||||
|     private const string CacheKey = "HistoryHardwareInfo"; | ||||
|     /// <inheritdoc/> | ||||
|     public async Task<List<HistoryHardwareInfo>> GetHistoryHardwareInfos() | ||||
|     { | ||||
|         var historyHardwareInfos = MemoryCache.Get<List<HistoryHardwareInfo>>(CacheKey); | ||||
|         if (historyHardwareInfos == null) | ||||
|         { | ||||
|             using var db = DbContext.Db.GetConnectionScopeWithAttr<HistoryHardwareInfo>().CopyNew(); | ||||
|             historyHardwareInfos = await db.Queryable<HistoryHardwareInfo>().Where(a => a.Date > DateTime.Now.AddDays(-3)).ToListAsync().ConfigureAwait(false); | ||||
|  | ||||
|             MemoryCache.Set(CacheKey, historyHardwareInfos); | ||||
|         } | ||||
|         return historyHardwareInfos; | ||||
|     } | ||||
|  | ||||
|     private bool error = false; | ||||
|     private DateTime hisInsertTime = default; | ||||
|  | ||||
|     public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken) | ||||
|     { | ||||
|         if (HardwareInfoOptions.Enable) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (HardwareInfo.MachineInfo == null) | ||||
|                 { | ||||
|                     await MachineInfo.RegisterAsync().ConfigureAwait(false); | ||||
|                     HardwareInfo.MachineInfo = MachineInfo.Current; | ||||
|  | ||||
|                     string currentPath = Directory.GetCurrentDirectory(); | ||||
|                     DriveInfo drive = new(Path.GetPathRoot(currentPath)); | ||||
|  | ||||
|                     HardwareInfoOptions.DaysAgo = Math.Min(Math.Max(HardwareInfoOptions.DaysAgo, 1), 7); | ||||
|                     if (HardwareInfoOptions.HistoryInterval < 60000) HardwareInfoOptions.HistoryInterval = 60000; | ||||
|                     HardwareInfo.DriveInfo = drive; | ||||
|                     HardwareInfo.OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(); // 系统架构 | ||||
|                     HardwareInfo.FrameworkDescription = RuntimeInformation.FrameworkDescription; // NET框架 | ||||
|                     HardwareInfo.Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production"; | ||||
|                     HardwareInfo.UUID = HardwareInfo.MachineInfo.UUID; | ||||
|  | ||||
|                     HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat(); | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|             try | ||||
|             { | ||||
|                 HardwareInfo.MachineInfo.Refresh(); | ||||
|                 HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat(); | ||||
|                 HardwareInfo.WorkingSet = (Environment.WorkingSet / 1024.0 / 1024.0).ToInt(); | ||||
|                 error = false; | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 if (!error) | ||||
|                     _logger.LogWarning(ex, _localizer["GetHardwareInfoFail"]); | ||||
|                 error = true; | ||||
|             } | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 if (HardwareInfoOptions.Enable) | ||||
|                 { | ||||
|                     if (DateTime.Now > hisInsertTime.Add(TimeSpan.FromMilliseconds(HardwareInfoOptions.HistoryInterval))) | ||||
|                     { | ||||
|                         hisInsertTime = DateTime.Now; | ||||
|                         using var db = DbContext.Db.GetConnectionScopeWithAttr<HistoryHardwareInfo>().CopyNew(); | ||||
|                         { | ||||
|                             var his = new HistoryHardwareInfo() | ||||
|                             { | ||||
|                                 Date = TimerX.Now, | ||||
|                                 DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToInt(), | ||||
|                                 Battery = (HardwareInfo.MachineInfo.Battery * 100).ToInt(), | ||||
|                                 MemoryUsage = (HardwareInfo.WorkingSet), | ||||
|                                 CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToInt(), | ||||
|                                 Temperature = (HardwareInfo.MachineInfo.Temperature).ToInt(), | ||||
|                             }; | ||||
|                             await db.Insertable(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false); | ||||
|                             MemoryCache.Remove(CacheKey); | ||||
|                         } | ||||
|                         var sevenDaysAgo = TimerX.Now.AddDays(-HardwareInfoOptions.DaysAgo); | ||||
|                         //删除特定信息 | ||||
|                         var result = await db.Deleteable<HistoryHardwareInfo>(a => a.Date <= sevenDaysAgo).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false); | ||||
|                         if (result > 0) | ||||
|                         { | ||||
|                             MemoryCache.Remove(CacheKey); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 error = false; | ||||
|             } | ||||
|             catch (OperationCanceledException) | ||||
|             { | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 if (!error) | ||||
|                     _logger.LogWarning(ex, _localizer["GetHardwareInfoFail"]); | ||||
|                 error = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,43 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| [SugarTable("his_hardwareinfo", TableDescription = "硬件信息历史表")] | ||||
| [Tenant(SqlSugarConst.DB_HardwareInfo)] | ||||
| public class HistoryHardwareInfo | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     [SugarColumn(ColumnDescription = "磁盘使用率")] | ||||
|     public int DriveUsage { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     [SugarColumn(ColumnDescription = "内存")] | ||||
|     public int MemoryUsage { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     [SugarColumn(ColumnDescription = "CPU使用率")] | ||||
|     public int CpuUsage { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     [SugarColumn(ColumnDescription = "温度")] | ||||
|     public int Temperature { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     [SugarColumn(ColumnDescription = "电池")] | ||||
|     public int Battery { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     [SugarColumn(ColumnDescription = "时间")] | ||||
|     public DateTime Date { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| // ------------------------------------------------------------------------------ | ||||
| // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| // 此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| // 源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| // 使用文档:https://thingsgateway.cn/ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| public interface IHardwareJob | ||||
| { | ||||
|     HardwareInfo HardwareInfo { get; } | ||||
|     HardwareInfoOptions HardwareInfoOptions { get; } | ||||
|     Task<List<HistoryHardwareInfo>> GetHistoryHardwareInfos(); | ||||
| } | ||||
| @@ -0,0 +1,76 @@ | ||||
| // ------------------------------------------------------------------------------ | ||||
| // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| // 此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| // 源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| // 使用文档:https://thingsgateway.cn/ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.Schedule; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 作业持久化(数据库) | ||||
| /// </summary> | ||||
| public class JobPersistence : IJobPersistence | ||||
| { | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 作业调度服务启动时 | ||||
|     /// </summary> | ||||
|     /// <param name="stoppingToken"></param> | ||||
|     /// <returns></returns> | ||||
|     /// <exception cref="NotSupportedException"></exception> | ||||
|     public async Task<IEnumerable<SchedulerBuilder>> PreloadAsync(CancellationToken stoppingToken) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|         // 获取所有定义的作业 | ||||
|         var allJobs = App.EffectiveTypes.ScanToBuilders().ToList(); | ||||
|         return allJobs; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 作业计划初始化通知 | ||||
|     /// </summary> | ||||
|     /// <param name="builder"></param> | ||||
|     /// <param name="stoppingToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public Task<SchedulerBuilder> OnLoadingAsync(SchedulerBuilder builder, CancellationToken stoppingToken) | ||||
|     { | ||||
|         return Task.FromResult(builder); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 作业计划Scheduler的JobDetail变化时 | ||||
|     /// </summary> | ||||
|     /// <param name="context"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task OnChangedAsync(PersistenceContext context) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 作业计划Scheduler的触发器Trigger变化时 | ||||
|     /// </summary> | ||||
|     /// <param name="context"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task OnTriggerChangedAsync(PersistenceTriggerContext context) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 作业触发器运行记录 | ||||
|     /// </summary> | ||||
|     /// <param name="context"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task OnExecutionRecordAsync(PersistenceExecutionRecordContext context) | ||||
|     { | ||||
|         await Task.CompletedTask.ConfigureAwait(false); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,34 @@ | ||||
| // ------------------------------------------------------------------------------ | ||||
| // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| // 此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| // 源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| // 使用文档:https://thingsgateway.cn/ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.Schedule; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 清理日志作业任务 | ||||
| /// </summary> | ||||
| [JobDetail("job_log", Description = "清理操作日志", GroupName = "Log", Concurrent = false)] | ||||
| [Daily(TriggerId = "trigger_log", Description = "清理操作日志", RunOnStart = true)] | ||||
| public class LogJob : IJob | ||||
| { | ||||
|     public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken) | ||||
|     { | ||||
|         var daysAgo = App.GetOptions<AdminLogOptions>()?.OperateLogDaysAgo ?? 7; | ||||
|         await LogJob.DeleteSysOperateLog(daysAgo, stoppingToken).ConfigureAwait(false); | ||||
|     } | ||||
|  | ||||
|     private static async Task DeleteSysOperateLog(int daysAgo, CancellationToken stoppingToken) | ||||
|     { | ||||
|         using var db = DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew(); | ||||
|         var time = DateTime.Now.AddDays(-daysAgo); | ||||
|         await db.DeleteableWithAttr<SysOperateLog>().Where(u => u.OpTime < time).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false); // 删除操作日志 | ||||
|     } | ||||
| } | ||||
							
								
								
									
										459
									
								
								src/Admin/ThingsGateway.Admin.Application/Locales/en-US.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,459 @@ | ||||
| { | ||||
|   "ThingsGateway.Admin.Application.BaseDataEntity": { | ||||
|     "CreateOrgId": "CreateOrgId" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.BaseEntity": { | ||||
|     "SortCode": "SortCode", | ||||
|     "CreateTime": "CreateTime", | ||||
|     "CreateUser": "CreateUser", | ||||
|     "UpdateTime": "UpdateTime", | ||||
|     "UpdateUser": "UpdateUser" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.BlazorAuthenticationHandler": { | ||||
|     "UserExpire": "User expired, please login again" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.SysUser": { | ||||
|     "Disable": "Disable", | ||||
|     "Enable": "Enable", | ||||
|     "GrantRole": "GrantRole", | ||||
|     "ExitVerificat": "You have been forcibly logged out", | ||||
|     "PasswordEdited": "Password changed, logged out", | ||||
|  | ||||
|     "Avatar": "Avatar", | ||||
|     "Account": "Account", | ||||
|     "Account.Required": "Account.Required", | ||||
|     "Password": "Password", | ||||
|     "Status": "Status", | ||||
|     "Phone": "Phone", | ||||
|     "Email": "Email", | ||||
|     "LastLoginIp": "LastLoginIp", | ||||
|     "LastLoginDevice": "LastLoginDevice", | ||||
|     "LastLoginTime": "LastLoginTime", | ||||
|     "LastLoginAddress": "LastLoginAddress", | ||||
|     "LatestLoginIp": "LatestLoginIp", | ||||
|     "LatestLoginTime": "LatestLoginTime", | ||||
|     "LatestLoginDevice": "LatestLoginDevice", | ||||
|     "LatestLoginAddress": "LatestLoginAddress", | ||||
|     "OrgNames": "OrgNames", | ||||
|     "PositionName": "PositionName", | ||||
|     "OrgId": "Org", | ||||
|     "PositionId": "Position", | ||||
|     "DirectorId": "Director", | ||||
|  | ||||
|     "CheckSelf": "Prohibit {0} yourself", | ||||
|     "CanotDeleteAdminUser": "Cannot delete built-in super admin user", | ||||
|     "CanotEditAdminUser": "Cannot edit super admin user", | ||||
|     "CanotGrantAdmin": "Cannot assign admins roles", | ||||
|     "EmailDup": "Duplicate email {0} exists", | ||||
|     "AccountDup": "Duplicate account {0} exists", | ||||
|     "CanotDeleteSelf": "Cannot delete yourself", | ||||
|     "EmailError": "Email format error {0}", | ||||
|     "PhoneError": "Phone number format error {0}", | ||||
|     "NoOrg": "The organization does not exist", | ||||
|     "DirectorSelf": "Cannot set oneself as the supervisor", | ||||
|  | ||||
|     "DemoCanotUpdatePassword": "DEMO environment does not allow password modification", | ||||
|     "OldPasswordError": "Incorrect old password", | ||||
|     "ConfirmPasswordDiff": "Passwords entered twice are inconsistent", | ||||
|     "PasswordLengthLess": "Password length cannot be less than {0}", | ||||
|     "PasswordMustNum ": "Password must contain numbers", | ||||
|     "PasswordMustLow": "Password must contain lowercase letters", | ||||
|     "PasswordMustUpp": "Password must contain uppercase letters", | ||||
|     "PasswordMustSpecial": "Password must contain special characters" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysRole": { | ||||
|     "Code": "Code", | ||||
|     "Name": "Name", | ||||
|     "Name.Required": "{0} is required", | ||||
|     "Category": "Category", | ||||
|     "OrgId": "Org", | ||||
|     "Global": "Global", | ||||
|     "Status": "Status", | ||||
|     "CanotDeleteAdmin": "Cannot delete built-in super admin role", | ||||
|     "CanotEditAdmin": "Cannot edit super admin role", | ||||
|     "CanotGrantAdmin": "Cannot assign admins roles", | ||||
|     "NameDup": "Duplicate role name {0}", | ||||
|  | ||||
|     "OrgNotNull": "Organization cannot be null", | ||||
|     "SameOrgNameDup": "Duplicate role name exists: {0}", | ||||
|     "CannotRoleScopeAll": "Organization role cannot select global data scope", | ||||
|     "CodeDup": "Duplicate code exists: {0}" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.RoleCategoryEnum": { | ||||
|     "Global": "Global", | ||||
|     "Org": "Org" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.DataScopeEnum": { | ||||
|     "SCOPE_SELF": "Self", | ||||
|     "SCOPE_ALL": "All", | ||||
|     "SCOPE_ORG": "OnlyOrg", | ||||
|     "SCOPE_ORG_CHILD": "OrgChild", | ||||
|     "SCOPE_ORG_DEFINE": "Define" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.DefaultDataScope": { | ||||
|     "ScopeCategory": "DataScope", | ||||
|     "ScopeDefineOrgIdList": "DefineOrgList" | ||||
|   }, | ||||
|  | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysResource": { | ||||
|     "Title": "Title", | ||||
|     "Module": "Module", | ||||
|     "Title.Required": "{0} is required", | ||||
|     "Href.Required": "{0} is required", | ||||
|     "Icon": "Icon", | ||||
|     "Href": "Path", | ||||
|     "Code": "Code", | ||||
|     "Category": "Category", | ||||
|     "Target": "Target", | ||||
|     "NavLinkMatch": "NavLinkMatch", | ||||
|     "ParentId": "Parent", | ||||
|     "ResourceDup": "Duplicate name {0} exists", | ||||
|     "ResourceParentChoiceSelf": "Parent cannot choose itself", | ||||
|     "ResourceParentNull": "Parent does not exist {0}", | ||||
|     "NotFoundResource": "System exception, menu not found", | ||||
|     "ModuleIdDiff": "Module is inconsistent with parent menu", | ||||
|     "CanotDeleteSystemResource": "Cannot delete system resource {0}", | ||||
|     "ResourceMenuHrefNotNull": "Menu href cannot null" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.SysOrgCopyInput": { | ||||
|     "TargetId": "Target", | ||||
|     "ContainsChild": "ContainsChild", | ||||
|     "ContainsPosition": "ContainsPosition" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysPosition": { | ||||
|     "Category.Required": "{0} is a required field", | ||||
|     "Name.Required": "{0} is a required field", | ||||
|     "Code.Required": "{0} is a required field", | ||||
|     "OrgId.MinValue": "{0} is a required field", | ||||
|     "Category": "Category", | ||||
|     "Name": "Name", | ||||
|     "Code": "Code", | ||||
|     "Status": "Status", | ||||
|     "OrgId": "Organization", | ||||
|     "Remark": "Remarks", | ||||
|     "Dup": "Duplicate position exists with Category {0} and Name {1}", | ||||
|     "CodeDup": "Duplicate code {0} exists", | ||||
|     "NameDup": "Duplicate name {0} exists", | ||||
|     "CanotContainsSelf": "Cannot contain itself", | ||||
|     "TargetNameDup": "Target node has duplicate name {0}", | ||||
|     "ParentChoiceSelf": "Parent cannot be itself", | ||||
|     "ParentNull": "Parent does not exist {0}", | ||||
|     "DeleteUserFirst": "Please remove the users under the position first" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysOrg": { | ||||
|     "Category.Required": "{0} is a required field", | ||||
|     "Name.Required": "{0} is a required field", | ||||
|     "Code.Required": "{0} is a required field", | ||||
|     "Category": "Category", | ||||
|     "Name": "Name", | ||||
|     "Code": "Code", | ||||
|     "Status": "Status", | ||||
|     "ParentId": "ParentOrg", | ||||
|     "Names": "Names", | ||||
|     "Remark": "Remarks", | ||||
|     "DirectorId": "Director", | ||||
|     "Dup": "Duplicate organization exists with Category {0} and Name {1}", | ||||
|     "CodeDup": "Duplicate code {0} exists", | ||||
|     "NameDup": "Duplicate name {0} exists", | ||||
|     "CanotContainsSelf": "Cannot contain itself", | ||||
|     "TargetNameDup": "Target node has duplicate name {0}", | ||||
|     "ParentChoiceSelf": "Parent cannot be itself", | ||||
|     "ParentNull": "Parent does not exist {0}", | ||||
|     "DeleteUserFirst": "Please remove the users under the organization first", | ||||
|     "DeleteRoleFirst": "Please remove the roles under the organization first", | ||||
|     "DeletePositionFirst": "Please remove the positions under the organization first", | ||||
|     "RootOrg": "Unable to create top-level organization" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.OrgEnum": { | ||||
|     "COMPANY": "Company", | ||||
|     "DEPT": "Dept" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.PositionCategoryEnum": { | ||||
|     "HIGH": "High", | ||||
|     "MIDDLE": "Middle", | ||||
|     "LOW": "Low" | ||||
|   }, | ||||
|   //controller | ||||
|   "ThingsGateway.Admin.Application.AuthController": { | ||||
|     //auth | ||||
|     "AuthController": "Login API", | ||||
|     "LoginAsync": "Login", | ||||
|     "LogoutAsync": "Logout" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.TestController": { | ||||
|     //auth | ||||
|     "TestController": "Test API", | ||||
|     "Test": "Test" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.OpenApiAuthController": { | ||||
|     //auth | ||||
|     "OpenApiAuthController": "Login API", | ||||
|     "LoginAsync": "Login", | ||||
|     "LogoutAsync": "Logout" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.FileService": { | ||||
|     "FileNullError": "File cannot be empty", | ||||
|     "FileLengthError": "File size cannot exceed {0} M", | ||||
|     "FileTypeError": "Not supported format {0}" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.UnifyResultProvider": { | ||||
|     "TokenOver": "Login has expired, please login again", | ||||
|     "NoPermission": "Access denied, no permission" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.AuthService": { | ||||
|  | ||||
|     "TenantNull": "The tenant does not exist", | ||||
|     "OrgDisable": "The affiliated company/department has been deactivated, please contact the administrator", | ||||
|  | ||||
|     "SingleLoginWarn": "Your account is logged in elsewhere", | ||||
|     "UserNull": "User {0} does not exist", | ||||
|     "MustDesc": "Password needs to be encrypted with DESC before passing", | ||||
|     "PasswordError": "Too many password errors, please try again in {0} minutes", | ||||
|     "UserDisable": "Account {0} has been disabled", | ||||
|     "UserNoModule": "This account has not been assigned a module. Please contact the administrator", | ||||
|     "AuthErrorMax": "Account password error, will be locked for {1} minutes after exceeding {0} times, error count {2}" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.HardwareInfo": { | ||||
|     "Environment": "HostEnvironment", | ||||
|     "FrameworkDescription": ".NETFramework", | ||||
|     "OsArchitecture": "System Architecture", | ||||
|     "UUID": "UUID", | ||||
|     "UpdateTime": "UpdateTime" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.HistoryHardwareInfo": { | ||||
|     "DriveUsage": "Disk Usage", | ||||
|     "MemoryUsage": "Memory", | ||||
|     "CpuUsage": "CPU Usage", | ||||
|     "Temperature": "Temperature", | ||||
|     "Battery": "Battery" | ||||
|   }, | ||||
|  | ||||
|  | ||||
|   //oper | ||||
|   "ThingsGateway.Admin.Application.OperDescAttribute": { | ||||
|     //dict | ||||
|     "SaveDict": "Modify dictionary", | ||||
|     "DeleteDict": "Delete dictionary", | ||||
|     "EditLoginPolicy": "Modify login policy", | ||||
|     "EditPasswordPolicy": "Modify password policy", | ||||
|     "EditPagePolicy": "Modify page policy", | ||||
|     "EditWebsitePolicy": "Modify website settings", | ||||
|     //operlog | ||||
|     "DeleteOperLog": "Delete operation log", | ||||
|     "ExportOperLog": "Export operation log", | ||||
|  | ||||
|     //resource | ||||
|     "SaveResource": "Modify resource", | ||||
|     "DeleteResource": "Delete resource", | ||||
|  | ||||
|     //role | ||||
|     "SaveRole": "Modify role", | ||||
|     "DeleteRole": "Delete role", | ||||
|     "RoleGrantResource": "Role grant resource", | ||||
|     "RoleGrantUser": "Role grant user", | ||||
|     "RoleGrantApiPermission": "Role grant OpenApi", | ||||
|     "GrantApi": "GrantApi", | ||||
|     "GrantUser": "GrantUser", | ||||
|     "GrantRole": "GrantRole", | ||||
|     "GrantResource": "GrantResource", | ||||
|     //user | ||||
|     "SaveUser": "Modify user", | ||||
|     "DeleteuSER": "Delete user", | ||||
|     "ResetPassword": "Reset pw", | ||||
|     "UserGrantRole": "User grant role", | ||||
|     "UserGrantResource": "User grant resource", | ||||
|     "UserGrantApiPermission": "User grant OpenApi", | ||||
|  | ||||
|     //usercenter | ||||
|     "UpdateUserInfo": "Update personal information", | ||||
|     "WorkbenchInfo": "Update personal workbench", | ||||
|     "UpdatePassword": "Update personal password", | ||||
|  | ||||
|     //session | ||||
|     "ExitVerificat": "Force token off", | ||||
|     "ExitSession": "Force session off", | ||||
|  | ||||
|  | ||||
|     "CopyOrg": "Copy Organization", | ||||
|     "DeleteOrg": "Delete Organization", | ||||
|     "SaveOrg": "Save Organization", | ||||
|  | ||||
|     "DeletePosition": "Delete Position", | ||||
|     "SavePosition": "Save Position", | ||||
|  | ||||
|     "NoPermission": "No Permission", | ||||
|  | ||||
|  | ||||
|     "CopyResource": "CopyResource", | ||||
|     "ChangeParentResource": "ChangeParentResource" | ||||
|   }, | ||||
|  | ||||
|  | ||||
|   //service | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.HardwareJob": { | ||||
|     "GetHardwareInfoFail": "Get Hardwareinfo Fail" | ||||
|   }, | ||||
|  | ||||
|   //dto | ||||
|   "ThingsGateway.Admin.Application.UserSelectorOutput": { | ||||
|     "Account": "Account", | ||||
|     "OrgId": "Org" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.ResourceTableSearchModel": { | ||||
|     "Module": "Module", | ||||
|     "Href": "Path", | ||||
|     "Title": "Title" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.WorkbenchInfo": { | ||||
|     "Razor": "Homepage", | ||||
|     "Shortcuts": "Shortcuts" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.UpdatePasswordInput": { | ||||
|     "Password": "Password", | ||||
|     "NewPassword": "New password", | ||||
|     "ConfirmPassword": "Confirm password", | ||||
|     "Password.Required": "{0} is required", | ||||
|     "NewPassword.Required": "{0} is required", | ||||
|     "ConfirmPassword.Required": "{0} is required" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.VerificatInfo": { | ||||
|     "Expire": "Expire(min)", | ||||
|     "Online": "Online", | ||||
|     "VerificatTimeout": "VerificatTimeout", | ||||
|     "Device": "Device", | ||||
|     "LoginIp": "LoginIp", | ||||
|     "LoginTime": "LoginTime" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SessionOutput": { | ||||
|     "Account": "Account", | ||||
|     "Online": "Online status", | ||||
|     "LatestLoginIp": "Latest login IP", | ||||
|     "LatestLoginTime": "Latest login time", | ||||
|     "VerificatCount": "Token count" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysDict": { | ||||
|     "Category.Required": "{0} is required", | ||||
|     "Name.Required": "{0} is required", | ||||
|     "Code.Required": "{0} is required", | ||||
|     "Category": "Category", | ||||
|     "Name": "Name", | ||||
|     "Code": "Code", | ||||
|     "Remark": "Remark", | ||||
|     "DemoCanotUpdateWebsitePolicy": "DEMO environment does not allow modifying website settings", | ||||
|     "DictDup": "Duplicate configuration exists, category {0}, name {1}" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysOperateLog": { | ||||
|     "ClassName": "ClassName", | ||||
|     "ExeMessage": "ExeMessage", | ||||
|     "MethodName": "MethodName", | ||||
|     "ParamJson": "ParamJson", | ||||
|     "ReqMethod": "RequestMethod", | ||||
|     "ReqUrl": "RequestUrl", | ||||
|     "ResultJson": "ResultJson", | ||||
|     "Category": "Category", | ||||
|     "ExeStatus": "ExeStatus", | ||||
|     "Name": "Name", | ||||
|     "OpAccount": "OpAccount", | ||||
|     "OpBrowser": "OpBrowser", | ||||
|     "OpIp": "OpIp", | ||||
|     "OpOs": "OpOs", | ||||
|     "OpTime": "OpTime", | ||||
|     "VerificatId": "VerificatId" | ||||
|  | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.OperateLogPageInput": { | ||||
|     "SearchDate": "SearchDate", | ||||
|     "Account": "Account", | ||||
|     "Category": "Category" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.LoginInput": { | ||||
|     "Account": "Account", | ||||
|     "Password": "Password", | ||||
|     "Account.Required": "{0} is required", | ||||
|     "Password.Required": "{0} is required" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.LogoutInput": { | ||||
|     "VerificatId.Required": "{0} is required" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.AppConfig": { | ||||
|     "LoginPolicy": "LoginPolicy", | ||||
|     "PasswordPolicy": "PasswordPolicy", | ||||
|     "PagePolicy": "PagePolicy", | ||||
|     "WebsitePolicy": "WebsitePolicy" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.LoginPolicy": { | ||||
|     "SingleOpen": "Single user login switch", | ||||
|     "ErrorLockTime": "Login error lock duration (min)", | ||||
|     "ErrorResetTime": "Login error count expiration duration (min)", | ||||
|     "ErrorCount": "Login error count lock threshold", | ||||
|     "VerificatExpireTime": "Login expiration time (min)", | ||||
|     "ErrorLockTime.MinValue": "{0} value is too small", | ||||
|     "ErrorResetTime.MinValue": "{0} value is too small", | ||||
|     "ErrorCount.MinValue": "{0} value is too small", | ||||
|     "VerificatExpireTime.MinValue": "{0} value is too small" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.PagePolicy": { | ||||
|     "Shortcuts": "Default shortcuts", | ||||
|     "Razor": "Default homepage" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.PasswordPolicy": { | ||||
|     "DefaultPassword": "Default user password", | ||||
|     "DefaultPassword.Required": "{0} is required", | ||||
|     "PasswordMinLen": "Minimum password length", | ||||
|     "PasswordMinLen.MinValue": "{0} value is too small", | ||||
|     "PasswordContainNum": "Contain numbers", | ||||
|     "PasswordContainLower": "Contain lowercase letters", | ||||
|     "PasswordContainUpper": "Contain uppercase letters", | ||||
|     "PasswordContainChar": "Contain special characters" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.WebsitePolicy": { | ||||
|     "WebStatus": "WebStatus", | ||||
|     "CloseTip": "CloseTip", | ||||
|     "CloseTip.Required": "{0} is required" | ||||
|   }, | ||||
|   //enum | ||||
|   "ThingsGateway.Admin.Application.ResourceCategoryEnum": { | ||||
|     "Module": "Module", | ||||
|     "Menu": "Menu", | ||||
|     "Button": "Button" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.TargetEnum": { | ||||
|     "_self": "Current window", | ||||
|     "_blank": "New window", | ||||
|     "_parent": "Parent window", | ||||
|     "_top": "Top window" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.DictTypeEnum": { | ||||
|     "System": "System", | ||||
|     "Define": "Business" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.LogCateGoryEnum": { | ||||
|     "Login": "Login", | ||||
|     "Logout": "Logout", | ||||
|     "Operate": "Operation", | ||||
|     "Exception": "Exception" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.LogEnum": { | ||||
|     "SUCCESS": "Success", | ||||
|     "FAIL": "Fail" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										462
									
								
								src/Admin/ThingsGateway.Admin.Application/Locales/zh-CN.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,462 @@ | ||||
| { | ||||
|   "ThingsGateway.Admin.Application.BaseDataEntity": { | ||||
|     "CreateOrgId": "创建机构Id" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.BaseEntity": { | ||||
|     "SortCode": "排序", | ||||
|     "CreateTime": "创建时间", | ||||
|     "CreateUser": "创建人", | ||||
|     "UpdateTime": "更新时间", | ||||
|     "UpdateUser": "更新人" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.BlazorAuthenticationHandler": { | ||||
|     "UserExpire": "用户登录已过期,请重新登录" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysUser": { | ||||
|     "Disable": "禁用", | ||||
|     "Enable": "启用", | ||||
|     "GrantRole": "分配角色", | ||||
|     "ExitVerificat": "您已被强制下线", | ||||
|     "PasswordEdited": "密码被修改,已退出登录", | ||||
|     "Avatar": "头像", | ||||
|     "Account": "账号", | ||||
|     "Account.Required": " {0} 是必填项", | ||||
|     "Password": "密码", | ||||
|     "Status": "状态", | ||||
|     "Phone": "手机", | ||||
|     "Email": "邮箱", | ||||
|     "LastLoginIp": "上次登录ip", | ||||
|     "LastLoginDevice": "上次登录设备", | ||||
|     "LastLoginTime": "上次登录时间", | ||||
|     "LastLoginAddress": "上次登录地点", | ||||
|     "LatestLoginIp": "最新登录ip", | ||||
|     "LatestLoginTime": "最新登录时间", | ||||
|     "LatestLoginDevice": "最新登录设备", | ||||
|     "LatestLoginAddress": "最新登录地点", | ||||
|     "OrgNames": "机构名称", | ||||
|     "PositionName": "职位名称", | ||||
|     "OrgId": "机构", | ||||
|     "PositionId": "职位", | ||||
|     "DirectorId": "主管", | ||||
|     "CheckSelf": "禁止 {0} 自己", | ||||
|     "CanotDeleteAdminUser": "不可删除系统内置超管用户", | ||||
|     "CanotEditAdminUser": "不可编辑超管用户", | ||||
|     "CanotGrantAdmin": "不能分配超管角色", | ||||
|     "EmailDup": "存在重复的邮箱 {0}", | ||||
|     "AccountDup": "存在重复的账号 {0}", | ||||
|     "CanotDeleteSelf": "不可删除自己", | ||||
|     "EmailError": "邮箱 {0} 格式错误", | ||||
|     "PhoneError": "手机号码 {0} 格式错误", | ||||
|     "NoOrg": "组织机构不存在", | ||||
|     "DirectorSelf": "不能设置自己为主管", | ||||
|  | ||||
|  | ||||
|     "DemoCanotUpdatePassword": "DEMO环境不允许修改密码", | ||||
|     "OldPasswordError": "原密码错误", | ||||
|     "ConfirmPasswordDiff": "两次输入的密码不一致", | ||||
|     "PasswordLengthLess": "密码长度不能小于 {0} ", | ||||
|     "PasswordMustNum ": "密码必须包含数字", | ||||
|     "PasswordMustLow": "密码必须包含小写字母", | ||||
|     "PasswordMustUpp": "密码必须包含大写字母", | ||||
|     "PasswordMustSpecial": "密码必须包含特殊字符" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysRole": { | ||||
|     "Code": "编码", | ||||
|     "Name": "名称", | ||||
|     "Name.Required": " {0} 是必填项", | ||||
|     "Category": "分类", | ||||
|     "Global": "全局", | ||||
|     "Status": "状态", | ||||
|     "OrgId": "机构", | ||||
|  | ||||
|     "CanotDeleteAdmin": "不可删除系统内置超管角色", | ||||
|     "CanotEditAdmin": "不可编辑超管角色", | ||||
|     "CanotGrantAdmin": "不能分配超管角色", | ||||
|  | ||||
|     "NameDup": "存在重复的角色名称 {0}", | ||||
|     "OrgNotNull": "机构不能为空", | ||||
|     "SameOrgNameDup": "存在重复的角色名称 {0}", | ||||
|     "CannotRoleScopeAll": "机构角色不能选择全局数据范围", | ||||
|     "CodeDup": "存在重复的编码 {0}" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.RoleCategoryEnum": { | ||||
|     "Global": "全局", | ||||
|     "Org": "机构" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.DataScopeEnum": { | ||||
|     "SCOPE_SELF": "仅自己", | ||||
|     "SCOPE_ALL": "全部", | ||||
|     "SCOPE_ORG": "仅所属组织", | ||||
|     "SCOPE_ORG_CHILD": "所属组织及以下", | ||||
|     "SCOPE_ORG_DEFINE": "自定义" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.DefaultDataScope": { | ||||
|     "ScopeCategory": "数据范围", | ||||
|     "ScopeDefineOrgIdList": "自定义列表" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.SysResource": { | ||||
|     "Title": "标题", | ||||
|     "Module": "模块", | ||||
|     "Title.Required": "{0} 是必填项", | ||||
|     "Href.Required": "{0} 是必填项", | ||||
|     "Icon": "图标", | ||||
|     "Href": "路径", | ||||
|     "Code": "编码", | ||||
|     "Category": "分类", | ||||
|     "Target": "跳转类型", | ||||
|     "NavLinkMatch": "匹配类型", | ||||
|     "ParentId": "上级菜单", | ||||
|     "ResourceDup": "存在重复的名称 {0}", | ||||
|     "ResourceParentChoiceSelf": "父级不能选择自己", | ||||
|     "ResourceParentNull": "父级不存在 {0}", | ||||
|     "NotFoundResource": "系统异常,没找到该菜单", | ||||
|     "ModuleIdDiff": "模块与上级菜单不一致", | ||||
|     "CanotDeleteSystemResource": "不可删除系统资源 {0}", | ||||
|     "ResourceMenuHrefNotNull": "菜单的路径不能为空" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysOrgCopyInput": { | ||||
|     "TargetId": "目标机构", | ||||
|     "ContainsChild": "包含下级", | ||||
|     "ContainsPosition": "包含职位" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.SysPosition": { | ||||
|     "Category.Required": "{0} 是必填项", | ||||
|     "Name.Required": "{0} 是必填项", | ||||
|     "Code.Required": "{0} 是必填项", | ||||
|     "OrgId.MinValue": "{0} 是必填项", | ||||
|     "Category": "分类", | ||||
|     "Name": "名称", | ||||
|     "Code": "代码", | ||||
|     "Status": "状态", | ||||
|     "OrgId": "机构", | ||||
|     "Remark": "备注", | ||||
|     "Dup": "存在重复的岗位 分类 {0} 名称 {1}", | ||||
|     "CodeDup": "存在重复的编码 {0}", | ||||
|     "NameDup": "存在重复的名称 {0}", | ||||
|     "CanotContainsSelf": "不可包含自己", | ||||
|     "TargetNameDup": "目标节点存在重复的名称 {0}", | ||||
|     "ParentChoiceSelf": "父级不能选择自己", | ||||
|     "ParentNull": "父级不存在 {0}", | ||||
|     "DeleteUserFirst": "请先删除职位下的用户" | ||||
|  | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysOrg": { | ||||
|     "Category.Required": "{0} 是必填项", | ||||
|     "Name.Required": "{0} 是必填项", | ||||
|     "Code.Required": "{0} 是必填项", | ||||
|     "Category": "分类", | ||||
|     "Name": "名称", | ||||
|     "Code": "代码", | ||||
|     "Status": "状态", | ||||
|     "ParentId": "上级机构", | ||||
|     "Names": "机构全称", | ||||
|     "Remark": "备注", | ||||
|     "DirectorId": "主管", | ||||
|     "Dup": "存在重复的机构 分类 {0} 名称 {1}", | ||||
|     "CodeDup": "存在重复的编码 {0}", | ||||
|     "NameDup": "存在重复的名称 {0}", | ||||
|     "CanotContainsSelf": "不可包含自己", | ||||
|     "TargetNameDup": "目标节点存在重复的名称 {0}", | ||||
|     "ParentChoiceSelf": "父级不能选择自己", | ||||
|     "ParentNull": "父级不存在 {0}", | ||||
|     "DeleteUserFirst": "请先删除机构下的用户", | ||||
|     "DeleteRoleFirst": "请先删除机构下的角色", | ||||
|     "DeletePositionFirst": "请先删除机构下的职位", | ||||
|     "RootOrg": "无法创建顶层机构" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.OrgEnum": { | ||||
|     "COMPANY": "公司", | ||||
|     "DEPT": "部门" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.PositionCategoryEnum": { | ||||
|     "HIGH": "高层", | ||||
|     "MIDDLE": "中层", | ||||
|     "LOW": "低层" | ||||
|   }, | ||||
|  | ||||
|   //controller | ||||
|   "ThingsGateway.Admin.Application.AuthController": { | ||||
|     //auth | ||||
|     "AuthController": "登录API", | ||||
|     "LoginAsync": "登录", | ||||
|     "LogoutAsync": "注销" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.TestController": { | ||||
|     //auth | ||||
|     "TestController": "测试API", | ||||
|     "Test": "测试" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.OpenApiAuthController": { | ||||
|     //auth | ||||
|     "OpenApiAuthController": "登录API", | ||||
|     "LoginAsync": "登录", | ||||
|     "LogoutAsync": "注销" | ||||
|   }, | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.FileService": { | ||||
|     "FileNullError": "文件不能为空", | ||||
|     "FileLengthError": "文件大小不允许超过 {0} M", | ||||
|     "FileTypeError": "不支持 {0} 格式" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.UnifyResultProvider": { | ||||
|     "TokenOver": "登录已过期,请重新登录", | ||||
|     "NoPermission": "禁止访问,没有权限" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.AuthService": { | ||||
|     "TenantNull": "租户不存在", | ||||
|     "OrgDisable": "所属公司/部门已停用,请联系管理员", | ||||
|     "SingleLoginWarn": "您的账号已在别处登录", | ||||
|     "UserNull": "用户 {0} 不存在", | ||||
|     "PasswordError": "密码错误次数过多,请 {0} 分钟后再试", | ||||
|     "AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ", | ||||
|     "UserDisable": "账号 {0} 已停用", | ||||
|     "MustDesc": "密码需要DESC加密后传入", | ||||
|     "UserNoModule": "该账号未分配模块,请联系管理员" | ||||
|   }, | ||||
|  | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.HardwareInfo": { | ||||
|     "Environment": "主机环境", | ||||
|     "FrameworkDescription": "NET框架", | ||||
|     "OsArchitecture": "系统架构", | ||||
|     "UUID": "唯一编码", | ||||
|     "UpdateTime": "更新时间" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.HistoryHardwareInfo": { | ||||
|     "DriveUsage": "磁盘使用率", | ||||
|     "MemoryUsage": "内存", | ||||
|     "CpuUsage": "CPU使用率", | ||||
|     "Temperature": "温度", | ||||
|     "Battery": "电池" | ||||
|   }, | ||||
|  | ||||
|   //oper | ||||
|   "ThingsGateway.Admin.Application.OperDescAttribute": { | ||||
|     //dict | ||||
|     "SaveDict": "修改字典", | ||||
|     "DeleteDict": "删除字典", | ||||
|     "EditLoginPolicy": "修改登录策略", | ||||
|     "EditPasswordPolicy": "修改密码策略", | ||||
|     "EditPagePolicy": "修改页面策略", | ||||
|     "EditWebsitePolicy": "修改网站设置", | ||||
|     //operlog | ||||
|     "DeleteOperLog": "删除操作日志", | ||||
|     "ExportOperLog": "导出操作日志", | ||||
|  | ||||
|     //resource | ||||
|     "SaveResource": "修改资源", | ||||
|     "DeleteResource": "删除资源", | ||||
|  | ||||
|     //role | ||||
|     "SaveRole": "修改角色", | ||||
|     "DeleteRole": "删除角色", | ||||
|     "RoleGrantResource": "角色授权资源", | ||||
|     "RoleGrantUser": "角色授权用户", | ||||
|     "RoleGrantApiPermission": "角色授权OpenApi", | ||||
|     "GrantApi": "API", | ||||
|     "GrantUser": "用户", | ||||
|     "GrantRole": "角色", | ||||
|     "GrantResource": "资源", | ||||
|     //user | ||||
|     "SaveUser": "修改用户", | ||||
|     "DeleteuSER": "删除用户", | ||||
|     "ResetPassword": "重置密码", | ||||
|     "UserGrantRole": "用户授权角色", | ||||
|     "UserGrantResource": "用户授权资源", | ||||
|     "UserGrantApiPermission": "用户授权OpenApi", | ||||
|  | ||||
|     //usercenter | ||||
|     "UpdateUserInfo": "更新个人信息", | ||||
|     "WorkbenchInfo": "更新个人工作台", | ||||
|     "UpdatePassword": "更新个人密码", | ||||
|  | ||||
|     //session | ||||
|     "ExitVerificat": "强退令牌", | ||||
|     "ExitSession": "强退会话", | ||||
|  | ||||
|  | ||||
|     "CopyOrg": "复制机构", | ||||
|     "DeleteOrg": "删除机构", | ||||
|     "SaveOrg": "保存机构", | ||||
|  | ||||
|     "DeletePosition": "删除岗位", | ||||
|     "SavePosition": "保存岗位", | ||||
|  | ||||
|     "NoPermission": "无权限操作", | ||||
|  | ||||
|     "CopyResource": "复制资源", | ||||
|     "ChangeParentResource": "更改父节点" | ||||
|   }, | ||||
|  | ||||
|   //service | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.HardwareJob": { | ||||
|     "GetHardwareInfoFail": "获取硬件信息出错" | ||||
|   }, | ||||
|  | ||||
|   //dto | ||||
|   "ThingsGateway.Admin.Application.UserSelectorOutput": { | ||||
|     "Account": "账号", | ||||
|     "OrgId": "机构" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.ResourceTableSearchModel": { | ||||
|     "Module": "模块", | ||||
|     "Href": "路径", | ||||
|     "Title": "标题" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.WorkbenchInfo": { | ||||
|     "Razor": "主页", | ||||
|     "Shortcuts": "快捷方式" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.UpdatePasswordInput": { | ||||
|     "Password": "密码", | ||||
|     "NewPassword": "新密码", | ||||
|     "ConfirmPassword": "确认密码", | ||||
|     "Password.Required": " {0} 是必填项", | ||||
|     "NewPassword.Required": " {0} 是必填项", | ||||
|     "ConfirmPassword.Required": " {0} 是必填项" | ||||
|   }, | ||||
|  | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.VerificatInfo": { | ||||
|     "Expire": "过期时间(分)", | ||||
|     "Online": "在线状态", | ||||
|     "VerificatTimeout": "超时时间", | ||||
|     "Device": "登录设备", | ||||
|     "LoginIp": "登录IP", | ||||
|     "LoginTime": "登录时间" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SessionOutput": { | ||||
|     "Account": "账号", | ||||
|     "Online": "在线状态", | ||||
|     "LatestLoginIp": "最新登录ip", | ||||
|     "LatestLoginTime": "最新登录时间", | ||||
|     "VerificatCount": "令牌数量" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysDict": { | ||||
|     "Category.Required": "{0} 是必填项", | ||||
|     "Name.Required": "{0} 是必填项", | ||||
|     "Code.Required": "{0} 是必填项", | ||||
|     "Category": "分类", | ||||
|     "Name": "名称", | ||||
|     "Code": "代码", | ||||
|     "Remark": "备注", | ||||
|     "DictDup": "存在重复的配置 分类 {0} 名称 {1}", | ||||
|     "DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置" | ||||
|   }, | ||||
|  | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.SysOperateLog": { | ||||
|     "ClassName": "类名", | ||||
|     "ExeMessage": "具体消息", | ||||
|     "MethodName": "方法名称", | ||||
|     "ParamJson": "请求参数", | ||||
|     "ReqMethod": "请求方式", | ||||
|     "ReqUrl": "请求地址", | ||||
|     "ResultJson": "返回结果", | ||||
|     "Category": "日志分类", | ||||
|     "ExeStatus": "执行状态", | ||||
|     "Name": "日志名称", | ||||
|     "OpAccount": "账号", | ||||
|     "OpBrowser": "浏览器", | ||||
|     "OpIp": "ip", | ||||
|     "OpOs": "系统", | ||||
|     "OpTime": "操作时间", | ||||
|     "VerificatId": "验证Id" | ||||
|  | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.OperateLogPageInput": { | ||||
|     "SearchDate": "时间范围", | ||||
|     "Account": "操作账号", | ||||
|     "Category": "分类" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.LoginInput": { | ||||
|     "Account": "登录账号", | ||||
|     "Password": "登录密码", | ||||
|     "Account.Required": "{0} 是必填项", | ||||
|     "Password.Required": "{0} 是必填项" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.LogoutInput": { | ||||
|     "VerificatId.Required": "{0} 是必填项" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.AppConfig": { | ||||
|     "LoginPolicy": "登录策略", | ||||
|     "PasswordPolicy": "密码策略", | ||||
|     "PagePolicy": "页面设置", | ||||
|     "WebsitePolicy": "网站设置" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.LoginPolicy": { | ||||
|     "SingleOpen": "单用户登录开关", | ||||
|     "ErrorLockTime": "登录错误锁定时长(分)", | ||||
|     "ErrorResetTime": "登录错误次数过期时长(分)", | ||||
|     "ErrorCount": "登录错误次数锁定阈值", | ||||
|     "VerificatExpireTime": "登录过期时间(分)", | ||||
|     "ErrorLockTime.MinValue": " {0} 值太小", | ||||
|     "ErrorResetTime.MinValue": " {0} 值太小", | ||||
|     "ErrorCount.MinValue": " {0} 值太小", | ||||
|     "VerificatExpireTime.MinValue": " {0} 值太小" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.PagePolicy": { | ||||
|     "Shortcuts": "默认快捷方式", | ||||
|     "Razor": "默认主页" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.PasswordPolicy": { | ||||
|     "DefaultPassword": "默认用户密码", | ||||
|     "DefaultPassword.Required": " {0} 是必填项", | ||||
|     "PasswordMinLen": "密码最小长度", | ||||
|     "PasswordMinLen.MinValue": " {0} 值太小", | ||||
|     "PasswordContainNum": "包含数字", | ||||
|     "PasswordContainLower": "包含小写字母", | ||||
|     "PasswordContainUpper": "包含大写字母", | ||||
|     "PasswordContainChar": "包含特殊字符" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.WebsitePolicy": { | ||||
|     "WebStatus": "是否开放", | ||||
|     "CloseTip": "关闭提示", | ||||
|     "CloseTip.Required": " {0} 是必填项" | ||||
|   }, | ||||
|  | ||||
|  | ||||
|  | ||||
|   //enum | ||||
|   "ThingsGateway.Admin.Application.ResourceCategoryEnum": { | ||||
|     "Module": "模块", | ||||
|     "Menu": "菜单", | ||||
|     "Button": "按钮" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.TargetEnum": { | ||||
|     "_self": "本窗口", | ||||
|     "_blank": "新窗口", | ||||
|     "_parent": "父级窗口", | ||||
|     "_top": "顶级窗口" | ||||
|   }, | ||||
|   "ThingsGateway.Admin.Application.DictTypeEnum": { | ||||
|     "System": "系统配置", | ||||
|     "Define": "业务配置" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.LogCateGoryEnum": { | ||||
|     "Login": "登录", | ||||
|     "Logout": "注销", | ||||
|     "Operate": "操作", | ||||
|     "Exception": "异常" | ||||
|   }, | ||||
|  | ||||
|   "ThingsGateway.Admin.Application.LogEnum": { | ||||
|     "SUCCESS": "成功", | ||||
|     "FAIL": "失败" | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,212 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Extension; | ||||
| using ThingsGateway.FriendlyException; | ||||
| using ThingsGateway.Logging; | ||||
| using ThingsGateway.NewLife.Json.Extension; | ||||
| using ThingsGateway.Razor; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 数据库写入器 | ||||
| /// </summary> | ||||
| public class DatabaseLoggingWriter : IDatabaseLoggingWriter | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志消息队列(线程安全) | ||||
|     /// </summary> | ||||
|     private readonly ConcurrentQueue<SysOperateLog> _operateLogMessageQueue = new(); | ||||
|  | ||||
|     private SqlSugarClient SqlSugarClient; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 此方法只会写入经由MVCFilter捕捉的方法日志,对于BlazorServer的内部操作,由<see cref="OperDescAttribute"/>执行 | ||||
|     /// </summary> | ||||
|     /// <param name="logMsg"></param> | ||||
|     /// <param name="flush"></param> | ||||
|     public async Task WriteAsync(LogMessage logMsg, bool flush) | ||||
|     { | ||||
|         //转成实体 | ||||
|         var requestAuditData = logMsg.Context.Get(nameof(RequestAuditData)) as RequestAuditData; | ||||
|         //日志时间赋值 | ||||
|         requestAuditData.LogDateTime = logMsg.LogDateTime; | ||||
|         // requestAuditData.ReturnInformation.Value | ||||
|         //验证失败不记录日志 | ||||
|         bool save = false; | ||||
|         if (requestAuditData.Validation == null) | ||||
|         { | ||||
|             var operation = requestAuditData.Operation;//获取操作名称 | ||||
|             var client = requestAuditData.Client;//获取客户端信息 | ||||
|             var path = requestAuditData.Path;//获取操作名称 | ||||
|             var method = requestAuditData.Method;//获取方法 | ||||
|             var methodInfo = requestAuditData.MethodInfo; | ||||
|             var login = methodInfo.GetCustomAttribute(typeof(LoginLogAttribute)); | ||||
|             var logout = methodInfo.GetCustomAttribute(typeof(LogoutLogAttribute)); | ||||
|  | ||||
|             //表示访问日志 | ||||
|             if (login != null || logout != null) | ||||
|             { | ||||
|                 //如果没有异常信息 | ||||
|                 if (requestAuditData.Exception == null) | ||||
|                 { | ||||
|                     LogCateGoryEnum logCateGoryEnum = login != null ? LogCateGoryEnum.Login : LogCateGoryEnum.Logout; | ||||
|                     save = await CreateVisitLog(operation, path, requestAuditData, client, logCateGoryEnum, flush).ConfigureAwait(false);//添加到访问日志 | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //添加到异常日志 | ||||
|                     save = await CreateOperationLog(operation, path, requestAuditData, client, flush).ConfigureAwait(false); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 //只有定义了Title的POST方法才记录日志 | ||||
|                 if (!operation.IsNullOrWhiteSpace() && method == "POST") | ||||
|                 { | ||||
|                     //添加到操作日志 | ||||
|                     save = await CreateOperationLog(operation, path, requestAuditData, client, flush).ConfigureAwait(false); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (save) | ||||
|         { | ||||
|             await Task.Delay(1000).ConfigureAwait(false); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 创建操作日志 | ||||
|     /// </summary> | ||||
|     /// <param name="operation">操作名称</param> | ||||
|     /// <param name="path">请求地址</param> | ||||
|     /// <param name="requestAuditData">requestAuditData</param> | ||||
|     /// <param name="userAgent">客户端信息</param> | ||||
|     /// <param name="flush"></param> | ||||
|     /// <returns></returns> | ||||
|     private async Task<bool> CreateOperationLog(string operation, string path, RequestAuditData requestAuditData, UserAgent userAgent, bool flush) | ||||
|     { | ||||
|         //账号 | ||||
|         var opAccount = requestAuditData.AuthorizationClaims?.Where(it => it.Type == ClaimConst.Account).Select(it => it.Value).FirstOrDefault(); | ||||
|  | ||||
|         //获取参数json字符串, | ||||
|         var paramJson = requestAuditData.Parameters == null || requestAuditData.Parameters.Count == 0 ? null : requestAuditData.Parameters.ToSystemTextJsonString(); | ||||
|  | ||||
|         //获取结果json字符串 | ||||
|         var resultJson = requestAuditData.ReturnInformation?.ToSystemTextJsonString(); | ||||
|  | ||||
|  | ||||
|         //操作日志表实体 | ||||
|         var sysLogOperate = new SysOperateLog | ||||
|         { | ||||
|             Name = operation, | ||||
|             Category = LogCateGoryEnum.Operate, | ||||
|             ExeStatus = true, | ||||
|             OpIp = requestAuditData.RemoteIPv4, | ||||
|             OpBrowser = userAgent?.Browser, | ||||
|             OpOs = userAgent?.Platform, | ||||
|             OpTime = requestAuditData.LogDateTime.LocalDateTime, | ||||
|             OpAccount = opAccount, | ||||
|             ReqMethod = requestAuditData.Method, | ||||
|             ReqUrl = path, | ||||
|             ResultJson = resultJson, | ||||
|             ClassName = requestAuditData.ControllerName, | ||||
|             MethodName = requestAuditData.ActionName, | ||||
|             ParamJson = paramJson, | ||||
|             VerificatId = UserManager.VerificatId, | ||||
|         }; | ||||
|         //如果异常不为空 | ||||
|         if (requestAuditData.Exception != null) | ||||
|         { | ||||
|             sysLogOperate.Category = LogCateGoryEnum.Exception;//操作类型为异常 | ||||
|             sysLogOperate.ExeStatus = false;//操作状态为失败 | ||||
|  | ||||
|             if (requestAuditData.Exception.Type == typeof(AppFriendlyException).ToString()) | ||||
|                 sysLogOperate.ExeMessage = requestAuditData?.Exception.Message; | ||||
|             else | ||||
|                 sysLogOperate.ExeMessage = $"{requestAuditData.Exception.Type}:{requestAuditData.Exception.Message}{Environment.NewLine}{requestAuditData.Exception.StackTrace}"; | ||||
|         } | ||||
|  | ||||
|         _operateLogMessageQueue.Enqueue(sysLogOperate); | ||||
|  | ||||
|         if (flush) | ||||
|         { | ||||
|             SqlSugarClient ??= DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew(); | ||||
|             await SqlSugarClient.InsertableWithAttr(_operateLogMessageQueue.ToListWithDequeue()).ExecuteCommandAsync().ConfigureAwait(false);//入库 | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 创建访问日志 | ||||
|     /// </summary> | ||||
|     /// <param name="operation">访问类型</param> | ||||
|     /// <param name="path"></param> | ||||
|     /// <param name="requestAuditData">requestAuditData</param> | ||||
|     /// <param name="userAgent">客户端信息</param> | ||||
|     /// <param name="logCateGoryEnum">logCateGory</param> | ||||
|     /// <param name="flush"></param> | ||||
|     private async Task<bool> CreateVisitLog(string operation, string path, RequestAuditData requestAuditData, UserAgent userAgent, LogCateGoryEnum logCateGoryEnum, bool flush) | ||||
|     { | ||||
|         long verificatId = 0;//验证Id | ||||
|         var opAccount = "";//用户账号 | ||||
|         if (logCateGoryEnum == LogCateGoryEnum.Login) | ||||
|         { | ||||
|             //如果是登录,用户信息就从返回值里拿 | ||||
|             if (requestAuditData.ReturnInformation is UnifyResult<LoginOutput> userInfo) | ||||
|             { | ||||
|                 opAccount = userInfo.Data.Account;//赋值账号 | ||||
|                 verificatId = userInfo.Data.VerificatId; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             //如果是登录出,用户信息就从AuthorizationClaims里拿 | ||||
|             opAccount = requestAuditData.AuthorizationClaims.Where(it => it.Type == ClaimConst.Account).Select(it => it.Value).FirstOrDefault(); | ||||
|             verificatId = requestAuditData.AuthorizationClaims.Where(it => it.Type == ClaimConst.VerificatId).Select(it => it.Value).FirstOrDefault().ToLong(); | ||||
|         } | ||||
|         //日志表实体 | ||||
|         var sysLogVisit = new SysOperateLog | ||||
|         { | ||||
|             Name = operation, | ||||
|             Category = logCateGoryEnum, | ||||
|             ExeStatus = true, | ||||
|             OpIp = requestAuditData.RemoteIPv4, | ||||
|             OpBrowser = userAgent?.Browser, | ||||
|             OpOs = userAgent?.Platform, | ||||
|             OpTime = requestAuditData.LogDateTime.LocalDateTime, | ||||
|             VerificatId = verificatId, | ||||
|             OpAccount = opAccount, | ||||
|  | ||||
|             ReqMethod = requestAuditData.Method, | ||||
|             ReqUrl = path, | ||||
|             ResultJson = requestAuditData.ReturnInformation?.ToSystemTextJsonString(), | ||||
|             ClassName = requestAuditData.ControllerName, | ||||
|             MethodName = requestAuditData.ActionName, | ||||
|             ParamJson = requestAuditData.Parameters?.ToSystemTextJsonString(), | ||||
|         }; | ||||
|         _operateLogMessageQueue.Enqueue(sysLogVisit); | ||||
|  | ||||
|         if (flush) | ||||
|         { | ||||
|             SqlSugarClient ??= DbContext.Db.GetConnectionScopeWithAttr<SysOperateLog>().CopyNew(); | ||||
|             await SqlSugarClient.InsertableWithAttr(_operateLogMessageQueue.ToListWithDequeue()).ExecuteCommandAsync().ConfigureAwait(false);//入库 | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,42 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 日志常量 | ||||
| /// </summary> | ||||
| public static class LoggingConst | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 分类 | ||||
|     /// </summary> | ||||
|     public const string CateGory = "CateGory"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 客户端信息 | ||||
|     /// </summary> | ||||
|     public const string Client = "Client"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求方法:POST/GET | ||||
|     /// </summary> | ||||
|     public const string Method = "Method"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 操作名称 | ||||
|     /// </summary> | ||||
|     public const string Operation = "Operation"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求地址 | ||||
|     /// </summary> | ||||
|     public const string Path = "Path"; | ||||
| } | ||||
| @@ -0,0 +1,187 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 认证信息 | ||||
| /// </summary> | ||||
| public class AuthorizationClaims | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 类型 | ||||
|     /// </summary> | ||||
|     public string Type { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 值 | ||||
|     /// </summary> | ||||
|     public string Value { get; set; } | ||||
| } | ||||
|  | ||||
| /// <summary> | ||||
| /// 异常信息 | ||||
| /// </summary> | ||||
| public class LogException | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 异常内容 | ||||
|     /// </summary> | ||||
|     public string Message { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 堆栈信息 | ||||
|     /// </summary> | ||||
|     public string StackTrace { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 异常类型 | ||||
|     /// </summary> | ||||
|     public string Type { get; set; } | ||||
| } | ||||
|  | ||||
| /// <summary> | ||||
| /// 请求信息格式化 | ||||
| /// </summary> | ||||
| public class LoggingMonitorJson | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 方法名称 | ||||
|     /// </summary> | ||||
|     public string ActionName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 认证信息 | ||||
|     /// </summary> | ||||
|     public List<AuthorizationClaims> AuthorizationClaims { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 控制器名 | ||||
|     /// </summary> | ||||
|     public string ControllerName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 类名称 | ||||
|     /// </summary> | ||||
|     public string DisplayName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 环境 | ||||
|     /// </summary> | ||||
|     public string Environment { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 异常信息 | ||||
|     /// </summary> | ||||
|     public LogException Exception { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求方法 | ||||
|     /// </summary> | ||||
|     public string HttpMethod { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 服务端 | ||||
|     /// </summary> | ||||
|     public string LocalIPv4 { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 日志时间 | ||||
|     /// </summary> | ||||
|     public DateTimeOffset LogDateTime { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 系统架构 | ||||
|     /// </summary> | ||||
|     public string OsArchitecture { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 系统名称 | ||||
|     /// </summary> | ||||
|     public string OsDescription { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 参数列表 | ||||
|     /// </summary> | ||||
|     public List<Parameters> Parameters { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 客户端IPV4地址 | ||||
|     /// </summary> | ||||
|     public string RemoteIPv4 { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 认证信息 | ||||
|     /// </summary> | ||||
|     public string RequestHeaderAuthorization { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 认证信息 | ||||
|     /// </summary> | ||||
|     public string RequestHeaderCookies { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 请求地址 | ||||
|     /// </summary> | ||||
|     public string RequestUrl { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 返回信息 | ||||
|     /// </summary> | ||||
|     public ReturnInformation ReturnInformation { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 浏览器标识 | ||||
|     /// </summary> | ||||
|     public string UserAgent { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 验证错误信息 | ||||
|     /// </summary> | ||||
|     public Validation Validation { get; set; } | ||||
| } | ||||
|  | ||||
| /// <summary> | ||||
| /// 请求参数 | ||||
| /// </summary> | ||||
| public class Parameters | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 参数名 | ||||
|     /// </summary> | ||||
|     public string Name { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 值 | ||||
|     /// </summary> | ||||
|     public object Value { get; set; } | ||||
| } | ||||
|  | ||||
| /// <summary> | ||||
| /// 返回信息 | ||||
| /// </summary> | ||||
| public class ReturnInformation | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 返回值 | ||||
|     /// </summary> | ||||
|     public object Value { get; set; } | ||||
| } | ||||
|  | ||||
| /// <summary> | ||||
| /// 验证失败信息 | ||||
| /// </summary> | ||||
| public class Validation | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 错误详情 | ||||
|     /// </summary> | ||||
|     public string Message { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.ConfigurableOptions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
|  | ||||
| public sealed class AdminLogOptions : IConfigurableOptions | ||||
| { | ||||
|  | ||||
|     public int OperateLogDaysAgo { get; set; } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,60 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.ConfigurableOptions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
|  | ||||
| /// <summary> | ||||
| /// 邮件配置选项 | ||||
| /// </summary> | ||||
| public sealed class EmailOptions : IConfigurableOptions | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 主机 | ||||
|     /// </summary> | ||||
|     public string Host { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 端口 | ||||
|     /// </summary> | ||||
|     public int Port { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 默认发件者邮箱 | ||||
|     /// </summary> | ||||
|     public string DefaultFromEmail { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 默认接收人邮箱 | ||||
|     /// </summary> | ||||
|     public string DefaultToEmail { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 启用SSL | ||||
|     /// </summary> | ||||
|     public bool EnableSsl { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 邮箱账号 | ||||
|     /// </summary> | ||||
|     public string UserName { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 邮箱密码 | ||||
|     /// </summary> | ||||
|     public string Password { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 默认邮件标题 | ||||
|     /// </summary> | ||||
|     public string DefaultFromName { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,34 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.ConfigurableOptions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 历史硬件信息 | ||||
| /// </summary> | ||||
| public class HardwareInfoOptions : IConfigurableOptions | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 启用 | ||||
|     /// </summary> | ||||
|     public bool Enable { get; set; } = true; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 历史保存间隔 | ||||
|     /// </summary> | ||||
|     public int HistoryInterval { get; set; } = 60000; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 历史保留天数 | ||||
|     /// </summary> | ||||
|     public int DaysAgo { get; set; } = 7; | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.ConfigurableOptions; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
|  | ||||
| public sealed class TenantOptions : IConfigurableOptions | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 启用 | ||||
|     /// </summary> | ||||
|     public bool Enable { get; set; } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,283 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Authentication; | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Components; | ||||
| using Microsoft.AspNetCore.Http; | ||||
|  | ||||
| using System.Security.Claims; | ||||
|  | ||||
| using ThingsGateway.Authorization; | ||||
| using ThingsGateway.DataEncryption; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class BlazorAuthenticationHandler : AppAuthorizeHandler | ||||
| { | ||||
|     private readonly ISysDictService _sysDictService; | ||||
|     private readonly ISysRoleService _sysRoleService; | ||||
|     private readonly ISysUserService _sysUserService; | ||||
|  | ||||
|     public BlazorAuthenticationHandler(ISysUserService sysUserService, ISysRoleService sysRoleService, ISysDictService sysDictService) | ||||
|     { | ||||
|         _sysUserService = sysUserService; | ||||
|         _sysRoleService = sysRoleService; | ||||
|         _sysDictService = sysDictService; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task HandleAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext) | ||||
|     { | ||||
|         var isAuthenticated = context.User.Identity?.IsAuthenticated; | ||||
|         if (isAuthenticated == true) | ||||
|         { | ||||
|             if (await CheckVerificatFromCacheAsync(context).ConfigureAwait(false)) | ||||
|             { | ||||
|                 await AuthorizeHandleAsync(context).ConfigureAwait(false); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 await Fail(context).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             await Fail(context).ConfigureAwait(false);// 授权失败 | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     static async Task Fail(AuthorizationHandlerContext context) | ||||
|     { | ||||
|         var verificatId = UserManager.VerificatId; | ||||
|         var verificatInfo = App.GetService<IVerificatInfoService>().GetOne(verificatId, false); | ||||
|         if (App.HttpContext != null) | ||||
|         { | ||||
|             var identity = new ClaimsIdentity(); | ||||
|             App.HttpContext.User = new ClaimsPrincipal(identity); | ||||
|         } | ||||
|         context.Fail(); // 授权失败 | ||||
|  | ||||
|         if (verificatInfo != null) | ||||
|             await App.GetService<INoticeService>().UserLoginOut(verificatInfo.ClientIds, App.CreateLocalizerByType(typeof(BlazorAuthenticationHandler))["UserExpire"]).ConfigureAwait(false); | ||||
|  | ||||
|         DefaultHttpContext currentHttpContext = context.GetCurrentHttpContext(); | ||||
|         if (currentHttpContext == null) | ||||
|             return; | ||||
|         currentHttpContext.Response.StatusCode = 401; //返回401给授权筛选器用 | ||||
|         currentHttpContext.SignoutToSwagger(); | ||||
|         await currentHttpContext.SignOutAsync().ConfigureAwait(false); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<bool> PipelineAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext) | ||||
|     { | ||||
|         var userId = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong(0) ?? 0; | ||||
|  | ||||
|         var user = await _sysUserService.GetUserByIdAsync(userId).ConfigureAwait(false); | ||||
|         if (context.Resource is Microsoft.AspNetCore.Components.RouteData routeData) | ||||
|         { | ||||
|             var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false); | ||||
|  | ||||
|             //这里鉴别用户使能状态 | ||||
|             if (user?.Status != true) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             //超级管理员都能访问 | ||||
|             var isSuperAdmin = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.SuperAdmin)?.Value.ToBoolean(); | ||||
|             if (isSuperAdmin == true) return true; | ||||
|  | ||||
|             // 获取超级管理员特性 | ||||
|             var superAdminAttr = routeData.PageType.CustomAttributes.FirstOrDefault(x => | ||||
|                x.AttributeType == typeof(SuperAdminAttribute)); | ||||
|  | ||||
|             if (superAdminAttr != null) //如果是超级管理员才能访问的接口 | ||||
|             { | ||||
|                 return false; //直接没权限 | ||||
|             } | ||||
|             //获取角色授权特性 | ||||
|             var isRolePermission = routeData.PageType.CustomAttributes.FirstOrDefault(x => | ||||
|                x.AttributeType == typeof(RolePermissionAttribute)); | ||||
|             if (isRolePermission != null) | ||||
|             { | ||||
|                 //获取忽略角色授权特性 | ||||
|                 var isIgnoreRolePermission = routeData.PageType.CustomAttributes.FirstOrDefault(x => | ||||
|        x.AttributeType == typeof(IgnoreRolePermissionAttribute)); | ||||
|                 if (isIgnoreRolePermission == null) | ||||
|                 { | ||||
|                     // 路由名称 | ||||
|                     var routeName = routeData.PageType.CustomAttributes.FirstOrDefault(x => | ||||
|                         x.AttributeType == typeof(RouteAttribute))?.ConstructorArguments?[0].Value as string; | ||||
|                     if (routeName == null) return true; | ||||
|  | ||||
|                     if ((!user.PermissionCodeList.Contains(routeName.CutStart("/")) && !user.PermissionCodeList.Contains(routeName))) //如果当前路由信息不包含在角色授权路由列表中则认证失败 | ||||
|                         return false; | ||||
|                     else | ||||
|                         return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             //这里鉴别用户使能状态 | ||||
|             if (user?.Status != true) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             //超级管理员都能访问 | ||||
|             var isSuperAdmin = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.SuperAdmin)?.Value?.ToBoolean(); | ||||
|             if (isSuperAdmin == true) return true; | ||||
|  | ||||
|             if (httpContext == null) | ||||
|             { | ||||
|                 //非API请求 | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             var superAdminAttr = httpContext.GetMetadata<SuperAdminAttribute>(); | ||||
|  | ||||
|             if (superAdminAttr != null) //如果是超级管理员才能访问的接口 | ||||
|             { | ||||
|                 //获取忽略超级管理员特性 | ||||
|                 var ignoreSpuerAdmin = httpContext.GetMetadata<IgnoreSuperAdminAttribute>(); | ||||
|                 if (ignoreSpuerAdmin == null && isSuperAdmin != true) //如果只能超级管理员访问并且用户不是超级管理员 | ||||
|                     return false; //直接没权限 | ||||
|             } | ||||
|  | ||||
|             //获取角色授权特性 | ||||
|             var isRolePermission = httpContext.GetMetadata<RolePermissionAttribute>(); | ||||
|             if (isRolePermission != null) | ||||
|             { | ||||
|                 //获取忽略角色授权特性 | ||||
|                 var ignoreRolePermission = httpContext.GetMetadata<IgnoreRolePermissionAttribute>(); | ||||
|                 if (ignoreRolePermission == null) | ||||
|                 { | ||||
|                     // 路由名称 | ||||
|                     var routeName = httpContext.Request.Path.Value; | ||||
|                     if (routeName == null) return true; | ||||
|                     if ((!user.PermissionCodeList.Contains(routeName.CutStart("/")) && !user.PermissionCodeList.Contains(routeName))) //如果当前路由信息不包含在角色授权路由列表中则认证失败 | ||||
|                         return false; | ||||
|                     else | ||||
|                     { | ||||
|                         return true; //没有用户信息则返回认证失败 | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 检查 BearerToken/Cookie 有效性 | ||||
|     /// </summary> | ||||
|     /// <param name="context">DefaultHttpContext</param> | ||||
|     /// <returns></returns> | ||||
|     private async Task<bool> CheckVerificatFromCacheAsync(AuthorizationHandlerContext context) | ||||
|     { | ||||
|         DefaultHttpContext currentHttpContext = context.GetCurrentHttpContext(); | ||||
|         var userId = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong(); | ||||
|         var verificatId = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.VerificatId)?.Value?.ToLong(); | ||||
|         var expire = (await _sysDictService.GetAppConfigAsync().ConfigureAwait(false)).LoginPolicy.VerificatExpireTime; | ||||
|         if (currentHttpContext == null) | ||||
|         { | ||||
|             return CheckVerificat(userId, verificatId); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (await JWTEncryption.AutoRefreshToken(context, currentHttpContext, expire, expire * 2).ConfigureAwait(false)) | ||||
|             { | ||||
|                 //var token = JWTEncryption.GetJwtBearerToken(currentHttpContext); //获取当前token | ||||
|  | ||||
|                 var verificatInfo = userId != null ? VerificatInfoService.GetOne(verificatId ?? 0, false) : null;//获取token信息 | ||||
|                 if (verificatInfo != null) | ||||
|                 { | ||||
|                     if (verificatInfo.VerificatTimeout < DateTime.Now.AddMinutes(1)) | ||||
|                     { | ||||
|                         verificatInfo.VerificatTimeout = verificatInfo.VerificatTimeout.AddMinutes(verificatInfo.Expire); //新的过期时间 | ||||
|                         VerificatInfoService.Update(verificatInfo); //更新tokne信息到cache | ||||
|                     } | ||||
|                     return true; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 //失败 | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     private static IVerificatInfoService _verificatInfoService; | ||||
|     private static IVerificatInfoService VerificatInfoService | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             if (_verificatInfoService == null) | ||||
|                 _verificatInfoService = App.GetService<IVerificatInfoService>(); | ||||
|  | ||||
|             return _verificatInfoService; | ||||
|         } | ||||
|     } | ||||
|     public static bool CheckVerificat(long? userId, long? verificatId, bool autoUpdate = true) | ||||
|     { | ||||
|         var verificatInfo = userId != null ? VerificatInfoService.GetOne(verificatId ?? 0, false) : null;//获取token信息 | ||||
|  | ||||
|         if (verificatInfo != null) | ||||
|         { | ||||
|             if (verificatInfo.VerificatTimeout < DateTime.Now.AddMinutes(1)) | ||||
|             { | ||||
|                 if (!autoUpdate && verificatInfo.VerificatTimeout < DateTime.Now) | ||||
|                     return false; | ||||
|  | ||||
|                 if (verificatInfo.VerificatTimeout.AddMinutes(verificatInfo.Expire) < DateTime.Now) | ||||
|                 { | ||||
|                     return false; | ||||
|                 } | ||||
|  | ||||
|                 if (autoUpdate) | ||||
|                 { | ||||
|                     verificatInfo.VerificatTimeout = verificatInfo.VerificatTimeout.AddMinutes(verificatInfo.Expire); //新的过期时间 | ||||
|                     VerificatInfoService.Update(verificatInfo); //更新tokne信息到cache | ||||
|  | ||||
|  | ||||
|                 } | ||||
|  | ||||
|                 //无法在server中刷新cookies,单页面应用会一直保持登录状态,所以这里不需要刷新cookies,但是F5刷新后会重新登录 | ||||
|  | ||||
|             } | ||||
|  | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,105 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Components.Authorization; | ||||
|  | ||||
| using System.Security.Claims; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class BlazorHybridAuthenticationStateProvider : AuthenticationStateProvider | ||||
| { | ||||
|     private readonly IAppService _appService; | ||||
|     private CancellationTokenSource _loopCancellationTokenSource = new CancellationTokenSource(); | ||||
|  | ||||
|     public BlazorHybridAuthenticationStateProvider(IAppService appService) | ||||
|     { | ||||
|         _appService = appService; | ||||
|     } | ||||
|  | ||||
|     private AuthenticationState AuthenticationState = new AuthenticationState(new ClaimsPrincipal()); | ||||
|     public void UserChanged(ClaimsPrincipal user) | ||||
|     { | ||||
|         AuthenticationState = new AuthenticationState(user); | ||||
|         NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); | ||||
|         var oldCancellationTokenSource = _loopCancellationTokenSource; | ||||
|         if (oldCancellationTokenSource is not null) | ||||
|         { | ||||
|             oldCancellationTokenSource.Cancel(); | ||||
|             oldCancellationTokenSource.Dispose(); | ||||
|         } | ||||
|  | ||||
|         _loopCancellationTokenSource = new CancellationTokenSource(); | ||||
|         if (AuthenticationState.User?.Identity?.IsAuthenticated == true) | ||||
|             _ = RevalidationLoop(_loopCancellationTokenSource.Token); | ||||
|     } | ||||
|     private async Task RevalidationLoop(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (AuthenticationState.User.Identity?.IsAuthenticated == true) | ||||
|             { | ||||
|                 while (!cancellationToken.IsCancellationRequested) | ||||
|                 { | ||||
|                     bool isValid = false; | ||||
|  | ||||
|                     try | ||||
|                     { | ||||
|                         await Task.Delay(30000, cancellationToken).ConfigureAwait(false); | ||||
|                         isValid = await ValidateAuthenticationStateAsync(AuthenticationState, cancellationToken).ConfigureAwait(false); | ||||
|                     } | ||||
|                     catch (TaskCanceledException) | ||||
|                     { | ||||
|                         break; | ||||
|                     } | ||||
|  | ||||
|                     if (!isValid) | ||||
|                     { | ||||
|                         ForceSignOut(); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|             ForceSignOut(); | ||||
|         } | ||||
|     } | ||||
|     protected virtual async Task<bool> ValidateAuthenticationStateAsync(AuthenticationState authenticationState, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var userId = authenticationState.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong(); | ||||
|         var verificatId = authenticationState.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.VerificatId)?.Value?.ToLong(); | ||||
|         var result = BlazorAuthenticationHandler.CheckVerificat(userId, verificatId, false); | ||||
|         if (!result) | ||||
|         { | ||||
|             var verificatInfo = App.GetService<IVerificatInfoService>().GetOne(verificatId ?? 0, false); | ||||
|             if (App.HttpContext != null) | ||||
|             { | ||||
|                 var identity = new ClaimsIdentity(); | ||||
|                 App.HttpContext.User = new ClaimsPrincipal(identity); | ||||
|             } | ||||
|  | ||||
|             if (verificatInfo != null) | ||||
|                 await App.GetService<INoticeService>().UserLoginOut(verificatInfo.ClientIds, App.CreateLocalizerByType(typeof(BlazorAuthenticationHandler))["UserExpire"]).ConfigureAwait(false); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|     private void ForceSignOut() | ||||
|     { | ||||
|         var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity()); | ||||
|         UserChanged(anonymousUser); | ||||
|     } | ||||
|     public override Task<AuthenticationState> GetAuthenticationStateAsync() | ||||
|     { | ||||
|         return Task.FromResult(AuthenticationState); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class BlazorHybridAuthorizationHandler : BlazorAuthenticationHandler | ||||
| { | ||||
|  | ||||
|     public BlazorHybridAuthorizationHandler(ISysUserService sysUserService, ISysRoleService sysRoleService, ISysDictService sysDictService) : base(sysUserService, sysRoleService, sysDictService) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,19 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class BlazorServerAuthenticationHandler : BlazorAuthenticationHandler | ||||
| { | ||||
|     public BlazorServerAuthenticationHandler(ISysUserService sysUserService, ISysRoleService sysRoleService, ISysDictService sysDictService) : base(sysUserService, sysRoleService, sysDictService) | ||||
|     { | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,50 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Components.Authorization; | ||||
| using Microsoft.AspNetCore.Components.Server; | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using System.Security.Claims; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class BlazorServerAuthenticationStateProvider : RevalidatingServerAuthenticationStateProvider | ||||
| { | ||||
|     private readonly IAppService _appService; | ||||
|  | ||||
|     public BlazorServerAuthenticationStateProvider(ILoggerFactory loggerFactory, IAppService appService) : base(loggerFactory) | ||||
|     { | ||||
|         _appService = appService; | ||||
|     } | ||||
|  | ||||
|     protected override TimeSpan RevalidationInterval => TimeSpan.FromSeconds(30); | ||||
|  | ||||
|     protected override async Task<bool> ValidateAuthenticationStateAsync(AuthenticationState authenticationState, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var userId = authenticationState.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong(); | ||||
|         var verificatId = authenticationState.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.VerificatId)?.Value?.ToLong(); | ||||
|         var result = BlazorAuthenticationHandler.CheckVerificat(userId, verificatId, false); | ||||
|         if (!result) | ||||
|         { | ||||
|             var verificatInfo = App.GetService<IVerificatInfoService>().GetOne(verificatId ?? 0, false); | ||||
|             if (App.HttpContext != null) | ||||
|             { | ||||
|                 var identity = new ClaimsIdentity(); | ||||
|                 App.HttpContext.User = new ClaimsPrincipal(identity); | ||||
|             } | ||||
|  | ||||
|             if (verificatInfo != null) | ||||
|                 await App.GetService<INoticeService>().UserLoginOut(verificatInfo.ClientIds, App.CreateLocalizerByType(typeof(BlazorAuthenticationHandler))["UserExpire"]).ConfigureAwait(false); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,102 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.AspNetCore.Mvc.Filters; | ||||
| using Microsoft.Extensions.Localization; | ||||
|  | ||||
| using ThingsGateway.DataValidation; | ||||
| using ThingsGateway.FriendlyException; | ||||
| using ThingsGateway.Razor; | ||||
| using ThingsGateway.UnifyResult; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 规范化RESTful风格返回值 | ||||
| /// </summary> | ||||
| [UnifyModel(typeof(UnifyResult<>))] | ||||
| public class UnifyResultProvider : IUnifyResultProvider | ||||
| { | ||||
|     private static IStringLocalizer Localizer = App.CreateLocalizerByType(typeof(UnifyResultProvider))!; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 状态码响应拦截 | ||||
|     /// </summary> | ||||
|     public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings = null) | ||||
|     { | ||||
|         switch (statusCode) | ||||
|         { | ||||
|             // 处理 401 状态码 | ||||
|             case StatusCodes.Status401Unauthorized: | ||||
|                 await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, false, Localizer["TokenOver"].Value)).ConfigureAwait(false); | ||||
|                 break; | ||||
|             // 处理 403 状态码 | ||||
|             case StatusCodes.Status403Forbidden: | ||||
|                 await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, false, default, Localizer["NoPermission"].Value)).ConfigureAwait(false); | ||||
|                 break; | ||||
|  | ||||
|             default: break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 成功返回 | ||||
|     /// </summary> | ||||
|     /// <param name="context"></param> | ||||
|     /// <param name="data"></param> | ||||
|     /// <returns></returns> | ||||
|     public IActionResult OnSucceeded(ActionExecutedContext context, object? data) | ||||
|     { | ||||
|         return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 返回 RESTful 风格结果集 | ||||
|     /// </summary> | ||||
|     /// <param name="statusCode">状态码</param> | ||||
|     /// <param name="succeeded">是否成功</param> | ||||
|     /// <param name="data">数据</param> | ||||
|     /// <param name="errors">错误信息</param> | ||||
|     /// <returns></returns> | ||||
|     private static UnifyResult<object> RESTfulResult(int statusCode, bool succeeded = default, object? data = default, object? errors = default) | ||||
|     { | ||||
|         return new UnifyResult<object> | ||||
|         { | ||||
|             Code = statusCode, | ||||
|             Msg = statusCode == StatusCodes.Status200OK ? "Success" : errors, | ||||
|             Data = data, | ||||
|             Time = DateTime.Now, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     public IActionResult OnAuthorizeException(DefaultHttpContext context, ExceptionMetadata metadata) | ||||
|     { | ||||
|         return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors ?? metadata.Exception?.Message) | ||||
|                , UnifyContext.GetSerializerSettings(context)); | ||||
|     } | ||||
|  | ||||
|     public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata) | ||||
|     { | ||||
|         return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors ?? metadata.Exception?.Message) | ||||
|                  , UnifyContext.GetSerializerSettings(context)); | ||||
|     } | ||||
|  | ||||
|     public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata) | ||||
|     { | ||||
|         return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult) // 如果需要只显示第一条错误,修改为:errors: metadata.FirstErrorMessage | ||||
|                       , UnifyContext.GetSerializerSettings(context)); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,124 @@ | ||||
| { | ||||
|   "RECORDS": [ | ||||
|     { | ||||
|       "Id": 252875263003121, | ||||
|       "DictType": "System", | ||||
|       "Category": "LoginPolicy", | ||||
|       "Name": "VerificatExpireTime", | ||||
|       "Code": "14400", | ||||
|       "Remark": "Verificat过期时间(分)", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 9 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 252875263003720, | ||||
|       "DictType": "System", | ||||
|       "Category": "PagePolicy", | ||||
|       "Name": "Shortcuts", | ||||
|       "Code": "[100]", | ||||
|       "Remark": "系统默认工作台数据", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 1 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 252875263003728, | ||||
|       "DictType": "System", | ||||
|       "Category": "PasswordPolicy", | ||||
|       "Name": "DefaultPassword", | ||||
|       "Code": "111111", | ||||
|       "Remark": "默认用户密码", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 1 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 364564896825413, | ||||
|       "DictType": "System", | ||||
|       "Category": "LoginPolicy", | ||||
|       "Name": "SingleOpen", | ||||
|       "Code": "False", | ||||
|       "Remark": "单用户登录开关", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 1 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 432079540875333, | ||||
|       "DictType": "System", | ||||
|       "Category": "LoginPolicy", | ||||
|       "Name": "ErrorCount", | ||||
|       "Code": "10", | ||||
|       "Remark": "登录错误次数", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 99 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 432079718375493, | ||||
|       "DictType": "System", | ||||
|       "Category": "LoginPolicy", | ||||
|       "Name": "ErrorResetTime", | ||||
|       "Code": "1", | ||||
|       "Remark": "错误重置时间", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 99 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 432079850803269, | ||||
|       "DictType": "System", | ||||
|       "Category": "LoginPolicy", | ||||
|       "Name": "ErrorLockTime", | ||||
|       "Code": "2", | ||||
|       "Remark": "登录错误锁定时长", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 99 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 432111701160005, | ||||
|       "DictType": "System", | ||||
|       "Category": "PasswordPolicy", | ||||
|       "Name": "PasswordMinLen", | ||||
|       "Code": "6", | ||||
|       "Remark": "密码最小长度", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 99 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 432112135159877, | ||||
|       "DictType": "System", | ||||
|       "Category": "PasswordPolicy", | ||||
|       "Name": "PasswordContainNum", | ||||
|       "Code": "False", | ||||
|       "Remark": "包含数字", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 99 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 432112321953861, | ||||
|       "DictType": "System", | ||||
|       "Category": "PasswordPolicy", | ||||
|       "Name": "PasswordContainLower", | ||||
|       "Code": "False", | ||||
|       "Remark": "包含小写字母", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 99 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 432112380907589, | ||||
|       "DictType": "System", | ||||
|       "Category": "PasswordPolicy", | ||||
|       "Name": "PasswordContainUpper", | ||||
|       "Code": "False", | ||||
|       "Remark": "包含大写字母", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 99 | ||||
|     }, | ||||
|     { | ||||
|       "Id": 432112495247429, | ||||
|       "DictType": "System", | ||||
|       "Category": "PasswordPolicy", | ||||
|       "Name": "PasswordContainChar", | ||||
|       "Code": "False", | ||||
|       "Remark": "包含特殊字符", | ||||
|       "IsDelete": "0", | ||||
|       "SortCode": 99 | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| { | ||||
|   "RECORDS": [ | ||||
|     { | ||||
|       "Id": 503217527844933, | ||||
|       "Category": 0, | ||||
|       "ObjectId": 212725263002001, | ||||
|       "TargetId": "212725263001001", | ||||
|       "ExtJson": null | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -0,0 +1,288 @@ | ||||
| { | ||||
|   "RECORDS": [ | ||||
|     { | ||||
|       "Id": 2, | ||||
|       "ParentId": 0, | ||||
|       "Title": "系统管理", | ||||
|       "Code": "System", | ||||
|       "Category": "MODULE", | ||||
|       "Target": "_self", | ||||
|       "Href": "", | ||||
|       "Icon": "fa-solid fa-house-user", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100001, | ||||
|       "ParentId": 0, | ||||
|       "Module": 2, | ||||
|       "Title": "权限管理", | ||||
|       "Code": "System", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": null, | ||||
|       "Icon": "fa-solid fa-users-gear", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 100, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100002, | ||||
|       "ParentId": 0, | ||||
|       "Module": 2, | ||||
|       "Title": "系统运维", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": null, | ||||
|       "Icon": "fa-solid fa-file-circle-check", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 101, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100001001, | ||||
|       "Module": 2, | ||||
|       "ParentId": 100001, | ||||
|       "Title": "用户管理", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": "/admin/user", | ||||
|       "Icon": "fa-solid fa-user-gear", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100001005, | ||||
|       "Module": 2, | ||||
|       "ParentId": 100001, | ||||
|       "Title": "机构管理", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": "/admin/org", | ||||
|       "Icon": "fa-solid fa-user-gear", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100001004, | ||||
|       "Module": 2, | ||||
|       "ParentId": 100001, | ||||
|       "Title": "职位管理", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": "/admin/position", | ||||
|       "Icon": "fa-solid fa-user-gear", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100001002, | ||||
|       "Module": 2, | ||||
|       "ParentId": 100001, | ||||
|       "Title": "角色管理", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Href": "/admin/role", | ||||
|       "Icon": "fa-solid fa-users-gear", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 2, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100001003, | ||||
|       "Module": 2, | ||||
|       "ParentId": 100001, | ||||
|       "Title": "菜单管理", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": "/admin/resource", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Icon": "fa-solid fa-bars", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 3, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100002001, | ||||
|       "ParentId": 100002, | ||||
|       "Module": 2, | ||||
|       "Title": "系统配置", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Target": "_self", | ||||
|       "Href": "/admin/config", | ||||
|       "Icon": "fa-solid fa-gear", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100002002, | ||||
|       "Module": 2, | ||||
|       "ParentId": 100002, | ||||
|       "Title": "字典管理", | ||||
|       "Code": "System", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": "/admin/dict", | ||||
|       "Icon": "fa-solid fa-gear", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100002003, | ||||
|       "Module": 2, | ||||
|       "ParentId": 100002, | ||||
|       "Title": "操作日志", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": "/admin/oplog", | ||||
|       "Icon": "fa-solid fa-edit", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 3, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100002004, | ||||
|       "Module": 2, | ||||
|       "ParentId": 100002, | ||||
|       "Title": "会话管理", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": "/admin/session", | ||||
|       "Icon": "fa-solid fa-person-circle-check", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 4, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 100002005, | ||||
|       "Module": 2, | ||||
|       "ParentId": 100002, | ||||
|       "Title": "硬件信息", | ||||
|       "NavLinkMatch": "All", | ||||
|       "Code": "System", | ||||
|       "Category": "MENU", | ||||
|       "Target": "_self", | ||||
|       "Href": "/admin/hardwareinfo", | ||||
|       "Icon": "fas fa-server", | ||||
|  | ||||
|       "CreateTime": null, | ||||
|       "CreateUser": null, | ||||
|       "CreateUserId": 0, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 4, | ||||
|       "ExtJson": null | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -0,0 +1,845 @@ | ||||
| { | ||||
|   "RECORDS": [ | ||||
|     { | ||||
|       "Id": 501949744332869, | ||||
|       "ParentId": 100001001, | ||||
|       "Module": 2, | ||||
|       "Title": "查询", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502949744332869, | ||||
|       "ParentId": 100001001, | ||||
|       "Module": 2, | ||||
|       "Title": "新增", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502949744332870, | ||||
|       "ParentId": 100001001, | ||||
|       "Module": 2, | ||||
|       "Title": "编辑", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 2, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502949744332871, | ||||
|       "ParentId": 100001001, | ||||
|       "Module": 2, | ||||
|       "Title": "删除", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 3, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502950809124933, | ||||
|       "ParentId": 100001001, | ||||
|       "Module": 2, | ||||
|       "Title": "重置密码", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:52:06.903", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502950980558917, | ||||
|       "ParentId": 100001001, | ||||
|       "Module": 2, | ||||
|       "Title": "授权角色", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:52:48.757", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502951076454469, | ||||
|       "ParentId": 100001001, | ||||
|       "Module": 2, | ||||
|       "Title": "授权资源", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:53:12.17", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": "11/1/2024 14:55:44.917", | ||||
|       "UpdateUser": "SuperAdmin", | ||||
|       "UpdateUserId": 212725263002001, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502951133163589, | ||||
|       "ParentId": 100001001, | ||||
|       "Module": 2, | ||||
|       "Title": "授权Api", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:53:26.017", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": "11/1/2024 15:01:16.98", | ||||
|       "UpdateUser": "SuperAdmin", | ||||
|       "UpdateUserId": 212725263002001, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 513951271813189, | ||||
|       "ParentId": 100001002, | ||||
|       "Module": 2, | ||||
|       "Title": "查询", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:53:59.867", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "Id": 502951271813189, | ||||
|       "ParentId": 100001002, | ||||
|       "Module": 2, | ||||
|       "Title": "新增", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:53:59.867", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502951271813190, | ||||
|       "ParentId": 100001002, | ||||
|       "Module": 2, | ||||
|       "Title": "编辑", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:53:59.867", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 2, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502951271813191, | ||||
|       "ParentId": 100001002, | ||||
|       "Module": 2, | ||||
|       "Title": "删除", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:53:59.867", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 3, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502951661793349, | ||||
|       "ParentId": 100001002, | ||||
|       "Module": 2, | ||||
|       "Title": "授权资源", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:55:35.077", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502952928694341, | ||||
|       "ParentId": 100001002, | ||||
|       "Module": 2, | ||||
|       "Title": "授权用户", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:00:44.377", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502952990781509, | ||||
|       "ParentId": 100001002, | ||||
|       "Module": 2, | ||||
|       "Title": "授权Api", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:00:59.537", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": "11/1/2024 15:01:10.2", | ||||
|       "UpdateUser": "SuperAdmin", | ||||
|       "UpdateUserId": 212725263002001, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 512953216376901, | ||||
|       "ParentId": 100001003, | ||||
|       "Module": 2, | ||||
|       "Title": "查询", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:01:54.617", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "Id": 502953216376901, | ||||
|       "ParentId": 100001003, | ||||
|       "Module": 2, | ||||
|       "Title": "新增", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:01:54.617", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502953216376902, | ||||
|       "ParentId": 100001003, | ||||
|       "Module": 2, | ||||
|       "Title": "编辑", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:01:54.617", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 2, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502953216376903, | ||||
|       "ParentId": 100001003, | ||||
|       "Module": 2, | ||||
|       "Title": "删除", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:01:54.617", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 3, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 512953961062469, | ||||
|       "ParentId": 100002002, | ||||
|       "Module": 2, | ||||
|       "Title": "查询", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:04:56.42", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "Id": 502953961062469, | ||||
|       "ParentId": 100002002, | ||||
|       "Module": 2, | ||||
|       "Title": "新增", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:04:56.42", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502953961062470, | ||||
|       "ParentId": 100002002, | ||||
|       "Module": 2, | ||||
|       "Title": "编辑", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:04:56.42", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 2, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502953961062471, | ||||
|       "ParentId": 100002002, | ||||
|       "Module": 2, | ||||
|       "Title": "删除", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:04:56.42", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 3, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "Id": 532953961062469, | ||||
|       "ParentId": 100002001, | ||||
|       "Module": 2, | ||||
|       "Title": "查询", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:04:56.42", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "Id": 502954345615429, | ||||
|       "ParentId": 100002001, | ||||
|       "Module": 2, | ||||
|       "Code": "System", | ||||
|       "Title": "密码策略保存", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:06:30.307", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502954382938181, | ||||
|       "ParentId": 100002001, | ||||
|       "Module": 2, | ||||
|       "Code": "System", | ||||
|       "Title": "登录策略保存", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:06:39.417", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502954382931181, | ||||
|       "ParentId": 100002001, | ||||
|       "Module": 2, | ||||
|       "Code": "System", | ||||
|       "Title": "页面策略保存", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:06:39.417", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502954382931281, | ||||
|       "ParentId": 100002001, | ||||
|       "Module": 2, | ||||
|       "Title": "网站策略保存", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:06:39.417", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 505954686193733, | ||||
|       "ParentId": 100002003, | ||||
|       "Module": 2, | ||||
|       "Title": "查询", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:07:53.453", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "Id": 502954686193733, | ||||
|       "ParentId": 100002003, | ||||
|       "Module": 2, | ||||
|       "Title": "日志清空", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:07:53.453", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 501954811879493, | ||||
|       "ParentId": 100002004, | ||||
|       "Module": 2, | ||||
|       "Title": "查询", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:08:24.14", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 502954811879493, | ||||
|       "ParentId": 100002004, | ||||
|       "Module": 2, | ||||
|       "Title": "会话强退", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 15:08:24.14", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 0, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 401949744332869, | ||||
|       "ParentId": 100001005, | ||||
|       "Module": 2, | ||||
|       "Title": "查询", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 402949744332869, | ||||
|       "ParentId": 100001005, | ||||
|       "Module": 2, | ||||
|       "Title": "新增", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 402949744332870, | ||||
|       "ParentId": 100001005, | ||||
|       "Module": 2, | ||||
|       "Title": "编辑", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 2, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 402949744332871, | ||||
|       "ParentId": 100001005, | ||||
|       "Module": 2, | ||||
|       "Title": "删除", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 3, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "Id": 301949744332869, | ||||
|       "ParentId": 100001004, | ||||
|       "Module": 2, | ||||
|       "Title": "查询", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 302949744332869, | ||||
|       "ParentId": 100001004, | ||||
|       "Module": 2, | ||||
|       "Title": "新增", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 1, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 302949744332870, | ||||
|       "ParentId": 100001004, | ||||
|       "Module": 2, | ||||
|       "Title": "编辑", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 2, | ||||
|       "ExtJson": null | ||||
|     }, | ||||
|     { | ||||
|       "Id": 302949744332871, | ||||
|       "ParentId": 100001004, | ||||
|       "Module": 2, | ||||
|       "Title": "删除", | ||||
|       "Code": "System", | ||||
|       "Category": "Button", | ||||
|  | ||||
|       "Href": null, | ||||
|       "Icon": null, | ||||
|  | ||||
|       "CreateTime": "11/1/2024 14:47:46.973", | ||||
|       "CreateUser": "SuperAdmin", | ||||
|       "CreateUserId": 212725263002001, | ||||
|       "IsDelete": "0", | ||||
|       "UpdateTime": null, | ||||
|       "UpdateUser": null, | ||||
|       "UpdateUserId": null, | ||||
|       "SortCode": 3, | ||||
|       "ExtJson": null | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 系统配置种子数据 | ||||
| /// </summary> | ||||
| public class SysDictSeedData : ISqlSugarEntitySeedData<SysDict> | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public IEnumerable<SysDict> SeedData() | ||||
|     { | ||||
|         var data = SeedDataUtil.GetSeedData<SysDict>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_dict.json")); | ||||
|  | ||||
|         var assembly = GetType().Assembly; | ||||
|         return SeedDataUtil.GetSeedDataByJson<SysDict>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_dict.json")).Concat(data); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,42 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 职位表种子数据 | ||||
| /// </summary> | ||||
| public class SysOrgSeedData : ISqlSugarEntitySeedData<SysOrg> | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public IEnumerable<SysOrg> SeedData() | ||||
|     { | ||||
|         var data = SeedDataUtil.GetSeedData<SysOrg>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_org.json")); | ||||
|  | ||||
|         var assembly = GetType().Assembly; | ||||
|         return new List<SysOrg>() | ||||
|         { | ||||
|             new SysOrg() | ||||
|             { | ||||
|                   Id=RoleConst.DefaultTenantId, | ||||
|                   Status=true, | ||||
|                   IsDelete=false, | ||||
|                   DirectorId=RoleConst.SuperAdminId, | ||||
|                   Name="Default", | ||||
|                   Code="Diego", | ||||
|                   Category=OrgEnum.COMPANY, | ||||
|                   CreateUserId=RoleConst.SuperAdminId, | ||||
|                   Names="Default", | ||||
|                   SortCode=0 | ||||
|             } | ||||
|         }.Concat(SeedDataUtil.GetSeedDataByJson<SysOrg>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_org.json")).Concat(data)); | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,41 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 职位表种子数据 | ||||
| /// </summary> | ||||
| public class SysPositionSeedData : ISqlSugarEntitySeedData<SysPosition> | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public IEnumerable<SysPosition> SeedData() | ||||
|     { | ||||
|         var data = SeedDataUtil.GetSeedData<SysPosition>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_position.json")); | ||||
|  | ||||
|         var assembly = GetType().Assembly; | ||||
|         return new List<SysPosition>() | ||||
|         { | ||||
|             new SysPosition() | ||||
|             { | ||||
|                   Id=RoleConst.DefaultPositionId, | ||||
|                   Status=true, | ||||
|                   IsDelete=false, | ||||
|                   Name="管理员", | ||||
|                   OrgId=RoleConst.DefaultTenantId, | ||||
|                   Code="ThingsGateway", | ||||
|                   Category=PositionCategoryEnum.HIGH, | ||||
|                   CreateUserId=RoleConst.SuperAdminId, | ||||
|                   SortCode=0 | ||||
|             } | ||||
|         }.Concat(SeedDataUtil.GetSeedDataByJson<SysPosition>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_position.json")).Concat(data)); | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 关系表种子数据 | ||||
| /// </summary> | ||||
| public class SysRelationSeedData : ISqlSugarEntitySeedData<SysRelation> | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public IEnumerable<SysRelation> SeedData() | ||||
|     { | ||||
|         var db = DbContext.Db.GetConnectionScopeWithAttr<SysRelation>().CopyNew(); | ||||
|         if (db.Queryable<SysRelation>().Any(a => a.ObjectId == RoleConst.SuperAdminId)) | ||||
|             return Enumerable.Empty<SysRelation>(); | ||||
|         var data = SeedDataUtil.GetSeedData<SysRelation>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_relation.json")); | ||||
|         var assembly = GetType().Assembly; | ||||
|         return SeedDataUtil.GetSeedDataByJson<SysRelation>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_relation.json")).Concat(data); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 资源表种子数据 | ||||
| /// </summary> | ||||
| public class SysResourceSeedData : ISqlSugarEntitySeedData<SysResource> | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public IEnumerable<SysResource> SeedData() | ||||
|     { | ||||
|         var data1 = SeedDataUtil.GetSeedData<SysResource>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_resource.json")); | ||||
|         var data2 = SeedDataUtil.GetSeedData<SysResource>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_resourcebutton.json")); | ||||
|         var data3 = SeedDataUtil.GetSeedData<SysResource>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_biz_resource.json")); | ||||
|  | ||||
|         var assembly = GetType().Assembly; | ||||
|         return SeedDataUtil.GetSeedDataByJson<SysResource>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_resource.json")) | ||||
|             .Concat(SeedDataUtil.GetSeedDataByJson<SysResource>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_resourcebutton.json"))) | ||||
|             .Concat(SeedDataUtil.GetSeedDataByJson<SysResource>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_biz_resource.json"))) | ||||
|  | ||||
|             .Concat(data1) | ||||
|             .Concat(data2) | ||||
|             .Concat(data3) | ||||
|             ; | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,37 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 角色种子数据 | ||||
| /// </summary> | ||||
| public class SysRoleSeedData : ISqlSugarEntitySeedData<SysRole> | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public IEnumerable<SysRole> SeedData() | ||||
|     { | ||||
|         var data = SeedDataUtil.GetSeedData<SysRole>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_role.json")); | ||||
|         var assembly = GetType().Assembly; | ||||
|         return new List<SysRole>() | ||||
|         { | ||||
|             new SysRole() | ||||
|             { | ||||
|                   Id=RoleConst.SuperAdminRoleId, | ||||
|                   Code=RoleConst.SuperAdmin, | ||||
|                   Name="超级管理员", | ||||
|                   Category=RoleCategoryEnum.Global, | ||||
|                   DefaultDataScope=new(), | ||||
|                   IsDelete=false, | ||||
|                   SortCode=0 | ||||
|             } | ||||
|         }.Concat(SeedDataUtil.GetSeedDataByJson<SysRole>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_role.json")).Concat(data)); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Admin.Application; | ||||
|  | ||||
| /// <summary> | ||||
| /// 用户表种子数据 | ||||
| /// </summary> | ||||
| public class SysUserSeedData : ISqlSugarEntitySeedData<SysUser> | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public IEnumerable<SysUser> SeedData() | ||||
|     { | ||||
|         var data = SeedDataUtil.GetSeedData<SysUser>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_user.json")); | ||||
|         var assembly = GetType().Assembly; | ||||
|         return new List<SysUser>() | ||||
|         { | ||||
|             new SysUser() | ||||
|             { | ||||
|                   Id=RoleConst.SuperAdminId, | ||||
|                   Account=RoleConst.SuperAdmin, | ||||
|                   Password="7DA385A25A98388E", | ||||
|                   OrgId=RoleConst.DefaultTenantId, | ||||
|                   PositionId=RoleConst.DefaultPositionId, | ||||
|                   Status=true, | ||||
|                   IsDelete=false, | ||||
|                   SortCode=0 | ||||
|             } | ||||
|         }.Concat(SeedDataUtil.GetSeedDataByJson<SysUser>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_user.json"))).Concat(data); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||