mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-31 01:12:17 +08:00
Compare commits
827 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f4e31c4902 | |||
| b2d382c2af | |||
| 5d0ad333c9 | |||
| ccf05b8c42 | |||
| 684c1b7140 | |||
| 15e12d083d | |||
| f6937bb4cb | |||
| 8683a85640 | |||
| 7ee6dd4d0e | |||
| 1c7c6074a1 | |||
| af865b62a5 | |||
| 69b430d7cb | |||
| 0956385284 | |||
| 46cf1fa7da | |||
| ac9b42ca4d | |||
| 264335429a | |||
| 323e2bf95a | |||
| 6b413d2962 | |||
| 83e0e33207 | |||
| 827df91241 | |||
| f78f414c77 | |||
| dfb73d1730 | |||
| 92c1d136c2 | |||
| 45428f3e0b | |||
| 139679ed0f | |||
| eed834bd5f | |||
| 422c92cca5 | |||
| 28d699c886 | |||
| f4533c9662 | |||
| ad71a8075b | |||
| 7d39015d97 | |||
| c3498aa5c5 | |||
| 2c3bb5bf91 | |||
| 3c2b6e6488 | |||
| dff0a61182 | |||
| d081f1e312 | |||
| 46809c8d5b | |||
| f1246ee779 | |||
| 03168a5652 | |||
| 0e43d25430 | |||
| 46a9446007 | |||
| 3761223230 | |||
| 836b34e59d | |||
| 3dc1106012 | |||
| 3eb8bf793f | |||
| cd823c4506 | |||
| 36d45ee66f | |||
| 068d36de3d | |||
| e3607b9bc7 | |||
| 76a2ff8e43 | |||
| b3e3da6833 | |||
| fa6c574743 | |||
| e7625a77a8 | |||
| e1b3c80771 | |||
| 3e9228cb65 | |||
| e8b5bb266f | |||
| a6d27f261b | |||
| 1a85571fca | |||
| 9b4f308367 | |||
| aabf49d041 | |||
| 0d22b53b36 | |||
| 8d9c25c195 | |||
| bfd4fe41d3 | |||
| 8400705053 | |||
| b3e72acad8 | |||
| 33884c4c67 | |||
| 45a939571d | |||
| ac4f2a8531 | |||
| 7c09b017c9 | |||
| 02c207d0b8 | |||
| 6d23f59d6a | |||
| 4c55552dec | |||
| 5ba24d11f6 | |||
| bcf557c47e | |||
| cac55a4f79 | |||
| 0d26edf6f0 | |||
| 7a39ef706c | |||
| f5c37c0402 | |||
| 16d571e4ea | |||
| b4b75aa9c5 | |||
| 6ed7784a89 | |||
| d2c7c1109d | |||
| 079a7f7c07 | |||
| 923c6a478c | |||
| 9e0f65cf52 | |||
| eab8184b6b | |||
| 3972a17bd1 | |||
| 34b7b8a10a | |||
| a4da49d8ae | |||
| f8a989fbfc | |||
| 4497b8fa82 | |||
| 0bb7ccd121 | |||
| b90227017c | |||
| 75d941060f | |||
| b37be8aae9 | |||
| f5a471768b | |||
| f6ad6b23cb | |||
| f5b9d3e912 | |||
| 9619c3f55b | |||
| 8c08ebb041 | |||
| 0567319671 | |||
| 760c3be9c7 | |||
| 1084968e99 | |||
| 188c2ffce0 | |||
| f6296e7704 | |||
| bd149f6524 | |||
| 989beb3642 | |||
| 5d4d94a5d8 | |||
| 41eca93f12 | |||
| 946def76c5 | |||
| 24768a4078 | |||
| 7751117dc6 | |||
| f9f286335d | |||
| 1d50da79bb | |||
| 7e78b6eaa3 | |||
| a098c988a7 | |||
| 0b04f32aed | |||
| e14c1db060 | |||
| e109e16610 | |||
| cd3f2b5951 | |||
| fe28e84bf7 | |||
| fe4e3850c9 | |||
| 2a070e14c0 | |||
| 3984f85d57 | |||
| dcd4eb1e49 | |||
| bc62fbadbb | |||
| d17ef0bfda | |||
| 622d08f6f4 | |||
| 2a2fbbf287 | |||
| 77fa6ec58f | |||
| fae4894cea | |||
| b4195459c5 | |||
| 5a40cd3910 | |||
| c6971004f5 | |||
| e6ff46ccac | |||
| a91d0b79a7 | |||
| f0a9aceb13 | |||
| c251f73895 | |||
| 79b8e58412 | |||
| 73be25dcf7 | |||
| fff4d3eae2 | |||
| e614e97312 | |||
| 7ac448d2c4 | |||
| 08f5c88cee | |||
| 779d734dad | |||
| c3ca61a665 | |||
| 832367eb2a | |||
| 929a799f68 | |||
| f1cdb19a59 | |||
| 6f588efea7 | |||
| 333e8ae39f | |||
| 12dc49968a | |||
| 960fc78518 | |||
| 447c393b46 | |||
| a72eb2ba43 | |||
| 6cff72de98 | |||
| 89be5e077a | |||
| 3e1caa4f2e | |||
| 459b71e8cb | |||
| 8c8a957df1 | |||
| 9648b24d58 | |||
| b5752ac9e5 | |||
| 6e8cb1d513 | |||
| 191c3d8b3f | |||
| 657382f929 | |||
| a64ec36744 | |||
| c527af97ca | |||
| 2293646c4c | |||
| 23e215b009 | |||
| a07c50b3f5 | |||
| 9956824d54 | |||
| 6ac5e63684 | |||
| 23b13ff79d | |||
| 4a7742b991 | |||
| 377b904459 | |||
| b30d476a59 | |||
| 3e7629b5b1 | |||
| 32bda21196 | |||
| d27a29151e | |||
| 2b74b35701 | |||
| 30b1cf28cb | |||
| 2d17de5266 | |||
| 01eb10bda3 | |||
| 86376503ac | |||
| 39fd752825 | |||
| 0b6eed5fed | |||
| 9a81c77167 | |||
| 03c375e7d9 | |||
| 645e6d663f | |||
| 2b112d735f | |||
| 4632cf7d58 | |||
| b15262b5ce | |||
| b25456e2fa | |||
| 398bdce676 | |||
| d25c035fc0 | |||
| 486cbe047b | |||
| 06d620ac06 | |||
| aa661972cc | |||
| 74261f4045 | |||
| 50ec26ef03 | |||
| dff6d27e04 | |||
| e3703975ee | |||
| 0cd49a3b18 | |||
| 1ad51e0fa0 | |||
| 405d8390bb | |||
| c4727dca27 | |||
| 19d1169491 | |||
| e95fb840f7 | |||
| 2e0def62ba | |||
| a4d12f8590 | |||
| 8a2c2411af | |||
| e79d4b43cc | |||
| 169b8c74f7 | |||
| 68eb239619 | |||
| 93f4797d44 | |||
| 170453e5ec | |||
| 6e44504378 | |||
| 27c851ba8e | |||
| 03c71a3fe6 | |||
| 95d88cc9a8 | |||
| db909f1235 | |||
| 27c0451d00 | |||
| 6b66552350 | |||
| c873f850bb | |||
| 61b93c81e6 | |||
| 7181390d94 | |||
| 2a9a6301ac | |||
| 22222f0fd7 | |||
| 74380a5d37 | |||
| 43764cf364 | |||
| 6cec736467 | |||
| d9138ddf89 | |||
| 73c2c31ce6 | |||
| f6bd27a0cb | |||
| 47df0ab396 | |||
| 35d7049bc1 | |||
| 438f7fd458 | |||
| 5cc2ae1a32 | |||
| f4af1319c9 | |||
| c966920e29 | |||
| a80b0ff8d4 | |||
| c1ed51767a | |||
| 7b8575b6dd | |||
| 48c41c7fd5 | |||
| 16791f3acb | |||
| 34e00731b5 | |||
| c54bff44cb | |||
| 3b738406c6 | |||
| 3f83119b39 | |||
| 0eae148737 | |||
| 7a648097d2 | |||
| 6de5e799ea | |||
| 36d7575f0b | |||
| 48a49db67b | |||
| a573a6e44f | |||
| d6b016d5e8 | |||
| 645a6d2b98 | |||
| 7dc98096a5 | |||
| 4f5961d7f7 | |||
| 4beb040b3d | |||
| 092da898b8 | |||
| 818c5b466b | |||
| 5dc14ef1bb | |||
| 813a8f42cf | |||
| 47fbbe7134 | |||
| 50d988a62b | |||
| 8e3d929def | |||
| 3022c91eb3 | |||
| 185f0fdb95 | |||
| a46b028275 | |||
| e525d70a03 | |||
| 8b0e7bcaa9 | |||
| c88d2baf42 | |||
| 3d53dd1069 | |||
| d8a4e7080f | |||
| bafdaa460b | |||
| ca5b357e6b | |||
| b28fb497c5 | |||
| 484eb24083 | |||
| ee48623545 | |||
| 48a20c4dce | |||
| 3e15f83275 | |||
| ac88896ab3 | |||
| 4ba50a5556 | |||
| 75dcaca0b6 | |||
| f67c02a0ec | |||
| cff89253c8 | |||
| 40d6b2c090 | |||
| 5337620cee | |||
| 3bdfb7f9af | |||
| d51d419086 | |||
| 70d9e5e83c | |||
| 31d1df1371 | |||
| 96cc85393a | |||
| e5839678b3 | |||
| f7b0798e8c | |||
| 7c3e11a0c0 | |||
| f3baff2aeb | |||
| ba544e3305 | |||
| 37119d1402 | |||
| 2d0a0d5293 | |||
| 7d48a80bf0 | |||
| def1f2d62c | |||
| b5f2475cf9 | |||
| e9a6fc0d6e | |||
| f71456db0e | |||
| 3b332cf7a6 | |||
| 50979e1904 | |||
| 84b154f1b9 | |||
| ca7865664f | |||
| 67537ef617 | |||
| bd365c4e15 | |||
| d47a764848 | |||
| 69d5d5cb21 | |||
| b5150193d0 | |||
| d3309c3a72 | |||
| bbe07f7bdd | |||
| c5b23aaa57 | |||
| ef60a44fbf | |||
| 0bea527004 | |||
| c1b270851a | |||
| b264e5d884 | |||
| 2deced3458 | |||
| 6c274f1644 | |||
| a279a4508c | |||
| 12b5daf305 | |||
| 03c74b2187 | |||
| c3e0bf7aed | |||
| cd815fb7cd | |||
| 8d7084091e | |||
| 98eeec73b6 | |||
| 5542ec2f4b | |||
| 1565bcfee3 | |||
| 0f1181210a | |||
| a27cae1e3f | |||
| c2621836fe | |||
| aae3951638 | |||
| 083c6bfada | |||
| 34fbda8bd8 | |||
| f8aa8934f1 | |||
| c09907610e | |||
| ed31eb6e64 | |||
| 015fb76d4c | |||
| a70f3901f7 | |||
| a3bd4c5bcc | |||
| a64660d0cb | |||
| 1fdc86cc0a | |||
| fe4f0d6dea | |||
| 8116ea7767 | |||
| 2050a96dcf | |||
| cd1d7c9757 | |||
| 24af7bab5f | |||
| 81be655113 | |||
| 5b5e10dee0 | |||
| c73e3d0f18 | |||
| af25b676f8 | |||
| f8b718d99f | |||
| 7792174409 | |||
| 6eced537f4 | |||
| 7315b3d443 | |||
| 567b5ea82e | |||
| 8d4a2c38ca | |||
| 3ffafee82e | |||
| 423cd48e18 | |||
| b169514666 | |||
| c15000d307 | |||
| 27d2eaaaff | |||
| 48e52f2f94 | |||
| 1252a02d66 | |||
| 38c99f4226 | |||
| 906e606e1b | |||
| b47b8b5675 | |||
| 1902a59ebd | |||
| 939d108c56 | |||
| 19a0d2368e | |||
| 984e320074 | |||
| 2d203ded01 | |||
| bc72ece7c0 | |||
| 6393721d7a | |||
| 9cfc2417d6 | |||
| d37433ce9f | |||
| ef3badd400 | |||
| 0986a02a94 | |||
| 38c7c3a477 | |||
| 20a6441d22 | |||
| bfa189e6fb | |||
| 4bc7fd3df8 | |||
| 276ca5dbbd | |||
| 857637ece6 | |||
| 9ce41bc02c | |||
| 2f87de40da | |||
| b47bd5c14d | |||
| a0d92d594c | |||
| c8ebadfc4d | |||
| a3bbbfa87c | |||
| 8c7a65a8b6 | |||
| 4b9ce222ba | |||
| 29ad4f5f34 | |||
| a505a6be92 | |||
| d21a9f0559 | |||
| a5eee5eeec | |||
| fc87cd0360 | |||
| fc97709ebf | |||
| 7bc716cff6 | |||
| 128f44ca55 | |||
| 2ebe0ba47d | |||
| 1fee3632fc | |||
| f66ce68e36 | |||
| 599ea2c245 | |||
| 674c48cce3 | |||
| 18ba92f44b | |||
| e8f7e1af76 | |||
| 9ae19208cb | |||
| ee13b03748 | |||
| 1cbb42a008 | |||
| 4a7258009d | |||
| 84158b2398 | |||
| 460ab12af3 | |||
| 78de211e49 | |||
| 234180f160 | |||
| f25a809dc9 | |||
| 0651f0efc9 | |||
| 8d58adfab4 | |||
| 0b08e59b6e | |||
| 6a60895eac | |||
| 37c10e65a4 | |||
| 695318d96e | |||
| 40a57792ff | |||
| 7c75508b25 | |||
| 7da21e3d6a | |||
| e7ddedea76 | |||
| 7a656595d9 | |||
| 5087399b26 | |||
| 110b40edd7 | |||
| fec09652b7 | |||
| 1dc0042507 | |||
| 746d2297fe | |||
| 7736143928 | |||
| a680a46a22 | |||
| bdd1565221 | |||
| c0e32b46c5 | |||
| 03b3ea3a8b | |||
| 5efeceecdb | |||
| 52587e9cb3 | |||
| c2aa4a4904 | |||
| 0f0ab6b678 | |||
| 6902d4dd2c | |||
| 1b7406ebfa | |||
| 8d729aaf48 | |||
| c2ae9dc4a1 | |||
| 5d5633b31a | |||
| 3677d0317c | |||
| cd8a10d799 | |||
| 3926bb83c1 | |||
| e666396b03 | |||
| ad12a83c0f | |||
| bc66c49fe2 | |||
| 01a4ec8800 | |||
| 637fb83398 | |||
| ff67ace165 | |||
| c1580afadf | |||
| 9a2e68e24e | |||
| 3fbaecbe9e | |||
| a2c96993d3 | |||
| 2208ff1c29 | |||
| 146c9aadbd | |||
| 929219e487 | |||
| 7aca6587af | |||
| a8575bcda9 | |||
| bdaa3c005b | |||
| e6fe1e48f7 | |||
| 33cb41cf42 | |||
| 0df2a9107d | |||
| 7f61793b36 | |||
| 7d9de56a82 | |||
| 18148b15b0 | |||
| ad989337b3 | |||
| 81d32420c0 | |||
| 5538f7498b | |||
| 371d2b3ac0 | |||
| ff17aee372 | |||
| 9e1d525b20 | |||
| f6526c15fa | |||
| be3ae54efe | |||
| 214b804062 | |||
| e1f074f116 | |||
| a96bbf3dfb | |||
| 39d2eee676 | |||
| 8c17b295a2 | |||
| 452ef92d8f | |||
| df49e167a9 | |||
| f3aa240aa9 | |||
| d81f4df8b6 | |||
| 4c2f9f1bbc | |||
| e2080b0603 | |||
| 53eb005531 | |||
| 9ae7b5201c | |||
| 119411a3ce | |||
| 112fc30437 | |||
| dea96649d7 | |||
| 2d136e40c5 | |||
| 11e4d05181 | |||
| 50381dff0c | |||
| 8e40d9a75d | |||
| 8251a9a70e | |||
| 0297ccbf28 | |||
| a9113f3e4c | |||
| 17918c0f1f | |||
| 9509cfa4e2 | |||
| 1e32f9addc | |||
| abe2e1fd3d | |||
| cbf9bdc926 | |||
| ab4a94c32d | |||
| 0ba29c7c40 | |||
| e99c71b8e1 | |||
| b95b035991 | |||
| 5959cab965 | |||
| d4668f2a7e | |||
| e1fb498aa5 | |||
| 6413d6462c | |||
| 3f9fce7841 | |||
| 58d100e636 | |||
| 98d57b6c23 | |||
| 3a8c7784f5 | |||
| 02bfddcf03 | |||
| 55750796b3 | |||
| 692f8ca030 | |||
| fbe99ea4dd | |||
| d34438d90e | |||
| 47e114d43e | |||
| 0b9bba45d7 | |||
| 00ea2f40e2 | |||
| 5183653dbb | |||
| 9f7d8e413e | |||
| 4450d2c8d1 | |||
| b5f7458344 | |||
| da44f1a8fe | |||
| 0317aa1401 | |||
| 90fc48f478 | |||
| a8178cbcd6 | |||
| 91ecd04c27 | |||
| 6743ac9621 | |||
| 5606d980be | |||
| 34300431a4 | |||
| 6f0f4c69ef | |||
| 5fba45e3a4 | |||
| bb140ade7b | |||
| 1316c50a57 | |||
| f14b4f5c54 | |||
| b08eb6bf1f | |||
| 7f5869f805 | |||
| ecadaa9bbc | |||
| 60b9350625 | |||
| d64f099f8e | |||
| 294a78cd95 | |||
| 8294a1a74e | |||
| c403efd762 | |||
| cf8dfca4c0 | |||
| e552caa38f | |||
| ddff13990a | |||
| 27f79ea7a3 | |||
| 04d570197a | |||
| 8e74428023 | |||
| f4c09324b7 | |||
| ee6a12fac2 | |||
| ba429068ad | |||
| 711ccb4504 | |||
| 7d6e078e20 | |||
| d3a7c9e29c | |||
| 10d22bfbc0 | |||
| 724d05d980 | |||
| 690e7f0391 | |||
| a28fc592d1 | |||
| ff7e76ac2b | |||
| f2eb3a180c | |||
| 020a4ad870 | |||
| b5e7cfc069 | |||
| 52f8a6f045 | |||
| 3831d47a23 | |||
| 3a579f2346 | |||
| e85e8dffb3 | |||
| e18d40b0ab | |||
| c308b8e42c | |||
| cafded526f | |||
| 99aa6c99d8 | |||
| e0daee4766 | |||
| bf8c832b32 | |||
| 2d35cbc176 | |||
| 6b51f58856 | |||
| f58c680ced | |||
| 9c6d0b5837 | |||
| d069cbec75 | |||
| 9b252aa8f6 | |||
| a52d06e3cb | |||
| c25c78bfef | |||
| 567f3ab693 | |||
| aa162e12db | |||
| 7169b90c1d | |||
| 5e1001f10f | |||
| 8e26589c80 | |||
| 27ec65650d | |||
| e2345a8285 | |||
| 4ebe20d7f8 | |||
| 8f5872b284 | |||
| 75c6522453 | |||
| ff34cc72cc | |||
| e2fb1080da | |||
| b96c6ad899 | |||
| c1a4fb0904 | |||
| 5c50da27c5 | |||
| 336a9bc675 | |||
| ea3bf37687 | |||
| 79e09f9cb7 | |||
| d73c988073 | |||
| 9641ae7f34 | |||
| fd2c89a480 | |||
| 526f192200 | |||
| 6423953aeb | |||
| 1091ec8a23 | |||
| 2cbc641b2d | |||
| f4d23211e3 | |||
| 1b6086bb0b | |||
| f399e9fcec | |||
| 17cf1ada0e | |||
| 85bd71aefe | |||
| f01afacf34 | |||
| 52e8830605 | |||
| e90c6f4f7b | |||
| eb94ef89b3 | |||
| adc6dccaae | |||
| 855b2e87f9 | |||
| 3c601b808f | |||
| 8652501e95 | |||
| 43b67f67be | |||
| 4fff0b84d8 | |||
| fd73982f86 | |||
| 10191ae5d6 | |||
| b162f71d47 | |||
| a9f34a5783 | |||
| 1e4285b516 | |||
| 44c370a119 | |||
| a1c0b4a399 | |||
| 805f6c0002 | |||
| 5066446053 | |||
| 0c31641ebd | |||
| 343d451752 | |||
| 12ecf0707e | |||
| 899ce1fa18 | |||
| 9ef56f6ede | |||
| 4b3ebc082c | |||
| fb3c8ccc85 | |||
| f620a8737e | |||
| d0d5b237ef | |||
| 11f5adc5a4 | |||
| 6927a0256c | |||
| dbc78343dc | |||
| d8347310ea | |||
| 8d6b3fe95e | |||
| 036e01795e | |||
| 2d7faaa653 | |||
| 5921a88c75 | |||
| d6f4b8736b | |||
| 227f10751c | |||
| 54cc556d17 | |||
| 0d8a26ccb6 | |||
| eb18b1080a | |||
| ffe7b95b9c | |||
| ca097cac1e | |||
| f3a578380a | |||
| beb271b3b9 | |||
| 310316c8e4 | |||
| 7521b0f788 | |||
| 2783e2b28e | |||
| cf26063287 | |||
| 2a61f8939f | |||
| 513a298541 | |||
| 1cbc53b311 | |||
| aed35990bc | |||
| 9179543b7f | |||
| f40ad8bdf9 | |||
| 56ae7ebfa0 | |||
| cf5f7bd866 | |||
| bd6496c1cb | |||
| 3c75c1c49d | |||
| 2b0f8a57bb | |||
| 658b6a79eb | |||
| 42dd00c5ea | |||
| 8fa5cec568 | |||
| 2af4b9569d | |||
| 408b9ce19b | |||
| b2845fba15 | |||
| ba819a5a9c | |||
| bf1359e3d6 | |||
| 2970aaaa47 | |||
| a65bcf582a | |||
| 145387b3df | |||
| f7c586e05b | |||
| 96c78b6be9 | |||
| 46ae37a4a1 | |||
| d56b41bfef | |||
| 68beffff22 | |||
| a774253ec3 | |||
| 9dfc1d3058 | |||
| 049f708cae | |||
| b749a8599f | |||
| 95cf05879d | |||
| 9ac5ae4968 | |||
| 2491f34b3f | |||
| 302d0f1bec | |||
| 5ac43df821 | |||
| 8a1e7ef88b | |||
| b0a4df7ca6 | |||
| 70f41b87ea | |||
| ea96634318 | |||
| cfff459673 | |||
| bba50692b3 | |||
| 595cb03c67 | |||
| fa5817febd | |||
| 9a9fdbdca6 | |||
| 2225e59ca6 | |||
| 5dce30523b | |||
| 965c39ee08 | |||
| 3507517d5b | |||
| 17cce7ec2a | |||
| a2b30b07bb | |||
| ffb7bfda51 | |||
| d36dd8fded | |||
| a5f69db353 | |||
| 4606c63c81 | |||
| 247000718d | |||
| aedde0d433 | |||
| 0b61f2f67c | |||
| b166022cea | |||
| fb88060824 | |||
| f760dbaf80 | |||
| 6050bf611b | |||
| 0b425f09ee | |||
| f18c39ff60 | |||
| 9db30ac11b | |||
| 3c8f606d45 | |||
| 59c08f0850 | |||
| 29a7aa4ca5 | |||
| 8eb75a921e | |||
| 86fb1c392e | |||
| df4e47cc7b | |||
| 6101bedee3 | |||
| 817e86466c | |||
| 710601600a | |||
| 63a934510e | |||
| f3aac307d0 | |||
| e32c887bd7 | |||
| bc37511b15 | |||
| 72652eb814 | |||
| 6e3db107ec | |||
| 81843470b7 | |||
| a539532ae2 | |||
| fc1584e7da | |||
| 3c72a66c1f | |||
| 346ec7ab1f | |||
| 7850b74966 | |||
| f2d2bad52f | |||
| 0786451c79 | |||
| 0291ae7637 | |||
| de76376e97 | |||
| de21179d61 | |||
| b81f3de01d | |||
| be3bae7d57 | |||
| ffb4e21f48 | |||
| 39624d8e1c | |||
| 1378413e54 | |||
| 730aea037c | |||
| 5ae6135429 | |||
| 84703391a3 | |||
| 354705ec5b | |||
| f182eab2dc | |||
| 2828795fb0 | |||
| e6aad21631 | |||
| ff239c467a | |||
| 892f91be3e | |||
| d51d11f460 | |||
| f9233bd36b | |||
| a2ebc070ae | |||
| 480a6a829d | |||
| 47b92a8b2a | |||
| 1a7f679fca | |||
| 8b6b94ea1f | |||
| 0d9c8b3a2d | |||
| 2be97a4543 | |||
| 7b6b86f0f6 | |||
| 6f82242d92 | |||
| 7f301cec58 | |||
| c75cfe9eb6 | |||
| b94664ed87 | |||
| 93fc5e08f6 | |||
| 9bcfd4cd73 | |||
| 2d10110eef | |||
| 78e31fdc40 | |||
| d2b1394950 | |||
| 389b0a005b | |||
| 0b1554c2c6 | |||
| 3b580e2827 | |||
| 595f5ba2d9 | |||
| 78543dac67 | |||
| e9cbc8e945 | |||
| 905e206f9c | |||
| b5b1992e3b | |||
| a7fe0c87a5 | |||
| 5dcbac1c93 | |||
| 67a6d5cea8 | |||
| f6fec06e82 | |||
| 2d74e8966e | |||
| 065877547e | |||
| 12b6a1af35 | |||
| b36f7f3085 | |||
| 2112e86329 | |||
| f2c3569447 | |||
| 1a9fe40b80 | |||
| df021819b4 | |||
| 57749f03b9 | |||
| d97697efde | |||
| bc28719b10 | |||
| db79d17919 | |||
| 4e6654af33 | |||
| 4015c7e1bc | |||
| d30874a5d4 | |||
| f4a97c1740 | |||
| 7cb497929b |
+1
-4
@@ -1,4 +1 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: tnb
|
||||
custom: [ 'https://afdian.com/a/TheTNB' ]
|
||||
custom: [ 'https://afdian.com/a/tnblabs' ]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
name: 🐛 报告问题 (Bug Report)
|
||||
name: ☢️ 报告问题 (Bug Report)
|
||||
description: 创建一个报告以帮助我们改进 (Create a report to help us improve)
|
||||
title: "🐛 [Bug] "
|
||||
labels: [ "☢️ Bug", "bug" ]
|
||||
type: ☢️ Bug
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
@@ -19,48 +18,30 @@ body:
|
||||
options:
|
||||
- label: 我已经搜索了现有的 Issues, Discussions 和 Google (I've searched the existing Issues, Discussions and Google)
|
||||
required: true
|
||||
- label: 我已经阅读了 README 中除版本记录以外的所有内容 (I've read everything in the README except the version notes)
|
||||
- label: 我已经阅读了文档中除版本记录以外的所有内容 (I've read everything in the documentation except the version notes)
|
||||
required: true
|
||||
- label: 我在提问题之前至少花费了 5 分钟来思考和准备 (I spend at least 5 minutes thinking and preparing before asking a question)
|
||||
required: true
|
||||
- label: 我没有改动过面板及系统的任何文件 (I have not changed any files of the panel and system)
|
||||
required: false
|
||||
- label: 这个问题可以被稳定复现 (The problem can be stably reproduced)
|
||||
required: false
|
||||
- label: 问题是在升级之后产生的 (The problem is generated after upgrading)
|
||||
- label: 问题是在更新之后产生的 (The problem is generated after upgrading)
|
||||
required: false
|
||||
- type: dropdown
|
||||
- type: input
|
||||
id: system
|
||||
attributes:
|
||||
label: 操作系统 (Operating System)
|
||||
label: 系统版本 (OS Version)
|
||||
description: |
|
||||
请提供安装面板的操作系统。
|
||||
Please provide the operating system on which the panel is installed.
|
||||
options:
|
||||
- RockyLinux
|
||||
- AlmaLinux
|
||||
- Debian
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: system-version
|
||||
attributes:
|
||||
label: 操作系统版本 (Operating System Version)
|
||||
description: |
|
||||
请提供操作系统的版本号。
|
||||
Please provide the version number of the operating system.
|
||||
placeholder: "9.0"
|
||||
请提供服务器所使用的操作系统及其版本号。
|
||||
Please provide the operating system and its version number used by the server.
|
||||
placeholder: "Ubuntu 24.04"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: 耗子面板版本 (HaoZi Panel Version)
|
||||
label: 面板版本 (Panel Version)
|
||||
description: |
|
||||
请提供面板的版本号。
|
||||
Please provide the version number of the panel.
|
||||
placeholder: "2.0.0"
|
||||
placeholder: "2.4.0"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@@ -94,7 +75,7 @@ body:
|
||||
简明概要地描述您期望发生的事情。
|
||||
Briefly describe what you expect to happen.
|
||||
validations:
|
||||
required: true
|
||||
required: false
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
@@ -104,8 +85,8 @@ body:
|
||||
Please copy and paste any relevant log output.
|
||||
可以把文件拖入这个区域以添加日志文件。
|
||||
Files can be dragged into this area to add log files.
|
||||
面板日志文件可在安装目录 `panel/storage/logs` 中找到。
|
||||
Panel log files can be found in the installation directory `panel/storage/logs`.
|
||||
面板日志文件可在面板目录 `storage/logs` 中找到。
|
||||
Panel log files can be found in the panel directory `storage/logs`.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
|
||||
@@ -2,4 +2,4 @@ blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: ❓ 讨论、问答和非项目问题 (Discussions, questions, and non-project issues)
|
||||
url: https://jq.qq.com/?_wv=1027&k=I1oJKSTH
|
||||
about: 其他不明之处,请移步我们的QQ群 12370907 (For other unclear things, please move to our qq group 12370907)
|
||||
about: 其他不明之处,请移步我们的QQ群 12370907 (For other unclear things, please move to our qq group 12370907)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
name: ✨ 功能请求 (Feature Request)
|
||||
description: 为这个项目提出一个想法 (Suggest an idea for this project)
|
||||
title: "✨ [Feature] "
|
||||
labels: [ "✏️ Feature", "enhancement" ]
|
||||
type: ✨ Feature
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 210 KiB |
@@ -1,55 +0,0 @@
|
||||
name: Backend
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
goarch: [ amd64, arm64 ]
|
||||
fail-fast: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: true
|
||||
go-version: 'stable'
|
||||
- name: Install dependencies
|
||||
run: go mod tidy
|
||||
- name: Wait for frontend build
|
||||
uses: lewagon/wait-on-check-action@v1.3.4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
check-name: 'build (frontend)'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Download frontend
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: frontend.yml
|
||||
name: frontend
|
||||
path: internal/embed/frontend
|
||||
check_artifacts: true
|
||||
- name: Build ${{ matrix.goarch }}
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: linux
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
run: |
|
||||
go build -ldflags '-s -w --extldflags "-static"' -o web-${{ matrix.goarch }} ./cmd/web
|
||||
go build -ldflags '-s -w --extldflags "-static"' -o cli-${{ matrix.goarch }} ./cmd/cli
|
||||
- name: Compress ${{ matrix.goarch }}
|
||||
run: |
|
||||
upx --best --lzma web-${{ matrix.goarch }}
|
||||
upx --best --lzma cli-${{ matrix.goarch }}
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.goarch }}
|
||||
path: |
|
||||
web-${{ matrix.goarch }}
|
||||
cli-${{ matrix.goarch }}
|
||||
@@ -0,0 +1,94 @@
|
||||
name: Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
jobs:
|
||||
frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
run_install: true
|
||||
package_json_file: web/package.json
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: web/pnpm-lock.yaml
|
||||
- name: Build frontend
|
||||
working-directory: web
|
||||
run: |
|
||||
cp .env.production .env
|
||||
cp settings/proxy-config.example.ts settings/proxy-config.ts
|
||||
pnpm build
|
||||
- name: Upload frontend
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend
|
||||
path: web/dist/
|
||||
backend:
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goarch: [ amd64, arm64 ]
|
||||
fail-fast: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: true
|
||||
go-version: 'stable'
|
||||
- name: Install dependencies
|
||||
run: go mod tidy
|
||||
- name: Download frontend
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend
|
||||
path: internal/embed/frontend
|
||||
- name: Set build info
|
||||
run: |
|
||||
echo "VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo '0.0.0')" >> $GITHUB_ENV
|
||||
echo "BUILD_TIME=$(date -u '+%F %T UTC')" >> $GITHUB_ENV
|
||||
echo "COMMIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
echo "GO_VERSION=$(go version | cut -d' ' -f3)" >> $GITHUB_ENV
|
||||
echo "BUILD_ID=${{ github.run_id }}" >> $GITHUB_ENV
|
||||
echo "BUILD_USER=$(whoami)" >> $GITHUB_ENV
|
||||
echo "BUILD_HOST=$(hostname)" >> $GITHUB_ENV
|
||||
- name: Build ${{ matrix.goarch }}
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: linux
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
run: |
|
||||
LDFLAGS="-s -w --extldflags '-static'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.Version=${VERSION}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.BuildTime=${BUILD_TIME}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.CommitHash=${COMMIT_HASH}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.GoVersion=${GO_VERSION}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.BuildID=${BUILD_ID}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.BuildUser=${BUILD_USER}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.BuildHost=${BUILD_HOST}'"
|
||||
go build -ldflags "${LDFLAGS}" -o web-${{ matrix.goarch }} ./cmd/web
|
||||
go build -ldflags "${LDFLAGS}" -o cli-${{ matrix.goarch }} ./cmd/cli
|
||||
- name: Compress ${{ matrix.goarch }}
|
||||
run: |
|
||||
upx --best --lzma web-${{ matrix.goarch }}
|
||||
upx --best --lzma cli-${{ matrix.goarch }}
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: backend-${{ matrix.goarch }}
|
||||
path: |
|
||||
web-${{ matrix.goarch }}
|
||||
cli-${{ matrix.goarch }}
|
||||
@@ -1,26 +0,0 @@
|
||||
name: Codecov
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
jobs:
|
||||
codecov:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: true
|
||||
go-version: 'stable'
|
||||
- name: Install dependencies
|
||||
run: go mod tidy
|
||||
- name: Run tests with coverage
|
||||
run: go test -v -coverprofile="coverage.out" ./...
|
||||
- name: Upload coverage report to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./coverage.out
|
||||
token: ${{ secrets.CODECOV }}
|
||||
@@ -1,42 +0,0 @@
|
||||
name: Frontend
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
jobs:
|
||||
build:
|
||||
name: build (frontend)
|
||||
runs-on: ubuntu-24.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: latest
|
||||
run_install: true
|
||||
package_json_file: web/package.json
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: web/pnpm-lock.yaml
|
||||
- name: Build frontend
|
||||
# We need to run the dev server first to generate the auto-imports files
|
||||
run: |
|
||||
cp .env.production .env
|
||||
cp settings/proxy-config.ts.example settings/proxy-config.ts
|
||||
pnpm dev &
|
||||
sleep 5
|
||||
kill %1
|
||||
pnpm build
|
||||
- name: Upload frontend
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend
|
||||
path: web/dist/ # https://github.com/actions/upload-artifact/issues/541
|
||||
@@ -7,7 +7,7 @@ permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -18,23 +18,33 @@ jobs:
|
||||
with:
|
||||
cache: true
|
||||
go-version: 'stable'
|
||||
- name: Wait for frontend build
|
||||
uses: lewagon/wait-on-check-action@v1.3.4
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
check-name: 'build (frontend)'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Download frontend
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
run_install: true
|
||||
package_json_file: web/package.json
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
workflow: frontend.yml
|
||||
name: frontend
|
||||
path: internal/embed/frontend
|
||||
check_artifacts: true
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: web/pnpm-lock.yaml
|
||||
- name: Build frontend
|
||||
working-directory: web
|
||||
run: |
|
||||
cp .env.production .env
|
||||
cp settings/proxy-config.example.ts settings/proxy-config.ts
|
||||
pnpm build
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
echo "GOVERSION=$(go version | cut -d' ' -f3)" >> $GITHUB_ENV
|
||||
echo "HOSTNAME=$(hostname)" >> $GITHUB_ENV
|
||||
- name: Clear node_modules
|
||||
run: rm -rf node_modules
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
name: Issue Auto Lock
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 */6 * * *"
|
||||
- cron: "*/5 * * * *"
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
issues:
|
||||
types: [ opened ]
|
||||
permissions:
|
||||
@@ -22,4 +19,4 @@ jobs:
|
||||
with:
|
||||
gh_repo: ${{ github.repository }}
|
||||
gh_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue_labels: "⭐ No Star"
|
||||
issue_labels: "⭐ No Star"
|
||||
|
||||
@@ -9,22 +9,8 @@ jobs:
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: ✏️ Feature
|
||||
if: github.event.label.name == '✏️ Feature'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
Hi @${{ github.event.issue.user.login }} 👋
|
||||
|
||||
我们认为您的建议非常有价值!欢迎提交 PR,请包含相应的测试用例、文档等,并确保 CI 通过,感谢和期待您的贡献!
|
||||
We think your suggestion is very valuable! Welcome to submit a PR, please include test cases, documentation, etc., and ensure that the CI is passed, thank you and look forward to your contribution!
|
||||
|
||||

|
||||
- name: ☢️ Bug
|
||||
if: github.event.label.name == '☢️ Bug'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
@@ -35,7 +21,7 @@ jobs:
|
||||
body: |
|
||||
Hi @${{ github.event.issue.user.login }} 👋
|
||||
|
||||
我们认为您的反馈非常有价值!欢迎提交 PR,请包含相应的测试用例、文档等,并确保 CI 通过,感谢和期待您的贡献!
|
||||
We think your feedback is very valuable! Welcome to submit a PR, please include test cases, documentation, etc., and ensure that the CI is passed, thank you and look forward to your contribution!
|
||||
感谢提交 Bug,请确保您的描述清晰,日志完整,最好能提供复现步骤,以便我们更快定位问题并解决。
|
||||
Thanks for submitting a bug, please make sure your description is clear, log is complete, and it is best to provide a reproduction step so that we can locate and solve the problem faster.
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -9,7 +9,7 @@ permissions:
|
||||
jobs:
|
||||
golangci:
|
||||
name: golanci-lint
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -24,25 +24,8 @@ jobs:
|
||||
skip-cache: true
|
||||
version: latest
|
||||
args: --timeout=30m ./...
|
||||
nilaway:
|
||||
runs-on: ubuntu-24.04
|
||||
if: false
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: true
|
||||
go-version: 'stable'
|
||||
- name: Install dependencies
|
||||
run: go mod tidy
|
||||
- name: Install NilAway
|
||||
run: go install go.uber.org/nilaway/cmd/nilaway@latest
|
||||
- name: Run NilAway
|
||||
run: nilaway -include-pkgs="github.com/TheTNB/panel" ./...
|
||||
govulncheck:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -56,24 +39,21 @@ jobs:
|
||||
- name: Run Govulncheck
|
||||
run: govulncheck ./...
|
||||
frontend:
|
||||
runs-on: ubuntu-24.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: latest
|
||||
run_install: true
|
||||
package_json_file: web/package.json
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: web/pnpm-lock.yaml
|
||||
- name: Run pnpm lint
|
||||
run: pnpm lint
|
||||
working-directory: web
|
||||
run: pnpm lint
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
name: Mockery
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
jobs:
|
||||
mockery:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: true
|
||||
go-version: 'stable'
|
||||
- name: Install Mockery
|
||||
run: |
|
||||
go install github.com/vektra/mockery/v2@latest
|
||||
- name: Generate Mocks
|
||||
run: |
|
||||
~/go/bin/mockery
|
||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
||||
name: Commit changes
|
||||
with:
|
||||
commit_message: "chore: update mocks"
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
pull_request:
|
||||
jobs:
|
||||
unit:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -21,4 +21,9 @@ jobs:
|
||||
run: |
|
||||
cp config.example.yml config.yml
|
||||
- name: Run tests
|
||||
run: go test ./...
|
||||
run: sudo go test -v -coverprofile="coverage.out" ./...
|
||||
- name: Upload coverage report to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
file: ./coverage.out
|
||||
token: ${{ secrets.CODECOV }}
|
||||
|
||||
+15
-1
@@ -14,6 +14,13 @@ builds:
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w --extldflags "-static"
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.Version={{ .Version }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.CommitHash={{ .ShortCommit }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.BuildUser={{ .Env.USER }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
|
||||
- id: cli
|
||||
main: ./cmd/cli
|
||||
binary: cli
|
||||
@@ -26,6 +33,13 @@ builds:
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w --extldflags "-static"
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.Version={{ .Version }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.CommitHash={{ .ShortCommit }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.BuildUser={{ .Env.USER }}'
|
||||
- -X 'github.com/tnb-labs/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
|
||||
|
||||
upx:
|
||||
- enabled: true
|
||||
@@ -46,7 +60,7 @@ archives:
|
||||
builds:
|
||||
- web
|
||||
- cli
|
||||
format: zip
|
||||
formats: ["zip", "tar.gz", "tar.xz"]
|
||||
wrap_in_directory: false
|
||||
strip_binary_directory: true
|
||||
files:
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
with-expecter: True
|
||||
disable-version-string: True
|
||||
dir: mocks/{{ replaceAll .InterfaceDirRelative "internal" "" }}
|
||||
mockname: "{{.InterfaceName}}"
|
||||
outpkg: "{{.PackageName}}"
|
||||
filename: "{{.InterfaceName}}.go"
|
||||
all: True
|
||||
packages:
|
||||
github.com/tnb-labs/panel/internal/biz:
|
||||
config:
|
||||
recursive: True
|
||||
@@ -4,131 +4,52 @@
|
||||
|
||||
<h1 align="center" style="font-size: 40px">耗子面板</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/10950" target="_blank"><img src="https://trendshift.io/api/badge/repositories/10950" alt="TheTNB%2Fpanel | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</p>
|
||||
<div align="center">
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/TheTNB/panel/releases"><img src="https://img.shields.io/github/release/TheTNB/panel.svg"></a>
|
||||
<a href="https://github.com/TheTNB/panel/actions"><img src="https://github.com/TheTNB/panel/actions/workflows/test.yml/badge.svg"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/TheTNB/panel"><img src="https://goreportcard.com/badge/github.com/TheTNB/panel"></a>
|
||||
<a href="https://img.shields.io/github/license/TheTNB/panel"><img src="https://img.shields.io/github/license/TheTNB/panel"></a>
|
||||
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2FTheTNB%2Fpanel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2FTheTNB%2Fpanel.svg?type=shield" alt="FOSSA Status"></a>
|
||||
</p>
|
||||
[](https://go.dev/)
|
||||
[](https://github.com/tnb-labs/panel/releases)
|
||||
[](https://github.com/tnb-labs/panel/actions)
|
||||
[](https://goreportcard.com/report/github.com/tnb-labs/panel)
|
||||
[](https://github.com/tnb-labs/panel)
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
耗子面板是使用 Golang + Vue 开发的开源轻量 Linux 服务器运维管理面板。
|
||||
</div>
|
||||
|
||||
交流QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | 论坛:[tom.moe](https://tom.moe) | 赞助:[爱发电](https://afdian.com/a/TheTNB)
|
||||
新一代全能服务器运维管理面板。简单轻量,高效运维。
|
||||
|
||||
官网:[panel.haozi.net](https://panel.haozi.net) | QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | 微信群:[复制此链接](https://work.weixin.qq.com/gm/d8ebf618553398d454e3378695c858b6)
|
||||
|
||||
## 优势
|
||||
|
||||
1. **极低占用:** 在 Debian 下部署面板 + LNMP 环境,内存占用不到 500 MB,遥遥领先于使用容器化的其他面板。
|
||||
2. **低破坏性:** 面板的设计理念是尽可能减少对系统的额外修改,在同类面板中,我们对系统的修改最少。
|
||||
3. **追随时代:** 面板所有组件均走在时代前沿,更新快,功能强大,安全性有保障。
|
||||
4. **高效运维:** 面板界面简洁,操作简单,无需繁琐的配置,即可快速部署各类环境、调整应用设置。
|
||||
5. **离线运行:** 面板运行可不依赖任何外部服务,您甚至可以在部署完成后停止面板进程,不会对已部署服务造成任何影响。
|
||||
6. **久经考验:** 我们生产环境自 2022 年即开始使用,已稳定运行 2 年无事故。
|
||||
7. **开源开放:** 面板开源,您可以自由修改、审计面板源码,安全性有保障。
|
||||
1. **极低占用:** Go 语言开发,安装包小,占用低,单文件运行,不会对系统性能造成影响
|
||||
2. **低破坏性:** 设计为尽可能减少对系统的额外修改,在同类产品中,我们对系统的修改最少
|
||||
3. **追随时代:** 整体设计走在时代前沿,对新系统兼容性好,在同类产品中处于领先地位
|
||||
4. **高效运维:** 功能完善,自定义能力强,既可快速部署小型网站,也可基于定制化需求部署复杂应用
|
||||
5. **离线运行:** 支持离线模式,甚至可以在部署完成后停止面板进程,不会对已有服务造成任何影响
|
||||
6. **安全稳定:** 面板采用业界多种技术保障本体安全性,已在我们的多个生存环境中长期稳定运行
|
||||
7. **全面开源:** 少有的全开源面板,您可以在遵守开源协议的前提下对面板自由修改、二次开发
|
||||
8. **全面开源:** 承诺面板本体未来不会引入任何收费/授权功能,永久免费使用
|
||||
|
||||
## UI 截图
|
||||
|
||||

|
||||
|
||||
## 运行环境
|
||||
## 快速安装
|
||||
|
||||
耗子面板支持 `amd64` | `arm64` 架构下的主流系统,下表中的系统均已测试 LNMP 环境安装。
|
||||
[https://ratpanel.github.io/quickstart/install.html](https://ratpanel.github.io/quickstart/install.html)
|
||||
|
||||
优先建议使用标注**推荐**的系统,无特殊情况不建议使用标注**不推荐**的系统。
|
||||
## 合作伙伴
|
||||
|
||||
不在下表中的其他系统,可自行尝试安装,但不提供技术支持(接受相关 PR 提交)。
|
||||
如果耗子面板对您有帮助,欢迎赞助我们,同时感谢以下支持者/赞助商的支持:
|
||||
|
||||
| 系统 | 版本 | 备注 |
|
||||
|---------------------|-----|-----|
|
||||
| AlmaLinux | 9 | 推荐 |
|
||||
| AlmaLinux | 8 | 不推荐 |
|
||||
| RockyLinux | 9 | |
|
||||
| RockyLinux | 8 | 不推荐 |
|
||||
| CentOS Stream | 9 | 不推荐 |
|
||||
| CentOS Stream | 8 | 不推荐 |
|
||||
| Ubuntu | 24 | 推荐 |
|
||||
| Ubuntu | 22 | |
|
||||
| Debian | 12 | 推荐 |
|
||||
| Debian | 11 | |
|
||||
| OpenCloudOS | 9 | |
|
||||
| TencentOS Server | 4 | |
|
||||
| TencentOS Server | 3.1 | 不推荐 |
|
||||
| Alibaba Cloud Linux | 3.2 | 不推荐 |
|
||||
| Anolis | 8 | 不推荐 |
|
||||
|
||||
随着系统版本的不断更新,我们亦可能会终止部分过于老旧的系统的支持,以保证面板的健壮性。
|
||||
|
||||
## 安装面板
|
||||
|
||||
> **Warning**
|
||||
> 安装面板前,您需要了解 LNMP 环境的基本知识,以及如何处理常见的 LNMP 环境问题,我们不建议 0 基础的用户安装和使用耗子面板。
|
||||
|
||||
如果您的服务器有未挂载的数据盘,可在安装前以`root`用户登录服务器运行以下命令自动挂载,面板安装后不支持跨目录迁移。
|
||||
|
||||
```shell
|
||||
curl -fsLm 10 -o auto_mount.sh https://dl.cdn.haozi.net/panel/auto_mount.sh && bash auto_mount.sh
|
||||
```
|
||||
|
||||
准备就绪后,请以`root`用户登录服务器,运行以下命令安装面板:
|
||||
|
||||
**当前 v2.3.x 为测试版本,欢迎帮助我们测试在不同操作系统下的兼容性。**
|
||||
|
||||
```shell
|
||||
curl -fsLm 10 -o install.sh https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
|
||||
```
|
||||
|
||||
## 卸载面板
|
||||
|
||||
优先建议备份数据重装系统,这样可以保证系统纯净。
|
||||
|
||||
如果您无法重装系统,请以`root`用户登录服务器,执行以下命令卸载面板:
|
||||
|
||||
```shell
|
||||
curl -fsLm 10 -o uninstall.sh https://dl.cdn.haozi.net/panel/uninstall.sh && bash uninstall.sh
|
||||
```
|
||||
|
||||
卸载面板前请务必备份好所有数据,提前卸载面板全部应用。卸载后数据将**无法恢复**!
|
||||
|
||||
## 日常维护
|
||||
|
||||
使用`panel-cli`命令进行日常维护:
|
||||
|
||||
```shell
|
||||
panel-cli
|
||||
```
|
||||
|
||||
在 [文档](https://tom.moe/docs?category=57) 中查看更多使用方法和技巧。
|
||||
|
||||
## 问题反馈
|
||||
|
||||
使用类问题,可在 [Moe Tom](https://tom.moe) 提问或寻求 AI 帮助,亦可在群里寻求付费支持。
|
||||
|
||||
面板自身问题,可在 GitHub 的`Issues`页面提交问题反馈,注意[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)。
|
||||
|
||||
## 赞助商
|
||||
|
||||
如果耗子面板对您有帮助,欢迎[赞助我们](https://afdian.com/a/TheTNB),感谢以下支持者/赞助商的支持:
|
||||
|
||||
**同时接受云资源赞助,可通过QQ群咨询联系**
|
||||
|
||||
### 资金
|
||||
 
|
||||
|
||||
<a href="https://www.weixiaoduo.com/">
|
||||
<img height="80" src=".github/assets/wxd.png" alt="微晓朵">
|
||||
</a>
|
||||
|
||||
### 服务器
|
||||
|
||||
<a href="https://www.dkdun.cn/">
|
||||
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
|
||||
<img height="80" src=".github/assets/dk.png" alt="林枫云">
|
||||
</a>
|
||||
|
||||
### CDN
|
||||
|
||||
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
|
||||
<img height="80" src=".github/assets/sctes.png" alt="无畏云加速">
|
||||
</a>
|
||||
@@ -138,33 +59,25 @@ panel-cli
|
||||
<a href="https://scdn.ddunyun.com/">
|
||||
<img height="80" src=".github/assets/ddunyun.png" alt="盾云SCDN">
|
||||
</a>
|
||||
|
||||
### 赞助商们
|
||||
<a href="https://hongtoutong.com">
|
||||
<img height="80" src=".github/assets/htt.png" alt="红透通">
|
||||
</a>
|
||||
<a href="https://1ms.run">
|
||||
<img height="80" src=".github/assets/1ms.svg" alt="毫秒镜像提供经过审核的 Docker 镜像加速服务">
|
||||
</a>
|
||||
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://afdian.com/a/TheTNB">
|
||||
<img alt="sponsors" src="https://github.com/TheTNB/sponsor/blob/main/sponsors.svg?raw=true"/>
|
||||
<a target="_blank" href="https://afdian.com/a/tnblabs">
|
||||
<img alt="sponsors" src="https://github.com/tnb-labs/sponsor/blob/main/sponsors.svg?raw=true"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 贡献者
|
||||
|
||||
这个项目的存在要归功于所有做出贡献的人,参与贡献请先查看贡献代码部分。
|
||||
|
||||
<a href="https://github.com/TheTNB/panel/graphs/contributors">
|
||||
<img alt="contributors" src="https://contrib.rocks/image?repo=TheTNB/panel"/>
|
||||
</a>
|
||||
|
||||
## Star 历史
|
||||
|
||||
<a href="https://star-history.com/#TheTNB/panel&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date" />
|
||||
</picture>
|
||||
<a href="https://star-history.com/#tnb-labs/panel&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## 免责声明
|
||||
|
||||
严禁使用耗子面板从事任何非法活动,非法站点请勿向我们请求任何形式的技术支持,如果在技术支持过程中发现非法内容,我们将立即停止技术支持并留存相关证据。
|
||||
|
||||
+39
-126
@@ -4,131 +4,52 @@
|
||||
|
||||
<h1 align="center" style="font-size: 40px">Rat Panel</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/10950" target="_blank"><img src="https://trendshift.io/api/badge/repositories/10950" alt="TheTNB%2Fpanel | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</p>
|
||||
<div align="center">
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/TheTNB/panel/releases"><img src="https://img.shields.io/github/release/TheTNB/panel.svg"></a>
|
||||
<a href="https://github.com/TheTNB/panel/actions"><img src="https://github.com/TheTNB/panel/actions/workflows/test.yml/badge.svg"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/TheTNB/panel"><img src="https://goreportcard.com/badge/github.com/TheTNB/panel"></a>
|
||||
<a href="https://img.shields.io/github/license/TheTNB/panel"><img src="https://img.shields.io/github/license/TheTNB/panel"></a>
|
||||
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2FTheTNB%2Fpanel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2FTheTNB%2Fpanel.svg?type=shield" alt="FOSSA Status"></a>
|
||||
</p>
|
||||
[](https://go.dev/)
|
||||
[](https://github.com/tnb-labs/panel/releases)
|
||||
[](https://github.com/tnb-labs/panel/actions)
|
||||
[](https://goreportcard.com/report/github.com/tnb-labs/panel)
|
||||
[](https://github.com/tnb-labs/panel)
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
The Rat Panel is an open source lightweight Linux server operation and maintenance management panel developed using Golang and Vue.
|
||||
</div>
|
||||
|
||||
Communication QQ group: [12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | Forum: [tom.moe](https://tom.moe) | Sponsor: [Open Collective](https://opencollective.com/tnb)
|
||||
A new generation of all-in-one server operation and maintenance management panel. Simple and lightweight, efficient operation and maintenance.
|
||||
|
||||
Website: [panel.haozi.net](https://panel.haozi.net) | QQ group: [12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | WeChat group: [Copy this link](https://work.weixin.qq.com/gm/d8ebf618553398d454e3378695c858b6)
|
||||
|
||||
## Advantages
|
||||
|
||||
1. **Extremely low usage:** Deploying the panel + LNMP environment under Debian, the memory usage is less than 500 MB, far ahead of other panels using containerization.
|
||||
2. **Low destructiveness:** The design concept of the panel is to minimize the additional modifications to the system. Among similar panels, we have made the least modifications to the system.
|
||||
3. **Follow the times:** All components of the panel are at the forefront of the times, updated quickly, powerful functions, and guaranteed security.
|
||||
4. **Efficient operation and maintenance:** The panel is simple and easy to operate, and you can quickly deploy various environments and adjust application settings without complicated configuration.
|
||||
5. **Offline operation:** The panel can run without relying on any external services. You can even stop the panel process after deployment is complete, and it will not affect the deployed services.
|
||||
6. **Tested by time:** We have been using it in production environment since 2022, and it has been running stably for 2 year without any accidents.
|
||||
7. **Open source and open:** The panel is open source, you can freely modify and audit the panel source code, and the security is guaranteed.
|
||||
1. **Extremely low occupancy:** Developed in Go language, small installation package, low occupancy, single file operation, will not affect system performance
|
||||
2. **Low Destructiveness:** Designed to minimize additional modifications to the system, we make the fewest modifications to the system among similar products
|
||||
3. **Follow the Times:** The overall design is at the forefront of the times, with good compatibility with new systems, leading in the same type of products
|
||||
4. **Efficient Operation and Maintenance:** Complete functions, strong customization capabilities, can quickly deploy small websites, and deploy complex applications based on customized requirements
|
||||
5. **Offline Operation:** Support offline mode, and even stop the panel process after deployment, without affecting any existing services
|
||||
6. **Safe and Stable:** The panel adopts a variety of industry technologies to ensure the security of the body, and has been running stably in multiple survival environments for a long time
|
||||
7. **Fully Open Source:** Few fully open source panels, you can freely modify and develop the panel on the premise of complying with the open source agreement
|
||||
8. **Permanently free:** Commit to the panel body will not introduce any charging/authorization functions in the future, and will be permanently free to use
|
||||
|
||||
## UI Screenshots
|
||||
|
||||

|
||||
|
||||
## Operating Environment
|
||||
## Quick Install
|
||||
|
||||
Rat Panel supports mainstream systems under the `amd64` | `arm64` architecture, and the systems in the table below have been tested for LNMP environment installation.
|
||||
[https://ratpanel.github.io/quickstart/install.html](https://ratpanel.github.io/quickstart/install.html)
|
||||
|
||||
It is recommended to use the systems marked **Recommended** first, and not recommended to use the systems marked **Not recommended** without special circumstances.
|
||||
## Partners
|
||||
|
||||
For other systems not listed in the table below, you can try to install them by yourself, but there is no technical support will be provided (accept related PR submissions).
|
||||
If the Rat Panel is helpful to you, welcome to sponsor us, also thanks to the following supporters/sponsors:
|
||||
|
||||
| OS | Version | Note |
|
||||
|---------------------|---------|-----------------|
|
||||
| AlmaLinux | 9 | Recommended |
|
||||
| AlmaLinux | 8 | Not recommended |
|
||||
| RockyLinux | 9 | |
|
||||
| RockyLinux | 8 | Not recommended |
|
||||
| CentOS Stream | 9 | Not recommended |
|
||||
| CentOS Stream | 8 | Not recommended |
|
||||
| Ubuntu | 24 | Recommended |
|
||||
| Ubuntu | 22 | |
|
||||
| Debian | 12 | Recommended |
|
||||
| Debian | 11 | |
|
||||
| OpenCloudOS | 9 | |
|
||||
| TencentOS Server | 4 | |
|
||||
| TencentOS Server | 3.1 | Not recommended |
|
||||
| Alibaba Cloud Linux | 3.2 | Not recommended |
|
||||
| Anolis | 8 | Not recommended |
|
||||
|
||||
As system versions are constantly updated, we may also terminate support for some older systems to ensure the robustness of the panel.
|
||||
|
||||
## Install Panel
|
||||
|
||||
> **Warning**
|
||||
> Before installing the panel, you need to understand the basic knowledge of the LNMP environment and how to deal with common LNMP environment problems. We are not recommended for users with zero basic knowledge to install and use Rat Panel.
|
||||
|
||||
If your server has an unmounted data disk, you can run the following command as the `root` user to automatically mount it before installation. The panel does not support cross-directory migration after installation.
|
||||
|
||||
```shell
|
||||
curl -fsLm 10 -o auto_mount.sh https://dl.cdn.haozi.net/panel/auto_mount.sh && bash auto_mount.sh
|
||||
```
|
||||
|
||||
After you are ready, log in to the server as the `root` user and run the following command to install the panel:
|
||||
|
||||
**Current v2.3.x is a test version, welcome to help us test compatibility on different operating systems.**
|
||||
|
||||
```shell
|
||||
curl -fsLm 10 -o install.sh https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
|
||||
```
|
||||
|
||||
## Uninstall Panel
|
||||
|
||||
Recommended to back up data and reinstall the system first, so that the system can be kept clean.
|
||||
|
||||
If you are unable to reinstall the system, log in to the server as the `root` user and execute the following command to uninstall the panel:
|
||||
|
||||
```shell
|
||||
curl -fsLm 10 -o uninstall.sh https://dl.cdn.haozi.net/panel/uninstall.sh && bash uninstall.sh
|
||||
```
|
||||
|
||||
Before uninstalling the panel, please be sure to back up all data and uninstall all panel plugins in advance. The data will **not be recoverable** after uninstallation!
|
||||
|
||||
## Daily Maintenance
|
||||
|
||||
Use `panel-cli` command for daily maintenance:
|
||||
|
||||
```shell
|
||||
panel-cli
|
||||
```
|
||||
|
||||
See more usage methods and tips in [Document](https://tom.moe/docs?category=57).
|
||||
|
||||
## Feedback
|
||||
|
||||
For usage issues, you can ask questions in the [Moe Tom](https://tom.moe) or seek AI help. You can also seek paid support in the group.
|
||||
|
||||
For issues with the panel itself, you can submit feedback on the `Issues` page of GitHub. Please note [the wisdom of asking questions](http://www.catb.org/~esr/faqs/smart-questions.html).
|
||||
|
||||
## Sponsor
|
||||
|
||||
If the Rat Panel is helpful to you, welcome to [sponsor us](https://opencollective.com/tnb), thanks to the following supporters/sponsors:
|
||||
|
||||
**Accept cloud resources and financial sponsorship, you can contact us through QQ group**
|
||||
|
||||
### Financial
|
||||
 
|
||||
|
||||
<a href="https://www.weixiaoduo.com/">
|
||||
<img height="80" src=".github/assets/wxd.png" alt="微晓朵">
|
||||
</a>
|
||||
|
||||
### Server
|
||||
|
||||
<a href="https://www.dkdun.cn/">
|
||||
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
|
||||
<img height="80" src=".github/assets/dk.png" alt="林枫云">
|
||||
</a>
|
||||
|
||||
### CDN
|
||||
|
||||
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
|
||||
<img height="80" src=".github/assets/sctes.png" alt="无畏云加速">
|
||||
</a>
|
||||
@@ -138,33 +59,25 @@ If the Rat Panel is helpful to you, welcome to [sponsor us](https://opencollecti
|
||||
<a href="https://scdn.ddunyun.com/">
|
||||
<img height="80" src=".github/assets/ddunyun.png" alt="盾云SCDN">
|
||||
</a>
|
||||
|
||||
### Sponsors
|
||||
<a href="https://hongtoutong.com">
|
||||
<img height="80" src=".github/assets/htt.png" alt="红透通">
|
||||
</a>
|
||||
<a href="https://1ms.run">
|
||||
<img height="80" src=".github/assets/1ms.svg" alt="毫秒镜像提供经过审核的 Docker 镜像加速服务">
|
||||
</a>
|
||||
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://afdian.com/a/TheTNB">
|
||||
<img alt="sponsors" src="https://github.com/TheTNB/sponsor/blob/main/sponsors.svg?raw=true"/>
|
||||
<a target="_blank" href="https://afdian.com/a/tnblabs">
|
||||
<img alt="sponsors" src="https://github.com/tnb-labs/sponsor/blob/main/sponsors.svg?raw=true"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Contributor
|
||||
|
||||
This project owes its existence to all those who have contributed. To contribute, please check the contributed code section first.
|
||||
|
||||
<a href="https://github.com/TheTNB/panel/graphs/contributors">
|
||||
<img alt="contributors" src="https://contrib.rocks/image?repo=TheTNB/panel"/>
|
||||
</a>
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://star-history.com/#TheTNB/panel&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=TheTNB/panel&type=Date" />
|
||||
</picture>
|
||||
<a href="https://star-history.com/#tnb-labs/panel&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
## Disclaimer
|
||||
|
||||
It is strictly prohibited to use the Rat Panel to engage in any illegal activities. Please do not request any form of technical support from us for illegal sites. If illegal content is discovered during the technical support process, we will immediately stop technical support and retain relevant evidence.
|
||||
|
||||
+14
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2022 - now HaoZi Technology Co., Ltd.
|
||||
Copyright (C) 2022 - now Rat Technology Co., Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
@@ -17,11 +17,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
_ "time/tzdata"
|
||||
|
||||
"github.com/TheTNB/panel/internal/bootstrap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
bootstrap.BootCli()
|
||||
if os.Geteuid() != 0 {
|
||||
panic("panel must run as root")
|
||||
}
|
||||
|
||||
cli, err := initCli()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err = cli.Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//go:build wireinject
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/apps"
|
||||
"github.com/tnb-labs/panel/internal/bootstrap"
|
||||
"github.com/tnb-labs/panel/internal/data"
|
||||
"github.com/tnb-labs/panel/internal/route"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
)
|
||||
|
||||
// initCli init command line.
|
||||
func initCli() (*app.Cli, error) {
|
||||
panic(wire.Build(bootstrap.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, apps.ProviderSet, app.NewCli))
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//go:build !wireinject
|
||||
// +build !wireinject
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/apps/benchmark"
|
||||
"github.com/tnb-labs/panel/internal/apps/docker"
|
||||
"github.com/tnb-labs/panel/internal/apps/fail2ban"
|
||||
"github.com/tnb-labs/panel/internal/apps/frp"
|
||||
"github.com/tnb-labs/panel/internal/apps/gitea"
|
||||
"github.com/tnb-labs/panel/internal/apps/memcached"
|
||||
"github.com/tnb-labs/panel/internal/apps/minio"
|
||||
"github.com/tnb-labs/panel/internal/apps/mysql"
|
||||
"github.com/tnb-labs/panel/internal/apps/nginx"
|
||||
"github.com/tnb-labs/panel/internal/apps/php74"
|
||||
"github.com/tnb-labs/panel/internal/apps/php80"
|
||||
"github.com/tnb-labs/panel/internal/apps/php81"
|
||||
"github.com/tnb-labs/panel/internal/apps/php82"
|
||||
"github.com/tnb-labs/panel/internal/apps/php83"
|
||||
"github.com/tnb-labs/panel/internal/apps/php84"
|
||||
"github.com/tnb-labs/panel/internal/apps/phpmyadmin"
|
||||
"github.com/tnb-labs/panel/internal/apps/podman"
|
||||
"github.com/tnb-labs/panel/internal/apps/postgresql"
|
||||
"github.com/tnb-labs/panel/internal/apps/pureftpd"
|
||||
"github.com/tnb-labs/panel/internal/apps/redis"
|
||||
"github.com/tnb-labs/panel/internal/apps/rsync"
|
||||
"github.com/tnb-labs/panel/internal/apps/s3fs"
|
||||
"github.com/tnb-labs/panel/internal/apps/supervisor"
|
||||
"github.com/tnb-labs/panel/internal/apps/toolbox"
|
||||
"github.com/tnb-labs/panel/internal/bootstrap"
|
||||
"github.com/tnb-labs/panel/internal/data"
|
||||
"github.com/tnb-labs/panel/internal/route"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
)
|
||||
|
||||
import (
|
||||
_ "time/tzdata"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
|
||||
// initCli init command line.
|
||||
func initCli() (*app.Cli, error) {
|
||||
koanf, err := bootstrap.NewConf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger := bootstrap.NewLog(koanf)
|
||||
db, err := bootstrap.NewDB(koanf, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheRepo := data.NewCacheRepo(db)
|
||||
queue := bootstrap.NewQueue()
|
||||
taskRepo := data.NewTaskRepo(db, logger, queue)
|
||||
appRepo := data.NewAppRepo(db, cacheRepo, taskRepo)
|
||||
userRepo := data.NewUserRepo(db)
|
||||
settingRepo := data.NewSettingRepo(db, koanf, taskRepo)
|
||||
databaseServerRepo := data.NewDatabaseServerRepo(db, logger)
|
||||
databaseUserRepo := data.NewDatabaseUserRepo(db, databaseServerRepo)
|
||||
databaseRepo := data.NewDatabaseRepo(db, databaseServerRepo, databaseUserRepo)
|
||||
certRepo := data.NewCertRepo(db)
|
||||
certAccountRepo := data.NewCertAccountRepo(db, userRepo)
|
||||
websiteRepo := data.NewWebsiteRepo(db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo)
|
||||
backupRepo := data.NewBackupRepo(db, settingRepo, websiteRepo)
|
||||
cliService := service.NewCliService(koanf, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo)
|
||||
cli := route.NewCli(cliService)
|
||||
command := bootstrap.NewCli(cli)
|
||||
gormigrate := bootstrap.NewMigrate(db)
|
||||
benchmarkApp := benchmark.NewApp()
|
||||
dockerApp := docker.NewApp()
|
||||
fail2banApp := fail2ban.NewApp(websiteRepo)
|
||||
frpApp := frp.NewApp()
|
||||
giteaApp := gitea.NewApp()
|
||||
minioApp := minio.NewApp()
|
||||
memcachedApp := memcached.NewApp()
|
||||
mysqlApp := mysql.NewApp(settingRepo)
|
||||
nginxApp := nginx.NewApp()
|
||||
php74App := php74.NewApp(taskRepo)
|
||||
php80App := php80.NewApp(taskRepo)
|
||||
php81App := php81.NewApp(taskRepo)
|
||||
php82App := php82.NewApp(taskRepo)
|
||||
php83App := php83.NewApp(taskRepo)
|
||||
php84App := php84.NewApp(taskRepo)
|
||||
phpmyadminApp := phpmyadmin.NewApp()
|
||||
podmanApp := podman.NewApp()
|
||||
postgresqlApp := postgresql.NewApp()
|
||||
pureftpdApp := pureftpd.NewApp()
|
||||
redisApp := redis.NewApp()
|
||||
rsyncApp := rsync.NewApp()
|
||||
s3fsApp := s3fs.NewApp(settingRepo)
|
||||
supervisorApp := supervisor.NewApp()
|
||||
toolboxApp := toolbox.NewApp()
|
||||
loader := bootstrap.NewLoader(benchmarkApp, dockerApp, fail2banApp, frpApp, giteaApp, minioApp, memcachedApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp, toolboxApp)
|
||||
appCli := app.NewCli(command, gormigrate, loader)
|
||||
return appCli, nil
|
||||
}
|
||||
+18
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2022 - now HaoZi Technology Co., Ltd.
|
||||
Copyright (C) 2022 - now Rat Technology Co., Ltd.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
@@ -17,11 +17,25 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime/debug"
|
||||
_ "time/tzdata"
|
||||
|
||||
"github.com/TheTNB/panel/internal/bootstrap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
bootstrap.BootWeb()
|
||||
if os.Geteuid() != 0 {
|
||||
panic("panel must run as root")
|
||||
}
|
||||
|
||||
debug.SetGCPercent(10)
|
||||
debug.SetMemoryLimit(128 << 20)
|
||||
|
||||
web, err := initWeb()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err = web.Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
//go:build wireinject
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/apps"
|
||||
"github.com/tnb-labs/panel/internal/bootstrap"
|
||||
"github.com/tnb-labs/panel/internal/data"
|
||||
"github.com/tnb-labs/panel/internal/http/middleware"
|
||||
"github.com/tnb-labs/panel/internal/job"
|
||||
"github.com/tnb-labs/panel/internal/route"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
)
|
||||
|
||||
// initWeb init application.
|
||||
func initWeb() (*app.Web, error) {
|
||||
panic(wire.Build(bootstrap.ProviderSet, middleware.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, apps.ProviderSet, job.ProviderSet, app.NewWeb))
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//go:build !wireinject
|
||||
// +build !wireinject
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/apps/benchmark"
|
||||
"github.com/tnb-labs/panel/internal/apps/docker"
|
||||
"github.com/tnb-labs/panel/internal/apps/fail2ban"
|
||||
"github.com/tnb-labs/panel/internal/apps/frp"
|
||||
"github.com/tnb-labs/panel/internal/apps/gitea"
|
||||
"github.com/tnb-labs/panel/internal/apps/memcached"
|
||||
"github.com/tnb-labs/panel/internal/apps/minio"
|
||||
"github.com/tnb-labs/panel/internal/apps/mysql"
|
||||
"github.com/tnb-labs/panel/internal/apps/nginx"
|
||||
"github.com/tnb-labs/panel/internal/apps/php74"
|
||||
"github.com/tnb-labs/panel/internal/apps/php80"
|
||||
"github.com/tnb-labs/panel/internal/apps/php81"
|
||||
"github.com/tnb-labs/panel/internal/apps/php82"
|
||||
"github.com/tnb-labs/panel/internal/apps/php83"
|
||||
"github.com/tnb-labs/panel/internal/apps/php84"
|
||||
"github.com/tnb-labs/panel/internal/apps/phpmyadmin"
|
||||
"github.com/tnb-labs/panel/internal/apps/podman"
|
||||
"github.com/tnb-labs/panel/internal/apps/postgresql"
|
||||
"github.com/tnb-labs/panel/internal/apps/pureftpd"
|
||||
"github.com/tnb-labs/panel/internal/apps/redis"
|
||||
"github.com/tnb-labs/panel/internal/apps/rsync"
|
||||
"github.com/tnb-labs/panel/internal/apps/s3fs"
|
||||
"github.com/tnb-labs/panel/internal/apps/supervisor"
|
||||
"github.com/tnb-labs/panel/internal/apps/toolbox"
|
||||
"github.com/tnb-labs/panel/internal/bootstrap"
|
||||
"github.com/tnb-labs/panel/internal/data"
|
||||
"github.com/tnb-labs/panel/internal/http/middleware"
|
||||
"github.com/tnb-labs/panel/internal/job"
|
||||
"github.com/tnb-labs/panel/internal/route"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
)
|
||||
|
||||
import (
|
||||
_ "time/tzdata"
|
||||
)
|
||||
|
||||
// Injectors from wire.go:
|
||||
|
||||
// initWeb init application.
|
||||
func initWeb() (*app.Web, error) {
|
||||
koanf, err := bootstrap.NewConf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger := bootstrap.NewLog(koanf)
|
||||
db, err := bootstrap.NewDB(koanf, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manager, err := bootstrap.NewSession(koanf, db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheRepo := data.NewCacheRepo(db)
|
||||
queue := bootstrap.NewQueue()
|
||||
taskRepo := data.NewTaskRepo(db, logger, queue)
|
||||
appRepo := data.NewAppRepo(db, cacheRepo, taskRepo)
|
||||
middlewares := middleware.NewMiddlewares(koanf, logger, manager, appRepo)
|
||||
userRepo := data.NewUserRepo(db)
|
||||
userService := service.NewUserService(koanf, manager, userRepo)
|
||||
databaseServerRepo := data.NewDatabaseServerRepo(db, logger)
|
||||
databaseUserRepo := data.NewDatabaseUserRepo(db, databaseServerRepo)
|
||||
databaseRepo := data.NewDatabaseRepo(db, databaseServerRepo, databaseUserRepo)
|
||||
certRepo := data.NewCertRepo(db)
|
||||
certAccountRepo := data.NewCertAccountRepo(db, userRepo)
|
||||
websiteRepo := data.NewWebsiteRepo(db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo)
|
||||
settingRepo := data.NewSettingRepo(db, koanf, taskRepo)
|
||||
cronRepo := data.NewCronRepo(db)
|
||||
backupRepo := data.NewBackupRepo(db, settingRepo, websiteRepo)
|
||||
dashboardService := service.NewDashboardService(koanf, taskRepo, websiteRepo, appRepo, settingRepo, cronRepo, backupRepo)
|
||||
taskService := service.NewTaskService(taskRepo)
|
||||
websiteService := service.NewWebsiteService(websiteRepo, settingRepo)
|
||||
databaseService := service.NewDatabaseService(databaseRepo)
|
||||
databaseServerService := service.NewDatabaseServerService(databaseServerRepo)
|
||||
databaseUserService := service.NewDatabaseUserService(databaseUserRepo)
|
||||
backupService := service.NewBackupService(backupRepo)
|
||||
certService := service.NewCertService(certRepo)
|
||||
certDNSRepo := data.NewCertDNSRepo(db)
|
||||
certDNSService := service.NewCertDNSService(certDNSRepo)
|
||||
certAccountService := service.NewCertAccountService(certAccountRepo)
|
||||
appService := service.NewAppService(appRepo, cacheRepo, settingRepo)
|
||||
cronService := service.NewCronService(cronRepo)
|
||||
processService := service.NewProcessService()
|
||||
safeRepo := data.NewSafeRepo()
|
||||
safeService := service.NewSafeService(safeRepo)
|
||||
firewallService := service.NewFirewallService()
|
||||
sshRepo := data.NewSSHRepo(db)
|
||||
sshService := service.NewSSHService(sshRepo)
|
||||
containerRepo := data.NewContainerRepo()
|
||||
containerService := service.NewContainerService(containerRepo)
|
||||
containerComposeRepo := data.NewContainerComposeRepo()
|
||||
containerComposeService := service.NewContainerComposeService(containerComposeRepo)
|
||||
containerNetworkRepo := data.NewContainerNetworkRepo()
|
||||
containerNetworkService := service.NewContainerNetworkService(containerNetworkRepo)
|
||||
containerImageRepo := data.NewContainerImageRepo()
|
||||
containerImageService := service.NewContainerImageService(containerImageRepo)
|
||||
containerVolumeRepo := data.NewContainerVolumeRepo()
|
||||
containerVolumeService := service.NewContainerVolumeService(containerVolumeRepo)
|
||||
fileService := service.NewFileService(taskRepo)
|
||||
monitorRepo := data.NewMonitorRepo(db, settingRepo)
|
||||
monitorService := service.NewMonitorService(settingRepo, monitorRepo)
|
||||
settingService := service.NewSettingService(settingRepo)
|
||||
systemctlService := service.NewSystemctlService()
|
||||
benchmarkApp := benchmark.NewApp()
|
||||
dockerApp := docker.NewApp()
|
||||
fail2banApp := fail2ban.NewApp(websiteRepo)
|
||||
frpApp := frp.NewApp()
|
||||
giteaApp := gitea.NewApp()
|
||||
minioApp := minio.NewApp()
|
||||
memcachedApp := memcached.NewApp()
|
||||
mysqlApp := mysql.NewApp(settingRepo)
|
||||
nginxApp := nginx.NewApp()
|
||||
php74App := php74.NewApp(taskRepo)
|
||||
php80App := php80.NewApp(taskRepo)
|
||||
php81App := php81.NewApp(taskRepo)
|
||||
php82App := php82.NewApp(taskRepo)
|
||||
php83App := php83.NewApp(taskRepo)
|
||||
php84App := php84.NewApp(taskRepo)
|
||||
phpmyadminApp := phpmyadmin.NewApp()
|
||||
podmanApp := podman.NewApp()
|
||||
postgresqlApp := postgresql.NewApp()
|
||||
pureftpdApp := pureftpd.NewApp()
|
||||
redisApp := redis.NewApp()
|
||||
rsyncApp := rsync.NewApp()
|
||||
s3fsApp := s3fs.NewApp(settingRepo)
|
||||
supervisorApp := supervisor.NewApp()
|
||||
toolboxApp := toolbox.NewApp()
|
||||
loader := bootstrap.NewLoader(benchmarkApp, dockerApp, fail2banApp, frpApp, giteaApp, minioApp, memcachedApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp, toolboxApp)
|
||||
http := route.NewHttp(userService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, loader)
|
||||
wsService := service.NewWsService(koanf, sshRepo)
|
||||
ws := route.NewWs(wsService)
|
||||
mux, err := bootstrap.NewRouter(middlewares, http, ws)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server, err := bootstrap.NewHttp(koanf, mux)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gormigrate := bootstrap.NewMigrate(db)
|
||||
jobs := job.NewJobs(db, logger, settingRepo, certRepo, backupRepo, cacheRepo, taskRepo)
|
||||
cron, err := bootstrap.NewCron(koanf, logger, jobs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
validation := bootstrap.NewValidator(db)
|
||||
web := app.NewWeb(koanf, mux, server, gormigrate, cron, queue, validation)
|
||||
return web, nil
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
# 警告:未经指导私自修改此配置文件可能导致严重后果
|
||||
# Warning: Modifying this configuration file without guidance may cause serious consequences
|
||||
app:
|
||||
debug: false
|
||||
key: a-long-string-with-32-characters
|
||||
|
||||
@@ -1,135 +1,82 @@
|
||||
module github.com/TheTNB/panel
|
||||
module github.com/tnb-labs/panel
|
||||
|
||||
go 1.23
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/bddjr/hlfhr v1.3.8
|
||||
github.com/beevik/ntp v1.4.3
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/expr-lang/expr v1.16.9
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/expr-lang/expr v1.17.2
|
||||
github.com/go-chi/chi/v5 v5.2.1
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.3
|
||||
github.com/go-playground/locales v0.14.1
|
||||
github.com/go-playground/universal-translator v0.18.1
|
||||
github.com/go-playground/validator/v10 v10.22.1
|
||||
github.com/go-rat/chix v1.1.4
|
||||
github.com/go-rat/chix v1.1.5
|
||||
github.com/go-rat/gormstore v1.0.6
|
||||
github.com/go-rat/sessions v1.0.11
|
||||
github.com/go-rat/utils v1.0.3
|
||||
github.com/go-resty/resty/v2 v2.15.3
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/goccy/go-yaml v1.12.0
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/go-rat/sessions v1.1.0
|
||||
github.com/go-rat/utils v1.1.4
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/go-sql-driver/mysql v1.9.1
|
||||
github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a
|
||||
github.com/gomodule/redigo v1.9.2
|
||||
github.com/google/wire v0.6.0
|
||||
github.com/gookit/validate v1.5.4
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/knadh/koanf/parsers/yaml v0.1.0
|
||||
github.com/knadh/koanf/providers/file v1.1.2
|
||||
github.com/knadh/koanf/v2 v2.1.1
|
||||
github.com/knadh/koanf/v2 v2.1.2
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/libdns/alidns v1.0.3
|
||||
github.com/libdns/cloudflare v0.1.1
|
||||
github.com/libdns/dnspod v0.0.3
|
||||
github.com/libdns/libdns v0.2.2
|
||||
github.com/libdns/tencentcloud v1.0.0
|
||||
github.com/mholt/acmez/v2 v2.0.3
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8
|
||||
github.com/libdns/cloudflare v0.1.3
|
||||
github.com/libdns/huaweicloud v0.3.1
|
||||
github.com/libdns/libdns v0.2.3
|
||||
github.com/libdns/tencentcloud v1.2.0
|
||||
github.com/mholt/acmez/v3 v3.1.1
|
||||
github.com/ncruces/go-sqlite3 v0.24.1
|
||||
github.com/ncruces/go-sqlite3/gormlite v0.24.0
|
||||
github.com/orandin/slog-gorm v1.4.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samber/lo v1.47.0
|
||||
github.com/samber/lo v1.49.1
|
||||
github.com/sethvargo/go-limiter v1.0.0
|
||||
github.com/shirou/gopsutil v2.21.11+incompatible
|
||||
github.com/spf13/cast v1.7.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20241013191809-e73b7dd454e8
|
||||
github.com/urfave/cli/v3 v3.0.0-alpha9.1
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.28.0
|
||||
golang.org/x/net v0.30.0
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/spf13/cast v1.7.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20250225174229-c03497ddaef6
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/net v0.38.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/gorm v1.25.12
|
||||
moul.io/zapgorm2 v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/bodgit/plumbing v1.2.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.3.0 // indirect
|
||||
github.com/bodgit/windows v1.0.0 // indirect
|
||||
github.com/connesc/cipherio v0.2.1 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.10.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-rat/securecookie v1.0.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/gofiber/schema v1.2.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-rat/securecookie v1.1.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/gofiber/schema v1.3.0 // indirect
|
||||
github.com/gookit/filter v1.2.2 // indirect
|
||||
github.com/gookit/goutil v0.6.18 // indirect
|
||||
github.com/jaevor/go-nanoid v1.4.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.1 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.597 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1096 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||
github.com/tklauser/numcpus v0.9.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect
|
||||
go.opentelemetry.io/otel v1.30.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.30.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.30.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.30.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.60.1 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/sqlite v1.32.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/mholt/acmez/v2 => github.com/TheTNB/acmez/v2 v2.0.0-20241012163130-5833d84639e0
|
||||
replace github.com/mholt/acmez/v3 => github.com/tnb-labs/acmez/v3 v3.0.0-20250328123917-f204dcee9d09
|
||||
|
||||
@@ -1,583 +1,227 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/TheTNB/acmez/v2 v2.0.0-20241012163130-5833d84639e0 h1:o66ZhpjXVs7oi3VyiZvlpp9StFFe4KTeklzB/T4s59Q=
|
||||
github.com/TheTNB/acmez/v2 v2.0.0-20241012163130-5833d84639e0/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/bddjr/hlfhr v1.3.8 h1:QQ6KYgtnBbvYvCWuu/tOnBZamKAPtJzesj2qbjgyn7o=
|
||||
github.com/bddjr/hlfhr v1.3.8/go.mod h1:oyIv4Q9JpCgZFdtH3KyTNWp7YYRWl4zl8k4ozrMAB4g=
|
||||
github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho=
|
||||
github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM=
|
||||
github.com/bodgit/plumbing v1.2.0/go.mod h1:b9TeRi7Hvc6Y05rjm8VML3+47n4XTZPtQ/5ghqic2n8=
|
||||
github.com/bodgit/sevenzip v1.3.0 h1:1ljgELgtHqvgIp8W8kgeEGHIWP4ch3xGI8uOBZgLVKY=
|
||||
github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzOJJ8FBlM=
|
||||
github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA=
|
||||
github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=
|
||||
github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
|
||||
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/expr-lang/expr v1.17.2 h1:o0A99O/Px+/DTjEnQiodAgOIK9PPxL8DtXhBRKC+Iso=
|
||||
github.com/expr-lang/expr v1.17.2/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
|
||||
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.3 h1:ei3Vq/rpPI/jCJY9mRHJAKg5vU+EhZyWhBAkaAomQuw=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.3/go.mod h1:VJ9FIOBAur+NmQ8c4tDVwOuiJcgupTG105FexPFrXzA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-rat/chix v1.1.4 h1:YVDwF4vrlkm5hmlDGWKoiWPgP98pJooBvhsto8UWq+o=
|
||||
github.com/go-rat/chix v1.1.4/go.mod h1:eKUTG7GXqMS278PH0QZi8yMWZddvMfxqWu7mverM6kQ=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-rat/chix v1.1.5 h1:nv+NHODd9o3AwK9JhEaUsuMCAc7cvw4bh39yTrVuo1M=
|
||||
github.com/go-rat/chix v1.1.5/go.mod h1:YQP0YT0x/U3Xx5uQe2jfyMiS/bA732Nunnii6MG0wos=
|
||||
github.com/go-rat/gormstore v1.0.6 h1:YxmaY6n3JoTk6o9RXoZ1+cQ6HQL+ayf/6d8D76ydCAc=
|
||||
github.com/go-rat/gormstore v1.0.6/go.mod h1:xG/n+Du8baWf7ptiafiHLWBZWVXrYIPC42CsoEbfr4M=
|
||||
github.com/go-rat/securecookie v1.0.1 h1:HW0fpKmB+FjJzXTw8ABOwBJ+XrPmRBSZqHhmrv86lBo=
|
||||
github.com/go-rat/securecookie v1.0.1/go.mod h1:tP/ObWYyjmcpabQ7WTon/i2lBSip/Aolliw2llXuPDU=
|
||||
github.com/go-rat/sessions v1.0.11 h1:2eTUvhEBtymrAtwv71LlGHNylz+Ad2SgY3jg/sSUboA=
|
||||
github.com/go-rat/sessions v1.0.11/go.mod h1:Ray/GCbuhm4U9xpjFFSCfOTCEn91puEhAXX5creHE9g=
|
||||
github.com/go-rat/utils v1.0.3 h1:SqH/O0KYq4SBv8naSAjJA4hC45/d8NLNrTCONfqjbFM=
|
||||
github.com/go-rat/utils v1.0.3/go.mod h1:4WNPlrF57KmeGZN3HOeBgdBVLJL3xgba4QRP/t6pKrE=
|
||||
github.com/go-resty/resty/v2 v2.15.3 h1:bqff+hcqAflpiF591hhJzNdkRsFhlB96CYfBwSFvql8=
|
||||
github.com/go-resty/resty/v2 v2.15.3/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-yaml v1.12.0 h1:/1WHjnMsI1dlIBQutrvSMGZRQufVO3asrHfTwfACoPM=
|
||||
github.com/goccy/go-yaml v1.12.0/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
|
||||
github.com/gofiber/schema v1.2.0 h1:j+ZRrNnUa/0ZuWrn/6kAtAufEr4jCJ+JuTURAMxNSZg=
|
||||
github.com/gofiber/schema v1.2.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/go-rat/securecookie v1.1.0 h1:bvwE9n6X3a4eazOIevEvZghkvZ3OpY4BX1cnH/1/x0s=
|
||||
github.com/go-rat/securecookie v1.1.0/go.mod h1:Z870TcL7ktAjRuhPeQXYuQcA6/DdzKwwqSwDQuPmufI=
|
||||
github.com/go-rat/sessions v1.1.0 h1:5zCySVVxNE6xLymf5F2u4TQPKIM7kz1MuD4TwIb/7Do=
|
||||
github.com/go-rat/sessions v1.1.0/go.mod h1:ofnsWh7JoAApzCfIGd60xEyT5HlJY+Nx8zpWcwUXSHc=
|
||||
github.com/go-rat/utils v1.1.4 h1:tjlX6IWHhA/VvEBVbLGjzA96dcFSdMU5quetUHY2uqs=
|
||||
github.com/go-rat/utils v1.1.4/go.mod h1:SUy4Xw44VkZvo2FaURgiIiJlL6Kukll85fVKMp/50+8=
|
||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||
github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI=
|
||||
github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gofiber/schema v1.3.0 h1:K3F3wYzAY+aivfCCEHPufCthu5/13r/lzp1nuk6mr3Q=
|
||||
github.com/gofiber/schema v1.3.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c=
|
||||
github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a h1:BAyyIK6rc6Tq9vT2KVlPJXD8EVv/LpaFwn4fgfLwtHg=
|
||||
github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a/go.mod h1:bgk4Ij/0OQ89UeoFFAQrSNhbbr4rKJ0fwWfo7wc+TCc=
|
||||
github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
|
||||
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gookit/filter v1.2.2 h1:LSBQLk4M4fpfhaOG0hJ/GJ+qu+2+lLddgxlGfzcd8VQ=
|
||||
github.com/gookit/filter v1.2.2/go.mod h1:y4xpiqM/fV9g9yT/Vg5OWUq4q9siPj0UlH9O57xYuGs=
|
||||
github.com/gookit/goutil v0.6.18 h1:MUVj0G16flubWT8zYVicIuisUiHdgirPAkmnfD2kKgw=
|
||||
github.com/gookit/goutil v0.6.18/go.mod h1:AY/5sAwKe7Xck+mEbuxj0n/bc3qwrGNe3Oeulln7zBA=
|
||||
github.com/gookit/validate v1.5.4 h1:nwBo6vULnVUeNFCOde6RKFRbOCKJXVMnWR0ghedacLg=
|
||||
github.com/gookit/validate v1.5.4/go.mod h1:p9sRPfpvYB4vXICBpEPzv8FoAky+XhUOhWQghgmmat4=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jaevor/go-nanoid v1.4.0 h1:mPz0oi3CrQyEtRxeRq927HHtZCJAAtZ7zdy7vOkrvWs=
|
||||
github.com/jaevor/go-nanoid v1.4.0/go.mod h1:GIpPtsvl3eSBsjjIEFQdzzgpi50+Bo1Luk+aYlbJzlc=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
|
||||
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
|
||||
github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=
|
||||
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
|
||||
github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w=
|
||||
github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
|
||||
github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
|
||||
github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
|
||||
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
|
||||
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
|
||||
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
|
||||
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
|
||||
github.com/libdns/dnspod v0.0.3 h1:xJHDIujgLjvZnpB8/rMoCHUqA/KxSGBqRUXxSIzNzAA=
|
||||
github.com/libdns/dnspod v0.0.3/go.mod h1:XLnqMmK7QlLPEbHwcOxbRvlzRvDgaaUlthRNFOPjXPI=
|
||||
github.com/libdns/cloudflare v0.1.3 h1:XPFa2f3Mm/3FDNwl9Ki2bfAQJ0Cm5GQB0e8PQVy25Us=
|
||||
github.com/libdns/cloudflare v0.1.3/go.mod h1:XbvSCSMcxspwpSialM3bq0LsS3/Houy9WYxW8Ok8b6M=
|
||||
github.com/libdns/huaweicloud v0.3.1 h1:wro0zpG86JKL3QVEaV/xfHW/59rmqwMYhcbl16w9EYg=
|
||||
github.com/libdns/huaweicloud v0.3.1/go.mod h1:6s5ZIwLUr2qKsQz2SRNo7uh8X5uJohxoBR95rtSpNcI=
|
||||
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/libdns/tencentcloud v1.0.0 h1:u4LXnYu/lu/9P5W+MCVPeSDnwI+6w+DxYhQ1wSnQOuU=
|
||||
github.com/libdns/tencentcloud v1.0.0/go.mod h1:NlCgPumzUsZWSOo1+Q/Hfh8G6TNRAaTUeWQdg6LbtUI=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
|
||||
github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60VhnHxb2X3T1teMyv5A=
|
||||
github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8=
|
||||
github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/libdns/tencentcloud v1.2.0 h1:SBbZ9gUZ+ba/p2d8NpiOcj+WZTdYqB5Fz7zypdbK3YU=
|
||||
github.com/libdns/tencentcloud v1.2.0/go.mod h1:o0+WCxQ7LGLtyjnjYU4HbGW9uVjN44SdUDhxdUYLGPw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/go-sqlite3 v0.24.1 h1:qHlIz+dlH3Y0wCUErFZXon5hCvw1Kc9eEkZVYYi8p14=
|
||||
github.com/ncruces/go-sqlite3 v0.24.1/go.mod h1:n6Z7036yFilJx04yV0mi5JWaF66rUmXn1It9Ux8dx68=
|
||||
github.com/ncruces/go-sqlite3/gormlite v0.24.0 h1:81sHeq3CCdhjoqAB650n5wEdRlLO9VBvosArskcN3+c=
|
||||
github.com/ncruces/go-sqlite3/gormlite v0.24.0/go.mod h1:vXfVWdBfg7qOgqQqHpzUWl9LLswD0h+8mK4oouaV2oc=
|
||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U=
|
||||
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/orandin/slog-gorm v1.4.0 h1:FgA8hJufF9/jeNSYoEXmHPPBwET2gwlF3B85JdpsTUU=
|
||||
github.com/orandin/slog-gorm v1.4.0/go.mod h1:MoZ51+b7xE9lwGNPYEhxcUtRNrYzjdcKvA8QXQQGEPA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
|
||||
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/sethvargo/go-limiter v1.0.0 h1:JqW13eWEMn0VFv86OKn8wiYJY/m250WoXdrjRV0kLe4=
|
||||
github.com/sethvargo/go-limiter v1.0.0/go.mod h1:01b6tW25Ap+MeLYBuD4aHunMrJoNO5PVUFdS9rac3II=
|
||||
github.com/shirou/gopsutil v2.21.11+incompatible h1:lOGOyCG67a5dv2hq5Z1BLDUqqKp3HkbjPcz5j6XMS0U=
|
||||
github.com/shirou/gopsutil v2.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.597 h1:C0GHdLTfikLVoEzfhgPfrZ7LwlG0xiCmk6iwNKE+xs0=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.597/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1096 h1:DMokC7T0UF8wMfT1kD+mX3M+hc2C06gmFvQ9gsfRPmI=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1096/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
|
||||
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20241013191809-e73b7dd454e8 h1:7yw1yHkylDcu/CwY4hEEe8MycDLo7B9LjcqwhoL8NrM=
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20241013191809-e73b7dd454e8/go.mod h1:itu4KWRgrfEwGcfNka+rV4houuirUau53i0diN4lG5g=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli/v3 v3.0.0-alpha9.1 h1:1fJU+bltkwN8lF4Sni/X0i1d8XwPIrS82ivZ8qsp/q4=
|
||||
github.com/urfave/cli/v3 v3.0.0-alpha9.1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
|
||||
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
|
||||
github.com/tnb-labs/acmez/v3 v3.0.0-20250328123917-f204dcee9d09 h1:Rz/1C7lc0qCuyZY20oh6grqy/hjm9gcjIJB3yEZ4i30=
|
||||
github.com/tnb-labs/acmez/v3 v3.0.0-20250328123917-f204dcee9d09/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20250225174229-c03497ddaef6 h1:HmtcQ7w07RI2SdTKkPf+NM8R33B1oR9MjIZYzlBizwA=
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20250225174229-c03497ddaef6/go.mod h1:hdMWBc1+TyB6G5ZZBBgPWQ8cjRZ6zpYdhal0uu6E9QM=
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg=
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI=
|
||||
go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts=
|
||||
go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 h1:umZgi92IyxfXd/l4kaDhnKgY8rnN/cZcF1LKc6I8OQ8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0/go.mod h1:4lVs6obhSVRb1EW5FhOuBTyiQhtRtAnnva9vD3yRfq8=
|
||||
go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w=
|
||||
go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ=
|
||||
go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE=
|
||||
go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg=
|
||||
go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc=
|
||||
go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU=
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
|
||||
google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
|
||||
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4=
|
||||
modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
|
||||
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/libc v1.60.1 h1:at373l8IFRTkJIkAU85BIuUoBM4T1b51ds0E1ovPG2s=
|
||||
modernc.org/libc v1.60.1/go.mod h1:xJuobKuNxKH3RUatS7GjR+suWj+5c2K7bi4m/S5arOY=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s=
|
||||
modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
moul.io/zapgorm2 v1.3.0 h1:+CzUTMIcnafd0d/BvBce8T4uPn6DQnpIrz64cyixlkk=
|
||||
moul.io/zapgorm2 v1.3.0/go.mod h1:nPVy6U9goFKHR4s+zfSo1xVFaoU7Qgd5DoCdOfzoCqs=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"github.com/tnb-labs/panel/pkg/apploader"
|
||||
)
|
||||
|
||||
type Cli struct {
|
||||
cmd *cli.Command
|
||||
migrator *gormigrate.Gormigrate
|
||||
}
|
||||
|
||||
func NewCli(cmd *cli.Command, migrator *gormigrate.Gormigrate, _ *apploader.Loader) *Cli {
|
||||
IsCli = true
|
||||
return &Cli{
|
||||
cmd: cmd,
|
||||
migrator: migrator,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Cli) Run() error {
|
||||
// migrate database
|
||||
// 这里不处理错误,这么做是为了在异常时用户可以用 fix 命令尝试修复
|
||||
_ = r.migrator.Migrate()
|
||||
|
||||
if err := r.cmd.Run(context.TODO(), os.Args); err != nil {
|
||||
fmt.Printf("|-%v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+17
-31
@@ -1,31 +1,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/go-rat/sessions"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/robfig/cron/v3"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/queue"
|
||||
)
|
||||
|
||||
var (
|
||||
Conf *koanf.Koanf
|
||||
Http *chi.Mux
|
||||
Orm *gorm.DB
|
||||
Validator *validator.Validate
|
||||
Translator *ut.Translator
|
||||
Session *sessions.Manager
|
||||
Cron *cron.Cron
|
||||
Queue *queue.Queue
|
||||
Logger *zap.Logger
|
||||
)
|
||||
|
||||
// 定义面板状态常量
|
||||
// 面板状态常量
|
||||
const (
|
||||
StatusNormal = iota
|
||||
StatusMaintain
|
||||
@@ -36,9 +11,20 @@ const (
|
||||
|
||||
// 面板全局变量
|
||||
var (
|
||||
Root string
|
||||
Version string
|
||||
Locale string
|
||||
IsCli bool
|
||||
Status = StatusNormal
|
||||
Key string // 密钥
|
||||
Root string // 根目录
|
||||
Locale string // 语言
|
||||
IsCli bool // 是否命令行
|
||||
Status = StatusNormal // 面板状态
|
||||
)
|
||||
|
||||
// 自动注入
|
||||
var (
|
||||
Version = "0.0.0"
|
||||
BuildTime string
|
||||
CommitHash string
|
||||
GoVersion string
|
||||
BuildID string
|
||||
BuildUser string
|
||||
BuildHost string
|
||||
)
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bddjr/hlfhr"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"github.com/gookit/validate"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/tnb-labs/panel/pkg/queue"
|
||||
)
|
||||
|
||||
type Web struct {
|
||||
conf *koanf.Koanf
|
||||
router *chi.Mux
|
||||
server *hlfhr.Server
|
||||
migrator *gormigrate.Gormigrate
|
||||
cron *cron.Cron
|
||||
queue *queue.Queue
|
||||
}
|
||||
|
||||
func NewWeb(conf *koanf.Koanf, router *chi.Mux, server *hlfhr.Server, migrator *gormigrate.Gormigrate, cron *cron.Cron, queue *queue.Queue, _ *validate.Validation) *Web {
|
||||
return &Web{
|
||||
conf: conf,
|
||||
router: router,
|
||||
server: server,
|
||||
migrator: migrator,
|
||||
cron: cron,
|
||||
queue: queue,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Web) Run() error {
|
||||
// migrate database
|
||||
if err := r.migrator.Migrate(); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("[DB] database migrated")
|
||||
|
||||
// start cron scheduler
|
||||
r.cron.Start()
|
||||
fmt.Println("[CRON] cron scheduler started")
|
||||
|
||||
// start queue
|
||||
r.queue.Run(context.TODO())
|
||||
|
||||
// run http server
|
||||
if r.conf.Bool("http.tls") {
|
||||
cert := filepath.Join(Root, "panel/storage/cert.pem")
|
||||
key := filepath.Join(Root, "panel/storage/cert.key")
|
||||
fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port"), "with tls")
|
||||
if err := r.server.ListenAndServeTLS(cert, key); !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port"))
|
||||
if err := r.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/apps/benchmark"
|
||||
"github.com/tnb-labs/panel/internal/apps/docker"
|
||||
"github.com/tnb-labs/panel/internal/apps/fail2ban"
|
||||
"github.com/tnb-labs/panel/internal/apps/frp"
|
||||
"github.com/tnb-labs/panel/internal/apps/gitea"
|
||||
"github.com/tnb-labs/panel/internal/apps/memcached"
|
||||
"github.com/tnb-labs/panel/internal/apps/minio"
|
||||
"github.com/tnb-labs/panel/internal/apps/mysql"
|
||||
"github.com/tnb-labs/panel/internal/apps/nginx"
|
||||
"github.com/tnb-labs/panel/internal/apps/php74"
|
||||
"github.com/tnb-labs/panel/internal/apps/php80"
|
||||
"github.com/tnb-labs/panel/internal/apps/php81"
|
||||
"github.com/tnb-labs/panel/internal/apps/php82"
|
||||
"github.com/tnb-labs/panel/internal/apps/php83"
|
||||
"github.com/tnb-labs/panel/internal/apps/php84"
|
||||
"github.com/tnb-labs/panel/internal/apps/phpmyadmin"
|
||||
"github.com/tnb-labs/panel/internal/apps/podman"
|
||||
"github.com/tnb-labs/panel/internal/apps/postgresql"
|
||||
"github.com/tnb-labs/panel/internal/apps/pureftpd"
|
||||
"github.com/tnb-labs/panel/internal/apps/redis"
|
||||
"github.com/tnb-labs/panel/internal/apps/rsync"
|
||||
"github.com/tnb-labs/panel/internal/apps/s3fs"
|
||||
"github.com/tnb-labs/panel/internal/apps/supervisor"
|
||||
"github.com/tnb-labs/panel/internal/apps/toolbox"
|
||||
)
|
||||
|
||||
var ProviderSet = wire.NewSet(
|
||||
benchmark.NewApp,
|
||||
docker.NewApp,
|
||||
fail2ban.NewApp,
|
||||
frp.NewApp,
|
||||
gitea.NewApp,
|
||||
minio.NewApp,
|
||||
memcached.NewApp,
|
||||
mysql.NewApp,
|
||||
nginx.NewApp,
|
||||
php74.NewApp,
|
||||
php80.NewApp,
|
||||
php81.NewApp,
|
||||
php82.NewApp,
|
||||
php83.NewApp,
|
||||
php84.NewApp,
|
||||
phpmyadmin.NewApp,
|
||||
podman.NewApp,
|
||||
postgresql.NewApp,
|
||||
pureftpd.NewApp,
|
||||
redis.NewApp,
|
||||
rsync.NewApp,
|
||||
s3fs.NewApp,
|
||||
supervisor.NewApp,
|
||||
toolbox.NewApp,
|
||||
)
|
||||
@@ -0,0 +1,691 @@
|
||||
package benchmark
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
cryptorand "crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand/v2"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Post("/test", s.Test)
|
||||
}
|
||||
|
||||
// Test 运行测试
|
||||
func (s *App) Test(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Test](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
switch req.Name {
|
||||
case "image":
|
||||
result := s.imageProcessing(req.Multi)
|
||||
service.Success(w, result)
|
||||
case "machine":
|
||||
result := s.machineLearning(req.Multi)
|
||||
service.Success(w, result)
|
||||
case "compile":
|
||||
result := s.compileSimulationSingle(req.Multi)
|
||||
service.Success(w, result)
|
||||
case "encryption":
|
||||
result := s.encryptionTest(req.Multi)
|
||||
service.Success(w, result)
|
||||
case "compression":
|
||||
result := s.compressionTest(req.Multi)
|
||||
service.Success(w, result)
|
||||
case "physics":
|
||||
result := s.physicsSimulation(req.Multi)
|
||||
service.Success(w, result)
|
||||
case "json":
|
||||
result := s.jsonProcessing(req.Multi)
|
||||
service.Success(w, result)
|
||||
case "disk":
|
||||
result := s.diskTestTask()
|
||||
service.Success(w, result)
|
||||
case "memory":
|
||||
result := s.memoryTestTask()
|
||||
service.Success(w, result)
|
||||
default:
|
||||
service.Error(w, http.StatusUnprocessableEntity, "未知测试类型")
|
||||
}
|
||||
}
|
||||
|
||||
// calculateCpuScore 计算CPU成绩
|
||||
func (s *App) calculateCpuScore(duration time.Duration) int {
|
||||
score := int((10 / duration.Seconds()) * float64(3000))
|
||||
|
||||
if score < 0 {
|
||||
score = 0
|
||||
}
|
||||
return score
|
||||
}
|
||||
|
||||
// calculateScore 计算内存/硬盘成绩
|
||||
func (s *App) calculateScore(duration time.Duration) int {
|
||||
score := int((20 / duration.Seconds()) * float64(30000))
|
||||
|
||||
if score < 0 {
|
||||
score = 0
|
||||
}
|
||||
return score
|
||||
}
|
||||
|
||||
// 图像处理
|
||||
|
||||
func (s *App) imageProcessing(multi bool) int {
|
||||
n := 1
|
||||
if multi {
|
||||
n = runtime.NumCPU()
|
||||
}
|
||||
start := time.Now()
|
||||
if err := s.imageProcessingTask(n); err != nil {
|
||||
return 0
|
||||
}
|
||||
duration := time.Since(start)
|
||||
return s.calculateCpuScore(duration)
|
||||
}
|
||||
|
||||
func (s *App) imageProcessingTask(numThreads int) error {
|
||||
img := image.NewRGBA(image.Rect(0, 0, 4000, 4000))
|
||||
for x := 0; x < 4000; x++ {
|
||||
for y := 0; y < 4000; y++ {
|
||||
img.Set(x, y, color.RGBA{R: uint8(x % 256), G: uint8(y % 256), A: 255})
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
dx := img.Bounds().Dx()
|
||||
dy := img.Bounds().Dy()
|
||||
chunkSize := dy / numThreads
|
||||
|
||||
for i := 0; i < numThreads; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
startY := i * chunkSize
|
||||
endY := startY + chunkSize
|
||||
if i == numThreads-1 {
|
||||
endY = dy
|
||||
}
|
||||
for x := 1; x < dx-1; x++ {
|
||||
for y := startY + 1; y < endY-1; y++ {
|
||||
// 卷积操作(模糊)
|
||||
rTotal, gTotal, bTotal := 0, 0, 0
|
||||
for k := -1; k <= 1; k++ {
|
||||
for l := -1; l <= 1; l++ {
|
||||
r, g, b, _ := img.At(x+k, y+l).RGBA()
|
||||
rTotal += int(r)
|
||||
gTotal += int(g)
|
||||
bTotal += int(b)
|
||||
}
|
||||
}
|
||||
rAvg := uint8(rTotal / 9 / 256)
|
||||
gAvg := uint8(gTotal / 9 / 256)
|
||||
bAvg := uint8(bTotal / 9 / 256)
|
||||
img.Set(x, y, color.RGBA{R: rAvg, G: gAvg, B: bAvg, A: 255})
|
||||
}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 机器学习(矩阵乘法)
|
||||
|
||||
func (s *App) machineLearning(multi bool) int {
|
||||
n := 1
|
||||
if multi {
|
||||
n = runtime.NumCPU()
|
||||
}
|
||||
start := time.Now()
|
||||
s.machineLearningTask(n)
|
||||
duration := time.Since(start)
|
||||
return s.calculateCpuScore(duration)
|
||||
}
|
||||
|
||||
func (s *App) machineLearningTask(numThreads int) {
|
||||
size := 900
|
||||
a := make([][]float64, size)
|
||||
b := make([][]float64, size)
|
||||
for i := 0; i < size; i++ {
|
||||
a[i] = make([]float64, size)
|
||||
b[i] = make([]float64, size)
|
||||
for j := 0; j < size; j++ {
|
||||
a[i][j] = rand.Float64()
|
||||
b[i][j] = rand.Float64()
|
||||
}
|
||||
}
|
||||
|
||||
c := make([][]float64, size)
|
||||
for i := 0; i < size; i++ {
|
||||
c[i] = make([]float64, size)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
chunkSize := size / numThreads
|
||||
|
||||
for k := 0; k < numThreads; k++ {
|
||||
wg.Add(1)
|
||||
go func(k int) {
|
||||
defer wg.Done()
|
||||
start := k * chunkSize
|
||||
end := start + chunkSize
|
||||
if k == numThreads-1 {
|
||||
end = size
|
||||
}
|
||||
for i := start; i < end; i++ {
|
||||
for j := 0; j < size; j++ {
|
||||
sum := 0.0
|
||||
for l := 0; l < size; l++ {
|
||||
sum += a[i][l] * b[l][j]
|
||||
}
|
||||
c[i][j] = sum
|
||||
}
|
||||
}
|
||||
}(k)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// 数学问题(计算斐波那契数)
|
||||
|
||||
func (s *App) compileSimulationSingle(multi bool) int {
|
||||
n := 1
|
||||
if multi {
|
||||
n = runtime.NumCPU()
|
||||
}
|
||||
start := time.Now()
|
||||
totalCalculations := 1000
|
||||
fibNumber := 20000
|
||||
|
||||
calculationsPerThread := totalCalculations / n
|
||||
remainder := totalCalculations % n
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
tasks := calculationsPerThread
|
||||
if i < remainder {
|
||||
tasks++ // 处理无法均分的剩余任务
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(tasks int) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < tasks; j++ {
|
||||
s.fib(fibNumber)
|
||||
}
|
||||
}(tasks)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
duration := time.Since(start)
|
||||
return s.calculateCpuScore(duration)
|
||||
}
|
||||
|
||||
// 斐波那契函数
|
||||
func (s *App) fib(n int) *big.Int {
|
||||
if n < 2 {
|
||||
return big.NewInt(int64(n))
|
||||
}
|
||||
a := big.NewInt(0)
|
||||
b := big.NewInt(1)
|
||||
temp := big.NewInt(0)
|
||||
for i := 2; i <= n; i++ {
|
||||
temp.Add(a, b)
|
||||
a.Set(b)
|
||||
b.Set(temp)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AES加密
|
||||
|
||||
func (s *App) encryptionTest(multi bool) int {
|
||||
n := 1
|
||||
if multi {
|
||||
n = runtime.NumCPU()
|
||||
}
|
||||
start := time.Now()
|
||||
if err := s.encryptionTestTask(n); err != nil {
|
||||
return 0
|
||||
}
|
||||
duration := time.Since(start)
|
||||
return s.calculateCpuScore(duration)
|
||||
}
|
||||
|
||||
func (s *App) encryptionTestTask(numThreads int) error {
|
||||
key := []byte("abcdefghijklmnopqrstuvwxyz123456")
|
||||
dataSize := 1024 * 1024 * 512 // 512 MB
|
||||
plaintext := []byte(strings.Repeat("A", dataSize))
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aesGCM, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chunkSize := dataSize / numThreads
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < numThreads; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
start := i * chunkSize
|
||||
end := start + chunkSize
|
||||
if i == numThreads-1 {
|
||||
end = dataSize
|
||||
}
|
||||
|
||||
nonce := make([]byte, aesGCM.NonceSize())
|
||||
if _, err = cryptorand.Read(nonce); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
aesGCM.Seal(nil, nonce, plaintext[start:end], nil)
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 压缩/解压缩
|
||||
|
||||
func (s *App) compressionTest(multi bool) int {
|
||||
n := 1
|
||||
if multi {
|
||||
n = runtime.NumCPU()
|
||||
}
|
||||
start := time.Now()
|
||||
s.compressionTestTask(n)
|
||||
duration := time.Since(start)
|
||||
return s.calculateCpuScore(duration)
|
||||
}
|
||||
|
||||
func (s *App) compressionTestTask(numThreads int) {
|
||||
data := []byte(strings.Repeat("耗子面板", 50000000))
|
||||
chunkSize := len(data) / numThreads
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
compressedChunks := make([]bytes.Buffer, numThreads)
|
||||
|
||||
// 压缩
|
||||
for i := 0; i < numThreads; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
start := i * chunkSize
|
||||
end := start + chunkSize
|
||||
if i == numThreads-1 {
|
||||
end = len(data)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
w := gzip.NewWriter(&buf)
|
||||
_, _ = w.Write(data[start:end])
|
||||
_ = w.Close()
|
||||
compressedChunks[i] = buf
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// 解压缩
|
||||
for i := 0; i < numThreads; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
r, err := gzip.NewReader(&compressedChunks[i])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = io.Copy(io.Discard, r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = r.Close()
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// 物理仿真(N体问题)
|
||||
|
||||
func (s *App) physicsSimulation(multi bool) int {
|
||||
n := 1
|
||||
if multi {
|
||||
n = runtime.NumCPU()
|
||||
}
|
||||
start := time.Now()
|
||||
s.physicsSimulationTask(n)
|
||||
duration := time.Since(start)
|
||||
return s.calculateCpuScore(duration)
|
||||
}
|
||||
|
||||
func (s *App) physicsSimulationTask(numThreads int) {
|
||||
const (
|
||||
numBodies = 4000
|
||||
steps = 30
|
||||
)
|
||||
|
||||
type Body struct {
|
||||
x, y, z, vx, vy, vz float64
|
||||
}
|
||||
|
||||
bodies := make([]Body, numBodies)
|
||||
for i := 0; i < numBodies; i++ {
|
||||
bodies[i] = Body{
|
||||
x: rand.Float64(),
|
||||
y: rand.Float64(),
|
||||
z: rand.Float64(),
|
||||
vx: rand.Float64(),
|
||||
vy: rand.Float64(),
|
||||
vz: rand.Float64(),
|
||||
}
|
||||
}
|
||||
|
||||
chunkSize := numBodies / numThreads
|
||||
|
||||
for step := 0; step < steps; step++ {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// 更新速度
|
||||
for k := 0; k < numThreads; k++ {
|
||||
wg.Add(1)
|
||||
go func(k int) {
|
||||
defer wg.Done()
|
||||
start := k * chunkSize
|
||||
end := start + chunkSize
|
||||
if k == numThreads-1 {
|
||||
end = numBodies
|
||||
}
|
||||
for i := start; i < end; i++ {
|
||||
bi := &bodies[i]
|
||||
for j := 0; j < numBodies; j++ {
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
bj := &bodies[j]
|
||||
dx := bj.x - bi.x
|
||||
dy := bj.y - bi.y
|
||||
dz := bj.z - bi.z
|
||||
dist := math.Sqrt(dx*dx + dy*dy + dz*dz)
|
||||
if dist == 0 {
|
||||
continue
|
||||
}
|
||||
force := 1 / (dist * dist)
|
||||
bi.vx += force * dx / dist
|
||||
bi.vy += force * dy / dist
|
||||
bi.vz += force * dz / dist
|
||||
}
|
||||
}
|
||||
}(k)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// 更新位置
|
||||
for k := 0; k < numThreads; k++ {
|
||||
wg.Add(1)
|
||||
go func(k int) {
|
||||
defer wg.Done()
|
||||
start := k * chunkSize
|
||||
end := start + chunkSize
|
||||
if k == numThreads-1 {
|
||||
end = numBodies
|
||||
}
|
||||
for i := start; i < end; i++ {
|
||||
bi := &bodies[i]
|
||||
bi.x += bi.vx
|
||||
bi.y += bi.vy
|
||||
bi.z += bi.vz
|
||||
}
|
||||
}(k)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
// JSON解析
|
||||
|
||||
func (s *App) jsonProcessing(multi bool) int {
|
||||
n := 1
|
||||
if multi {
|
||||
n = runtime.NumCPU()
|
||||
}
|
||||
start := time.Now()
|
||||
s.jsonProcessingTask(n)
|
||||
duration := time.Since(start)
|
||||
return s.calculateCpuScore(duration)
|
||||
}
|
||||
|
||||
func (s *App) jsonProcessingTask(numThreads int) {
|
||||
numElements := 1000000
|
||||
elementsPerThread := numElements / numThreads
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < numThreads; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
|
||||
start := i * elementsPerThread
|
||||
end := start + elementsPerThread
|
||||
if i == numThreads-1 {
|
||||
end = numElements
|
||||
}
|
||||
|
||||
elements := make([]map[string]any, 0, end-start)
|
||||
for j := start; j < end; j++ {
|
||||
elements = append(elements, map[string]any{
|
||||
"id": j,
|
||||
"value": fmt.Sprintf("Value%d", j),
|
||||
})
|
||||
}
|
||||
encoded, err := json.Marshal(elements)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var parsed []map[string]any
|
||||
err = json.Unmarshal(encoded, &parsed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// 内存性能
|
||||
|
||||
func (s *App) memoryTestTask() map[string]any {
|
||||
results := make(map[string]any)
|
||||
dataSize := 500 * 1024 * 1024 // 500 MB
|
||||
data := make([]byte, dataSize)
|
||||
_, _ = cryptorand.Read(data)
|
||||
|
||||
start := time.Now()
|
||||
// 内存读写速度
|
||||
results["bandwidth"] = s.memoryBandwidthTest(data)
|
||||
// 内存访问延迟
|
||||
data = data[:100*1024*1024] // 100 MB
|
||||
results["latency"] = s.memoryLatencyTest(data)
|
||||
duration := time.Since(start)
|
||||
results["score"] = s.calculateScore(duration)
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
func (s *App) memoryBandwidthTest(data []byte) string {
|
||||
dataSize := len(data)
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
for i := 0; i < dataSize; i++ {
|
||||
data[i] ^= 0xFF
|
||||
}
|
||||
|
||||
duration := time.Since(startTime).Seconds()
|
||||
if duration == 0 {
|
||||
return "N/A"
|
||||
}
|
||||
speed := float64(dataSize) / duration / (1024 * 1024)
|
||||
return fmt.Sprintf("%.2f MB/s", speed)
|
||||
}
|
||||
|
||||
func (s *App) memoryLatencyTest(data []byte) string {
|
||||
dataSize := len(data)
|
||||
indices := rand.Perm(dataSize)
|
||||
|
||||
startTime := time.Now()
|
||||
sum := byte(0)
|
||||
for _, idx := range indices {
|
||||
sum ^= data[idx]
|
||||
}
|
||||
duration := time.Since(startTime).Seconds()
|
||||
if duration == 0 {
|
||||
return "N/A"
|
||||
}
|
||||
avgLatency := duration * 1e9 / float64(dataSize)
|
||||
return fmt.Sprintf("%.2f ns", avgLatency)
|
||||
}
|
||||
|
||||
// 硬盘IO
|
||||
|
||||
func (s *App) diskTestTask() map[string]any {
|
||||
results := make(map[string]any)
|
||||
blockSizes := []int64{4 * 1024, 64 * 1024, 512 * 1024, 1 * 1024 * 1024} // 4K, 64K, 512K, 1M
|
||||
fileSize := int64(100 * 1024 * 1024) // 100MB 文件
|
||||
|
||||
start := time.Now()
|
||||
for _, blockSize := range blockSizes {
|
||||
result := s.diskIOTest(blockSize, fileSize)
|
||||
results[fmt.Sprintf("%d", blockSize/1024)] = result
|
||||
}
|
||||
duration := time.Since(start)
|
||||
results["score"] = s.calculateScore(duration)
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
func (s *App) diskIOTest(blockSize int64, fileSize int64) map[string]any {
|
||||
result := make(map[string]any)
|
||||
tempFile := fmt.Sprintf("tempfile_%d", blockSize)
|
||||
defer os.Remove(tempFile)
|
||||
|
||||
// 写测试
|
||||
writeSpeed, writeIOPS := s.diskWriteTest(tempFile, blockSize, fileSize)
|
||||
// 读测试
|
||||
readSpeed, readIOPS := s.diskReadTest(tempFile, blockSize, fileSize)
|
||||
|
||||
result["write_speed"] = fmt.Sprintf("%.2f MB/s", writeSpeed)
|
||||
result["write_iops"] = fmt.Sprintf("%.2f IOPS", writeIOPS)
|
||||
result["read_speed"] = fmt.Sprintf("%.2f MB/s", readSpeed)
|
||||
result["read_iops"] = fmt.Sprintf("%.2f IOPS", readIOPS)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *App) diskWriteTest(fileName string, blockSize int64, fileSize int64) (float64, float64) {
|
||||
totalBlocks := fileSize / blockSize
|
||||
|
||||
data := make([]byte, blockSize)
|
||||
_, _ = cryptorand.Read(data)
|
||||
|
||||
file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_SYNC, 0644)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
for i := int64(0); i < totalBlocks; i++ {
|
||||
// 生成随机偏移
|
||||
offset := rand.Int64N(fileSize - blockSize + 1)
|
||||
_, err := file.WriteAt(data, offset)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
}
|
||||
|
||||
_ = file.Sync()
|
||||
|
||||
duration := time.Since(start).Seconds()
|
||||
if duration == 0 {
|
||||
duration = 1
|
||||
}
|
||||
speed := float64(totalBlocks*blockSize) / duration / (1024 * 1024)
|
||||
iops := float64(totalBlocks) / duration
|
||||
return speed, iops
|
||||
}
|
||||
|
||||
func (s *App) diskReadTest(fileName string, blockSize int64, fileSize int64) (float64, float64) {
|
||||
totalBlocks := fileSize / blockSize
|
||||
|
||||
data := make([]byte, blockSize)
|
||||
|
||||
file, err := os.OpenFile(fileName, os.O_RDONLY|os.O_SYNC, 0644)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
for i := int64(0); i < totalBlocks; i++ {
|
||||
// 生成随机偏移
|
||||
offset := rand.Int64N(fileSize - blockSize + 1)
|
||||
_, err := file.ReadAt(data, offset)
|
||||
if err != nil && err != io.EOF {
|
||||
return 0, 0
|
||||
}
|
||||
}
|
||||
|
||||
duration := time.Since(start).Seconds()
|
||||
if duration == 0 {
|
||||
duration = 1
|
||||
}
|
||||
speed := float64(totalBlocks*blockSize) / duration / (1024 * 1024)
|
||||
iops := float64(totalBlocks) / duration
|
||||
return speed, iops
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package benchmark
|
||||
|
||||
type Test struct {
|
||||
Name string `json:"name" validate:"required|in:image,machine,compile,encryption,compression,physics,json,memory,disk"`
|
||||
Multi bool `json:"multi"`
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
}
|
||||
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read("/etc/docker/daemon.json")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = io.Write("/etc/docker/daemon.json", req.Config, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = systemctl.Restart("docker"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package docker
|
||||
|
||||
type UpdateConfig struct {
|
||||
Config string `form:"config" json:"config" validate:"required"`
|
||||
}
|
||||
@@ -6,31 +6,40 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/go-rat/utils/str"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
type App struct {
|
||||
websiteRepo biz.WebsiteRepo
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{
|
||||
websiteRepo: data.NewWebsiteRepo(),
|
||||
func NewApp(website biz.WebsiteRepo) *App {
|
||||
return &App{
|
||||
websiteRepo: website,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/jails", s.List)
|
||||
r.Post("/jails", s.Create)
|
||||
r.Delete("/jails", s.Delete)
|
||||
r.Get("/jails/{name}", s.BanList)
|
||||
r.Post("/unban", s.Unban)
|
||||
r.Post("/whiteList", s.SetWhiteList)
|
||||
r.Get("/whiteList", s.GetWhiteList)
|
||||
}
|
||||
|
||||
// List 所有规则
|
||||
func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) List(w http.ResponseWriter, r *http.Request) {
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -55,7 +64,6 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
continue
|
||||
}
|
||||
jailEnabled := strings.Contains(jailRaw, "enabled = true")
|
||||
jailLogPath := regexp.MustCompile(`logpath = (.*)`).FindStringSubmatch(jailRaw)
|
||||
jailMaxRetry := regexp.MustCompile(`maxretry = (.*)`).FindStringSubmatch(jailRaw)
|
||||
jailFindTime := regexp.MustCompile(`findtime = (.*)`).FindStringSubmatch(jailRaw)
|
||||
jailBanTime := regexp.MustCompile(`bantime = (.*)`).FindStringSubmatch(jailRaw)
|
||||
@@ -63,7 +71,6 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
jails = append(jails, Jail{
|
||||
Name: jailName,
|
||||
Enabled: jailEnabled,
|
||||
LogPath: jailLogPath[1],
|
||||
MaxRetry: cast.ToInt(jailMaxRetry[1]),
|
||||
FindTime: cast.ToInt(jailFindTime[1]),
|
||||
BanTime: cast.ToInt(jailBanTime[1]),
|
||||
@@ -79,7 +86,7 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Create 添加规则
|
||||
func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Create(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Add](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -128,7 +135,6 @@ port = ` + ports + `
|
||||
maxretry = ` + jailMaxRetry + `
|
||||
findtime = ` + jailFindTime + `
|
||||
bantime = ` + jailBanTime + `
|
||||
action = %(action_mwl)s
|
||||
logpath = ` + app.Root + `/wwwlogs/` + website.Name + `.log
|
||||
# ` + jailWebsiteName + `-` + jailWebsiteMode + `-END
|
||||
`
|
||||
@@ -158,25 +164,17 @@ ignoreregex =
|
||||
}
|
||||
|
||||
case "service":
|
||||
var logPath string
|
||||
var filter string
|
||||
var port string
|
||||
var err error
|
||||
switch jailName {
|
||||
case "ssh":
|
||||
if os.IsDebian() || os.IsUbuntu() {
|
||||
logPath = "/var/log/auth.log"
|
||||
} else {
|
||||
logPath = "/var/log/secure"
|
||||
}
|
||||
filter = "sshd"
|
||||
port, err = shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
|
||||
case "mysql":
|
||||
logPath = app.Root + "/server/mysql/mysql-error.log"
|
||||
filter = "mysqld-auth"
|
||||
port, err = shell.Execf("cat %s/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'", app.Root)
|
||||
case "pure-ftpd":
|
||||
logPath = "/var/log/messages"
|
||||
filter = "pure-ftpd"
|
||||
port, err = shell.Execf(`cat %s/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`, app.Root)
|
||||
default:
|
||||
@@ -197,8 +195,6 @@ port = ` + port + `
|
||||
maxretry = ` + jailMaxRetry + `
|
||||
findtime = ` + jailFindTime + `
|
||||
bantime = ` + jailBanTime + `
|
||||
action = %(action_mwl)s
|
||||
logpath = ` + logPath + `
|
||||
# ` + jailName + `-END
|
||||
`
|
||||
raw += rule
|
||||
@@ -208,7 +204,7 @@ logpath = ` + logPath + `
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
|
||||
if _, err = shell.Execf("fail2ban-client reload"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重载配置失败")
|
||||
return
|
||||
}
|
||||
@@ -217,7 +213,7 @@ logpath = ` + logPath + `
|
||||
}
|
||||
|
||||
// Delete 删除规则
|
||||
func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Delete](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -251,7 +247,7 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// BanList 获取封禁列表
|
||||
func (s *Service) BanList(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) BanList(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[BanList](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -296,7 +292,7 @@ func (s *Service) BanList(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Unban 解封
|
||||
func (s *Service) Unban(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Unban(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Unban](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -312,7 +308,7 @@ func (s *Service) Unban(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// SetWhiteList 设置白名单
|
||||
func (s *Service) SetWhiteList(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) SetWhiteList(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[SetWhiteList](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -346,7 +342,7 @@ func (s *Service) SetWhiteList(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// GetWhiteList 获取白名单
|
||||
func (s *Service) GetWhiteList(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetWhiteList(w http.ResponseWriter, r *http.Request) {
|
||||
raw, err := io.Read("/etc/fail2ban/jail.local")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -1,24 +0,0 @@
|
||||
package fail2ban
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "fail2ban",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/jails", service.List)
|
||||
r.Post("/jails", service.Create)
|
||||
r.Delete("/jails", service.Delete)
|
||||
r.Get("/jails/{name}", service.BanList)
|
||||
r.Post("/unban", service.Unban)
|
||||
r.Post("/whiteList", service.SetWhiteList)
|
||||
r.Get("/whiteList", service.GetWhiteList)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package fail2ban
|
||||
type Jail struct {
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
LogPath string `json:"log_path"`
|
||||
MaxRetry int `json:"max_retry"`
|
||||
FindTime int `json:"find_time"`
|
||||
BanTime int `json:"ban_time"`
|
||||
|
||||
@@ -4,19 +4,26 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
type App struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
}
|
||||
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Name](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -32,7 +39,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -1,19 +0,0 @@
|
||||
package frp
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "frp",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -4,24 +4,31 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
type App struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
}
|
||||
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, _ := io.Read(fmt.Sprintf("%s/server/gitea/app.ini", app.Root))
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -1,19 +0,0 @@
|
||||
package gitea
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "gitea",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
_ "github.com/TheTNB/panel/internal/apps/fail2ban"
|
||||
_ "github.com/TheTNB/panel/internal/apps/frp"
|
||||
_ "github.com/TheTNB/panel/internal/apps/gitea"
|
||||
_ "github.com/TheTNB/panel/internal/apps/mysql"
|
||||
_ "github.com/TheTNB/panel/internal/apps/nginx"
|
||||
_ "github.com/TheTNB/panel/internal/apps/php"
|
||||
_ "github.com/TheTNB/panel/internal/apps/phpmyadmin"
|
||||
_ "github.com/TheTNB/panel/internal/apps/podman"
|
||||
_ "github.com/TheTNB/panel/internal/apps/postgresql"
|
||||
_ "github.com/TheTNB/panel/internal/apps/pureftpd"
|
||||
_ "github.com/TheTNB/panel/internal/apps/redis"
|
||||
_ "github.com/TheTNB/panel/internal/apps/rsync"
|
||||
_ "github.com/TheTNB/panel/internal/apps/s3fs"
|
||||
_ "github.com/TheTNB/panel/internal/apps/supervisor"
|
||||
_ "github.com/TheTNB/panel/internal/apps/toolbox"
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
)
|
||||
|
||||
func Boot(r chi.Router) {
|
||||
apploader.Boot(r)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package memcached
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/load", s.Load)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
}
|
||||
|
||||
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := systemctl.Status("memcached")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "failed to get Memcached status: %v", err)
|
||||
return
|
||||
}
|
||||
if !status {
|
||||
service.Success(w, []types.NV{})
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:11211")
|
||||
if err != nil {
|
||||
service.Success(w, []types.NV{})
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Write([]byte("stats\nquit\n"))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "failed to write to Memcached: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
data := make([]types.NV, 0)
|
||||
re := regexp.MustCompile(`STAT\s(\S+)\s(\S+)`)
|
||||
scanner := bufio.NewScanner(conn)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if matches := re.FindStringSubmatch(line); len(matches) == 3 {
|
||||
data = append(data, types.NV{
|
||||
Name: matches[1],
|
||||
Value: matches[2],
|
||||
})
|
||||
}
|
||||
if line == "END" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err = scanner.Err(); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "failed to read from Memcached: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, data)
|
||||
}
|
||||
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read("/etc/systemd/system/memcached.service")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = io.Write("/etc/systemd/system/memcached.service", req.Config, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = systemctl.Restart("memcached"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package memcached
|
||||
|
||||
type UpdateConfig struct {
|
||||
Config string `form:"config" json:"config" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package minio
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/env", s.GetEnv)
|
||||
r.Post("/env", s.UpdateEnv)
|
||||
}
|
||||
|
||||
func (s *App) GetEnv(w http.ResponseWriter, r *http.Request) {
|
||||
env, _ := io.Read("/etc/default/minio")
|
||||
service.Success(w, env)
|
||||
}
|
||||
|
||||
func (s *App) UpdateEnv(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateEnv](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = io.Write("/etc/default/minio", req.Env, 0600); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = systemctl.Restart("minio"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package minio
|
||||
|
||||
type UpdateEnv struct {
|
||||
Env string `form:"env" json:"env" validate:"required"`
|
||||
}
|
||||
@@ -6,32 +6,43 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/db"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/db"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
"github.com/tnb-labs/panel/pkg/tools"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
type App struct {
|
||||
settingRepo biz.SettingRepo
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{
|
||||
settingRepo: data.NewSettingRepo(),
|
||||
func NewApp(setting biz.SettingRepo) *App {
|
||||
return &App{
|
||||
settingRepo: setting,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/load", s.Load)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
r.Post("/clearErrorLog", s.ClearErrorLog)
|
||||
r.Get("/slowLog", s.SlowLog)
|
||||
r.Post("/clearSlowLog", s.ClearSlowLog)
|
||||
r.Get("/rootPassword", s.GetRootPassword)
|
||||
r.Post("/rootPassword", s.SetRootPassword)
|
||||
}
|
||||
|
||||
// GetConfig 获取配置
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read(app.Root + "/server/mysql/conf/my.cnf")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取配置失败")
|
||||
@@ -42,7 +53,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdateConfig 保存配置
|
||||
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -63,7 +74,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Load 获取负载
|
||||
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
rootPassword, err := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取root密码失败")
|
||||
@@ -123,7 +134,7 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
if len(matches) > 1 {
|
||||
d := map[string]string{"name": expression.name, "value": matches[1]}
|
||||
if expression.name == "发送" || expression.name == "接收" {
|
||||
d["value"] = str.FormatBytes(cast.ToFloat64(matches[1]))
|
||||
d["value"] = tools.FormatBytes(cast.ToFloat64(matches[1]))
|
||||
}
|
||||
|
||||
load = append(load, d)
|
||||
@@ -142,15 +153,9 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, load)
|
||||
}
|
||||
|
||||
// ErrorLog 获取错误日志
|
||||
func (s *Service) ErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
log, _ := shell.Execf("tail -n 100 %s/server/mysql/mysql-error.log", app.Root)
|
||||
service.Success(w, log)
|
||||
}
|
||||
|
||||
// ClearErrorLog 清空错误日志
|
||||
func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf("echo '' > %s/server/mysql/mysql-error.log", app.Root); err != nil {
|
||||
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
if err := systemctl.LogsClear("mysqld"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -159,13 +164,12 @@ func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// SlowLog 获取慢查询日志
|
||||
func (s *Service) SlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
log, _ := shell.Execf("tail -n 100 %s/server/mysql/mysql-slow.log", app.Root)
|
||||
service.Success(w, log)
|
||||
func (s *App) SlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, fmt.Sprintf("%s/server/mysql/mysql-slow.log", app.Root))
|
||||
}
|
||||
|
||||
// ClearSlowLog 清空慢查询日志
|
||||
func (s *Service) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf("echo '' > %s/server/mysql/mysql-slow.log", app.Root); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -175,7 +179,7 @@ func (s *Service) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// GetRootPassword 获取root密码
|
||||
func (s *Service) GetRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
rootPassword, err := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取root密码失败")
|
||||
@@ -190,7 +194,7 @@ func (s *Service) GetRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// SetRootPassword 设置root密码
|
||||
func (s *Service) SetRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) SetRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[SetRootPassword](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -206,7 +210,8 @@ func (s *Service) SetRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err = mysql.UserPassword("root", req.Password); err != nil {
|
||||
defer mysql.Close()
|
||||
if err = mysql.UserPassword("root", req.Password, "localhost"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -219,7 +224,7 @@ func (s *Service) SetRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) getSock() string {
|
||||
func (s *App) getSock() string {
|
||||
if io.Exists("/tmp/mysql.sock") {
|
||||
return "/tmp/mysql.sock"
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "mysql",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/load", service.Load)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
r.Get("/errorLog", service.ErrorLog)
|
||||
r.Post("/clearErrorLog", service.ClearErrorLog)
|
||||
r.Get("/slowLog", service.SlowLog)
|
||||
r.Post("/clearSlowLog", service.ClearSlowLog)
|
||||
r.Get("/rootPassword", service.GetRootPassword)
|
||||
r.Post("/rootPassword", service.SetRootPassword)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -5,5 +5,5 @@ type UpdateConfig struct {
|
||||
}
|
||||
|
||||
type SetRootPassword struct {
|
||||
Password string `form:"password" json:"password" validate:"required"`
|
||||
Password string `form:"password" json:"password" validate:"required|password"`
|
||||
}
|
||||
|
||||
@@ -6,27 +6,36 @@ import (
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
"github.com/tnb-labs/panel/pkg/tools"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
type App struct {
|
||||
// Dependent services
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/load", s.Load)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.SaveConfig)
|
||||
r.Get("/errorLog", s.ErrorLog)
|
||||
r.Post("/clearErrorLog", s.ClearErrorLog)
|
||||
}
|
||||
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read(fmt.Sprintf("%s/server/nginx/conf/nginx.conf", app.Root))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取配置失败")
|
||||
@@ -36,7 +45,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) SaveConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) SaveConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -50,19 +59,18 @@ func (s *Service) SaveConfig(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err = systemctl.Reload("nginx"); err != nil {
|
||||
_, err = shell.Execf("nginx -t")
|
||||
service.Error(w, http.StatusInternalServerError, "重载服务失败:%v", err)
|
||||
service.Error(w, http.StatusInternalServerError, "重载服务失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) ErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
out, _ := shell.Execf("tail -n 100 %s/%s", app.Root, "wwwlogs/nginx-error.log")
|
||||
service.Success(w, out)
|
||||
func (s *App) ErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, fmt.Sprintf("%s/%s", app.Root, "wwwlogs/nginx-error.log"))
|
||||
}
|
||||
|
||||
func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf("echo '' > %s/%s", app.Root, "wwwlogs/nginx-error.log"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -71,7 +79,7 @@ func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
client := resty.New().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/nginx_status")
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
@@ -96,7 +104,7 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
service.Error(w, http.StatusInternalServerError, "获取负载失败")
|
||||
return
|
||||
}
|
||||
mem := str.FormatBytes(cast.ToFloat64(out))
|
||||
mem := tools.FormatBytes(cast.ToFloat64(out))
|
||||
data = append(data, types.NV{
|
||||
Name: "内存占用",
|
||||
Value: mem,
|
||||
@@ -1,22 +0,0 @@
|
||||
package nginx
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "nginx",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/load", service.Load)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.SaveConfig)
|
||||
r.Get("/errorLog", service.ErrorLog)
|
||||
r.Post("/clearErrorLog", service.ClearErrorLog)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -4,36 +4,64 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
type App struct {
|
||||
version uint
|
||||
taskRepo biz.TaskRepo
|
||||
}
|
||||
|
||||
func NewService(version uint) *Service {
|
||||
return &Service{
|
||||
version: version,
|
||||
taskRepo: data.NewTaskRepo(),
|
||||
func NewApp(task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
taskRepo: task,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Route(version uint) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
php := new(App)
|
||||
php.version = version
|
||||
php.taskRepo = s.taskRepo
|
||||
r.Post("/setCli", php.SetCli)
|
||||
r.Get("/config", php.GetConfig)
|
||||
r.Post("/config", php.UpdateConfig)
|
||||
r.Get("/fpmConfig", php.GetFPMConfig)
|
||||
r.Post("/fpmConfig", php.UpdateFPMConfig)
|
||||
r.Get("/load", php.Load)
|
||||
r.Get("/errorLog", php.ErrorLog)
|
||||
r.Get("/slowLog", php.SlowLog)
|
||||
r.Post("/clearErrorLog", php.ClearErrorLog)
|
||||
r.Post("/clearSlowLog", php.ClearSlowLog)
|
||||
r.Get("/extensions", php.ExtensionList)
|
||||
r.Post("/extensions", php.InstallExtension)
|
||||
r.Delete("/extensions", php.UninstallExtension)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) SetCli(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf("ln -sf %s/server/php/%d/bin/php /usr/bin/php", app.Root, s.version); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php.ini", app.Root, s.version))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -43,7 +71,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -58,7 +86,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) GetFPMConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetFPMConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php-fpm.conf", app.Root, s.version))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -68,7 +96,7 @@ func (s *Service) GetFPMConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateFPMConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateFPMConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -83,44 +111,41 @@ func (s *Service) UpdateFPMConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
var raw map[string]any
|
||||
client := resty.New().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get(fmt.Sprintf("http://127.0.0.1/phpfpm_status/%d", s.version))
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
service.Error(w, http.StatusInternalServerError, "获取负载状态失败:%v", err)
|
||||
_, err := client.R().SetResult(&raw).Get(fmt.Sprintf("http://127.0.0.1/phpfpm_status/%d?json", s.version))
|
||||
if err != nil {
|
||||
service.Success(w, []types.NV{})
|
||||
return
|
||||
}
|
||||
|
||||
raw := resp.String()
|
||||
dataKeys := []string{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"}
|
||||
regexKeys := []string{"pool", "process manager", "start time", "accepted conn", "listen queue", "max listen queue", "listen queue len", "idle processes", "active processes", "total processes", "max active processes", "max children reached", "slow requests"}
|
||||
rawKeys := []string{"pool", "process manager", "start time", "accepted conn", "listen queue", "max listen queue", "listen queue len", "idle processes", "active processes", "total processes", "max active processes", "max children reached", "slow requests"}
|
||||
|
||||
loads := make([]types.NV, len(dataKeys))
|
||||
loads := make([]types.NV, 0)
|
||||
for i := range dataKeys {
|
||||
loads[i].Name = dataKeys[i]
|
||||
|
||||
r := regexp.MustCompile(fmt.Sprintf("%s:\\s+(.*)", regexKeys[i]))
|
||||
match := r.FindStringSubmatch(raw)
|
||||
|
||||
if len(match) > 1 {
|
||||
loads[i].Value = strings.TrimSpace(match[1])
|
||||
v, ok := raw[rawKeys[i]]
|
||||
if ok {
|
||||
loads = append(loads, types.NV{
|
||||
Name: dataKeys[i],
|
||||
Value: cast.ToString(v),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
service.Success(w, loads)
|
||||
}
|
||||
|
||||
func (s *Service) ErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
log, _ := shell.Execf("tail -n 500 %s/server/php/%d/var/log/php-fpm.log", app.Root, s.version)
|
||||
service.Success(w, log)
|
||||
func (s *App) ErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, fmt.Sprintf("%s/server/php/%d/var/log/php-fpm.log", app.Root, s.version))
|
||||
}
|
||||
|
||||
func (s *Service) SlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
log, _ := shell.Execf("tail -n 500 %s/server/php/%d/var/log/slow.log", app.Root, s.version)
|
||||
service.Success(w, log)
|
||||
func (s *App) SlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, fmt.Sprintf("%s/server/php/%d/var/log/slow.log", app.Root, s.version))
|
||||
}
|
||||
|
||||
func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf("echo '' > %s/server/php/%d/var/log/php-fpm.log", app.Root, s.version); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -129,7 +154,7 @@ func (s *Service) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf("echo '' > %s/server/php/%d/var/log/slow.log", app.Root, s.version); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -138,19 +163,8 @@ func (s *Service) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) ExtensionList(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ExtensionList(w http.ResponseWriter, r *http.Request) {
|
||||
extensions := s.getExtensions()
|
||||
|
||||
// ionCube 只支持 PHP 8.3 以下版本
|
||||
if cast.ToUint(s.version) < 83 {
|
||||
extensions = append(extensions, Extension{
|
||||
Name: "ionCube",
|
||||
Slug: "ionCube Loader",
|
||||
Description: "ionCube 是一个专业级的 PHP 加密解密工具。",
|
||||
Installed: false,
|
||||
})
|
||||
}
|
||||
|
||||
raw, err := shell.Execf("%s/server/php/%d/bin/php -m", app.Root, s.version)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -172,7 +186,7 @@ func (s *Service) ExtensionList(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, extensions)
|
||||
}
|
||||
|
||||
func (s *Service) InstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) InstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ExtensionSlug](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -184,10 +198,10 @@ func (s *Service) InstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
cmd := fmt.Sprintf(`curl -fsLm 10 --retry 3 "https://dl.cdn.haozi.net/panel/php_exts/%s.sh" | bash -s -- "install" "%d" >> /tmp/%s.log 2>&1`, url.QueryEscape(req.Slug), s.version, req.Slug)
|
||||
officials := []string{"fileinfo", "exif", "imap", "pdo_pgsql", "zip", "bz2", "readline", "snmp", "ldap", "enchant", "pspell", "calendar", "gmp", "sysvmsg", "sysvsem", "sysvshm", "xsl", "intl", "gettext"}
|
||||
cmd := fmt.Sprintf(`curl -fsLm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/%s.sh' | bash -s -- 'install' '%d' >> '/tmp/%s.log' 2>&1`, url.PathEscape(req.Slug), s.version, req.Slug)
|
||||
officials := []string{"fileinfo", "exif", "imap", "pgsql", "pdo_pgsql", "zip", "bz2", "readline", "snmp", "ldap", "enchant", "pspell", "calendar", "gmp", "sysvmsg", "sysvsem", "sysvshm", "xsl", "intl", "gettext"}
|
||||
if slices.Contains(officials, req.Slug) {
|
||||
cmd = fmt.Sprintf(`curl -fsLm 10 --retry 3 "https://dl.cdn.haozi.net/panel/php_exts/official.sh" | bash -s -- "install" "%d" "%s" >> /tmp/%s.log 2>&1`, s.version, req.Slug, req.Slug)
|
||||
cmd = fmt.Sprintf(`curl -fsLm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/official.sh' | bash -s -- 'install' '%d' '%s' >> '/tmp/%s.log' 2>&1`, s.version, req.Slug, req.Slug)
|
||||
}
|
||||
|
||||
task := new(biz.Task)
|
||||
@@ -203,7 +217,7 @@ func (s *Service) InstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) UninstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UninstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ExtensionSlug](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -215,10 +229,10 @@ func (s *Service) UninstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
cmd := fmt.Sprintf(`curl -fsLm 10 --retry 3 "https://dl.cdn.haozi.net/panel/php_exts/%s.sh" | bash -s -- "uninstall" "%d" >> /tmp/%s.log 2>&1`, url.QueryEscape(req.Slug), s.version, req.Slug)
|
||||
officials := []string{"fileinfo", "exif", "imap", "pdo_pgsql", "zip", "bz2", "readline", "snmp", "ldap", "enchant", "pspell", "calendar", "gmp", "sysvmsg", "sysvsem", "sysvshm", "xsl", "intl", "gettext"}
|
||||
cmd := fmt.Sprintf(`curl -fsLm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/%s.sh' | bash -s -- 'uninstall' '%d' >> '/tmp/%s.log' 2>&1`, url.PathEscape(req.Slug), s.version, req.Slug)
|
||||
officials := []string{"fileinfo", "exif", "imap", "pgsql", "pdo_pgsql", "zip", "bz2", "readline", "snmp", "ldap", "enchant", "pspell", "calendar", "gmp", "sysvmsg", "sysvsem", "sysvshm", "xsl", "intl", "gettext"}
|
||||
if slices.Contains(officials, req.Slug) {
|
||||
cmd = fmt.Sprintf(`curl -fsLm 10 --retry 3 "https://dl.cdn.haozi.net/panel/php_exts/official.sh" | bash -s -- "uninstall" "%d" "%s" >> /tmp/%s.log 2>&1`, s.version, req.Slug, req.Slug)
|
||||
cmd = fmt.Sprintf(`curl -fsLm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/official.sh' | bash -s -- 'uninstall' '%d' '%s' >> '/tmp/%s.log' 2>&1`, s.version, req.Slug, req.Slug)
|
||||
}
|
||||
|
||||
task := new(biz.Task)
|
||||
@@ -234,142 +248,160 @@ func (s *Service) UninstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) getExtensions() []Extension {
|
||||
func (s *App) getExtensions() []Extension {
|
||||
extensions := []Extension{
|
||||
{
|
||||
Name: "fileinfo",
|
||||
Slug: "fileinfo",
|
||||
Description: "Fileinfo 是一个用于识别文件类型的库。",
|
||||
Description: "Fileinfo 是一个用于识别文件类型的库",
|
||||
},
|
||||
{
|
||||
Name: "OPcache",
|
||||
Slug: "Zend OPcache",
|
||||
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。",
|
||||
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销",
|
||||
},
|
||||
{
|
||||
Name: "PhpRedis",
|
||||
Name: "Redis",
|
||||
Slug: "redis",
|
||||
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据。",
|
||||
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据(需先安装 igbinary 拓展)",
|
||||
},
|
||||
{
|
||||
Name: "Memcached",
|
||||
Slug: "memcached",
|
||||
Description: "Memcached 使用 libmemcached 库连接 Memcached 服务器",
|
||||
},
|
||||
{
|
||||
Name: "ImageMagick",
|
||||
Slug: "imagick",
|
||||
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。",
|
||||
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件",
|
||||
},
|
||||
{
|
||||
Name: "exif",
|
||||
Slug: "exif",
|
||||
Description: "通过 exif 扩展,您可以操作图像元数据。",
|
||||
Description: "通过 exif 扩展,您可以操作图像元数据",
|
||||
},
|
||||
{
|
||||
Name: "pgsql",
|
||||
Slug: "pgsql",
|
||||
Description: "pgsql 是一个驱动程序,它启用从 PHP 到 PostgreSQL 数据库的访问(需先安装 PostgreSQL 应用)",
|
||||
},
|
||||
{
|
||||
Name: "pdo_pgsql",
|
||||
Slug: "pdo_pgsql",
|
||||
Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。",
|
||||
Description: "pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问(需先安装 PostgreSQL 应用)",
|
||||
},
|
||||
{
|
||||
Name: "imap",
|
||||
Slug: "imap",
|
||||
Description: "IMAP 扩展允许 PHP 读取、搜索、删除、下载和管理邮件。",
|
||||
Description: "IMAP 扩展允许 PHP 读取、搜索、删除、下载和管理邮件",
|
||||
},
|
||||
{
|
||||
Name: "zip",
|
||||
Slug: "zip",
|
||||
Description: "Zip 是一个用于处理 ZIP 文件的库。",
|
||||
Description: "Zip 是一个用于处理 ZIP 文件的库",
|
||||
},
|
||||
{
|
||||
Name: "bz2",
|
||||
Slug: "bz2",
|
||||
Description: "Bzip2 是一个用于压缩和解压缩文件的库。",
|
||||
Description: "Bzip2 是一个用于压缩和解压缩文件的库",
|
||||
},
|
||||
{
|
||||
Name: "readline",
|
||||
Slug: "readline",
|
||||
Description: "Readline 是一个库,它提供了一种用于处理文本的接口。",
|
||||
Description: "Readline 是一个库,它提供了一种用于处理文本的接口",
|
||||
},
|
||||
{
|
||||
Name: "snmp",
|
||||
Slug: "snmp",
|
||||
Description: "SNMP 是一种用于网络管理的协议。",
|
||||
Description: "SNMP 是一种用于网络管理的协议",
|
||||
},
|
||||
{
|
||||
Name: "ldap",
|
||||
Slug: "ldap",
|
||||
Description: "LDAP 是一种用于访问目录服务的协议。",
|
||||
Description: "LDAP 是一种用于访问目录服务的协议",
|
||||
},
|
||||
{
|
||||
Name: "enchant",
|
||||
Slug: "enchant",
|
||||
Description: "Enchant 是一个拼写检查库。",
|
||||
Description: "Enchant 是一个拼写检查库",
|
||||
},
|
||||
{
|
||||
Name: "pspell",
|
||||
Slug: "pspell",
|
||||
Description: "Pspell 是一个拼写检查库。",
|
||||
Description: "Pspell 是一个拼写检查库",
|
||||
},
|
||||
{
|
||||
Name: "calendar",
|
||||
Slug: "calendar",
|
||||
Description: "Calendar 是一个用于处理日期的库。",
|
||||
Description: "Calendar 是一个用于处理日期的库",
|
||||
},
|
||||
{
|
||||
Name: "gmp",
|
||||
Slug: "gmp",
|
||||
Description: "GMP 是一个用于处理大整数的库。",
|
||||
Description: "GMP 是一个用于处理大整数的库",
|
||||
},
|
||||
{
|
||||
Name: "sysvmsg",
|
||||
Slug: "sysvmsg",
|
||||
Description: "Sysvmsg 是一个用于处理 System V 消息队列的库。",
|
||||
Description: "Sysvmsg 是一个用于处理 System V 消息队列的库",
|
||||
},
|
||||
{
|
||||
Name: "sysvsem",
|
||||
Slug: "sysvsem",
|
||||
Description: "Sysvsem 是一个用于处理 System V 信号量的库。",
|
||||
Description: "Sysvsem 是一个用于处理 System V 信号量的库",
|
||||
},
|
||||
{
|
||||
Name: "sysvshm",
|
||||
Slug: "sysvshm",
|
||||
Description: "Sysvshm 是一个用于处理 System V 共享内存的库。",
|
||||
Description: "Sysvshm 是一个用于处理 System V 共享内存的库",
|
||||
},
|
||||
{
|
||||
Name: "xsl",
|
||||
Slug: "xsl",
|
||||
Description: "XSL 是一个用于处理 XML 文档的库。",
|
||||
Description: "XSL 是一个用于处理 XML 文档的库",
|
||||
},
|
||||
{
|
||||
Name: "intl",
|
||||
Slug: "intl",
|
||||
Description: "Intl 是一个用于处理国际化和本地化的库。",
|
||||
Description: "Intl 是一个用于处理国际化和本地化的库",
|
||||
},
|
||||
{
|
||||
Name: "gettext",
|
||||
Slug: "gettext",
|
||||
Description: "Gettext 是一个用于处理多语言的库。",
|
||||
Description: "Gettext 是一个用于处理多语言的库",
|
||||
},
|
||||
{
|
||||
Name: "igbinary",
|
||||
Slug: "igbinary",
|
||||
Description: "Igbinary 是一个用于序列化和反序列化数据的库。",
|
||||
},
|
||||
{
|
||||
Name: "Swoole",
|
||||
Slug: "swoole",
|
||||
Description: "Swoole 是一个用于构建高性能的异步并发服务器的 PHP 扩展。",
|
||||
},
|
||||
{
|
||||
Name: "Swow",
|
||||
Slug: "Swow",
|
||||
Description: "Swow 是一个用于构建高性能的异步并发服务器的 PHP 扩展。",
|
||||
Description: "Igbinary 是一个用于序列化和反序列化数据的库",
|
||||
},
|
||||
}
|
||||
|
||||
// ionCube 只支持 PHP 8.3 以下版本
|
||||
if cast.ToUint(s.version) < 83 {
|
||||
// ionCube Swoole 不支持 PHP 8.4
|
||||
if cast.ToUint(s.version) < 84 {
|
||||
extensions = append(extensions, Extension{
|
||||
Name: "ionCube",
|
||||
Slug: "ionCube Loader",
|
||||
Description: "ionCube 是一个专业级的 PHP 加密解密工具。",
|
||||
Installed: false,
|
||||
Description: "ionCube 是一个专业级的 PHP 加密解密工具(需在 OPcache 之后安装)",
|
||||
})
|
||||
extensions = append(extensions, Extension{
|
||||
Name: "Swoole",
|
||||
Slug: "swoole",
|
||||
Description: "Swoole 是一个用于构建高性能的异步并发服务器的 PHP 扩展",
|
||||
})
|
||||
}
|
||||
// Swow 不支持 PHP 8.0 以下版本且目前不支持 PHP 8.4
|
||||
if cast.ToUint(s.version) >= 80 && cast.ToUint(s.version) < 84 {
|
||||
extensions = append(extensions, Extension{
|
||||
Name: "Swow",
|
||||
Slug: "Swow",
|
||||
Description: "Swow 是一个用于构建高性能的异步并发服务器的 PHP 扩展",
|
||||
})
|
||||
}
|
||||
// PHP 8.4 移除了 pspell 和 imap 并且不再建议使用
|
||||
if cast.ToUint(s.version) >= 84 {
|
||||
extensions = slices.DeleteFunc(extensions, func(extension Extension) bool {
|
||||
return extension.Slug == "pspell" || extension.Slug == "imap"
|
||||
})
|
||||
}
|
||||
|
||||
@@ -389,7 +421,7 @@ func (s *Service) getExtensions() []Extension {
|
||||
return extensions
|
||||
}
|
||||
|
||||
func (s *Service) checkExtension(slug string) bool {
|
||||
func (s *App) checkExtension(slug string) bool {
|
||||
extensions := s.getExtensions()
|
||||
|
||||
for _, item := range extensions {
|
||||
@@ -1,83 +0,0 @@
|
||||
package php
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "php80",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService(80)
|
||||
r.Get("/load", service.Load)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
r.Get("/fpmConfig", service.GetFPMConfig)
|
||||
r.Post("/fpmConfig", service.UpdateFPMConfig)
|
||||
r.Get("/errorLog", service.ErrorLog)
|
||||
r.Get("/slowLog", service.SlowLog)
|
||||
r.Post("/clearErrorLog", service.ClearErrorLog)
|
||||
r.Post("/clearSlowLog", service.ClearSlowLog)
|
||||
r.Get("/extensions", service.ExtensionList)
|
||||
r.Post("/extensions", service.InstallExtension)
|
||||
r.Delete("/extensions", service.UninstallExtension)
|
||||
},
|
||||
})
|
||||
apploader.Register(&types.App{
|
||||
Slug: "php81",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService(81)
|
||||
r.Get("/load", service.Load)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
r.Get("/fpmConfig", service.GetFPMConfig)
|
||||
r.Post("/fpmConfig", service.UpdateFPMConfig)
|
||||
r.Get("/errorLog", service.ErrorLog)
|
||||
r.Get("/slowLog", service.SlowLog)
|
||||
r.Post("/clearErrorLog", service.ClearErrorLog)
|
||||
r.Post("/clearSlowLog", service.ClearSlowLog)
|
||||
r.Get("/extensions", service.ExtensionList)
|
||||
r.Post("/extensions", service.InstallExtension)
|
||||
r.Delete("/extensions", service.UninstallExtension)
|
||||
},
|
||||
})
|
||||
apploader.Register(&types.App{
|
||||
Slug: "php82",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService(82)
|
||||
r.Get("/load", service.Load)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
r.Get("/fpmConfig", service.GetFPMConfig)
|
||||
r.Post("/fpmConfig", service.UpdateFPMConfig)
|
||||
r.Get("/errorLog", service.ErrorLog)
|
||||
r.Get("/slowLog", service.SlowLog)
|
||||
r.Post("/clearErrorLog", service.ClearErrorLog)
|
||||
r.Post("/clearSlowLog", service.ClearSlowLog)
|
||||
r.Get("/extensions", service.ExtensionList)
|
||||
r.Post("/extensions", service.InstallExtension)
|
||||
r.Delete("/extensions", service.UninstallExtension)
|
||||
},
|
||||
})
|
||||
apploader.Register(&types.App{
|
||||
Slug: "php83",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService(83)
|
||||
r.Get("/load", service.Load)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
r.Get("/fpmConfig", service.GetFPMConfig)
|
||||
r.Post("/fpmConfig", service.UpdateFPMConfig)
|
||||
r.Get("/errorLog", service.ErrorLog)
|
||||
r.Get("/slowLog", service.SlowLog)
|
||||
r.Post("/clearErrorLog", service.ClearErrorLog)
|
||||
r.Post("/clearSlowLog", service.ClearSlowLog)
|
||||
r.Get("/extensions", service.ExtensionList)
|
||||
r.Post("/extensions", service.InstallExtension)
|
||||
r.Delete("/extensions", service.UninstallExtension)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package php74
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/apps/php"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(74)(r)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package php80
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/apps/php"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(80)(r)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package php81
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/apps/php"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(81)(r)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package php82
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/apps/php"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(82)(r)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package php83
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/apps/php"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(83)(r)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package php84
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/apps/php"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(84)(r)
|
||||
}
|
||||
@@ -3,28 +3,37 @@ package phpmyadmin
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/firewall"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/firewall"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
type App struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *Service) Info(w http.ResponseWriter, r *http.Request) {
|
||||
files, err := io.ReadDir(fmt.Sprintf("%s/server/phpmyadmin", app.Root))
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/info", s.Info)
|
||||
r.Post("/port", s.UpdatePort)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
}
|
||||
|
||||
func (s *App) Info(w http.ResponseWriter, r *http.Request) {
|
||||
files, err := os.ReadDir(fmt.Sprintf("%s/server/phpmyadmin", app.Root))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
|
||||
return
|
||||
@@ -58,7 +67,7 @@ func (s *Service) Info(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdatePort](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -78,9 +87,11 @@ func (s *Service) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
fw := firewall.NewFirewall()
|
||||
err = fw.Port(firewall.FireInfo{
|
||||
Type: firewall.TypeNormal,
|
||||
PortStart: req.Port,
|
||||
PortEnd: req.Port,
|
||||
Protocol: "tcp",
|
||||
Direction: firewall.DirectionIn,
|
||||
Strategy: firewall.StrategyAccept,
|
||||
}, firewall.OperationAdd)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -96,7 +107,7 @@ func (s *Service) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read(fmt.Sprintf("%s/server/vhost/phpmyadmin.conf", app.Root))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -106,7 +117,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -1,21 +0,0 @@
|
||||
package phpmyadmin
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "phpmyadmin",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/info", service.Info)
|
||||
r.Post("/port", service.UpdatePort)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -5,5 +5,5 @@ type UpdateConfig struct {
|
||||
}
|
||||
|
||||
type UpdatePort struct {
|
||||
Port uint `form:"port" json:"port" validate:"required,number,gte=1,lte=65535"`
|
||||
Port uint `form:"port" json:"port" validate:"required|number|min:1|max:65535"`
|
||||
}
|
||||
|
||||
@@ -3,18 +3,27 @@ package podman
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
type App struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *Service) GetRegistryConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/registryConfig", s.GetRegistryConfig)
|
||||
r.Post("/registryConfig", s.UpdateRegistryConfig)
|
||||
r.Get("/storageConfig", s.GetStorageConfig)
|
||||
r.Post("/storageConfig", s.UpdateStorageConfig)
|
||||
}
|
||||
|
||||
func (s *App) GetRegistryConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read("/etc/containers/registries.conf")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -24,7 +33,7 @@ func (s *Service) GetRegistryConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateRegistryConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateRegistryConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -44,7 +53,7 @@ func (s *Service) UpdateRegistryConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) GetStorageConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetStorageConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read("/etc/containers/storage.conf")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -54,7 +63,7 @@ func (s *Service) GetStorageConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateStorageConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateStorageConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -1,21 +0,0 @@
|
||||
package podman
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "podman",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/registryConfig", service.GetRegistryConfig)
|
||||
r.Post("/registryConfig", service.UpdateRegistryConfig)
|
||||
r.Get("/storageConfig", service.GetStorageConfig)
|
||||
r.Post("/storageConfig", service.UpdateStorageConfig)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -5,22 +5,34 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
type App struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
r.Get("/userConfig", s.GetUserConfig)
|
||||
r.Post("/userConfig", s.UpdateUserConfig)
|
||||
r.Get("/load", s.Load)
|
||||
r.Get("/log", s.Log)
|
||||
r.Post("/clearLog", s.ClearLog)
|
||||
}
|
||||
|
||||
// GetConfig 获取配置
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
// 获取配置
|
||||
config, err := io.Read(fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root))
|
||||
if err != nil {
|
||||
@@ -32,20 +44,20 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdateConfig 保存配置
|
||||
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := io.Write(fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root), req.Config, 0644); err != nil {
|
||||
if err = io.Write(fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root), req.Config, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入PostgreSQL配置失败")
|
||||
return
|
||||
}
|
||||
|
||||
if err := systemctl.Reload("postgresql"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重载服务失败")
|
||||
if err = systemctl.Reload("postgresql"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重载服务失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,7 +65,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// GetUserConfig 获取用户配置
|
||||
func (s *Service) GetUserConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetUserConfig(w http.ResponseWriter, r *http.Request) {
|
||||
// 获取配置
|
||||
config, err := io.Read(fmt.Sprintf("%s/server/postgresql/data/pg_hba.conf", app.Root))
|
||||
if err != nil {
|
||||
@@ -65,20 +77,20 @@ func (s *Service) GetUserConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdateUserConfig 保存用户配置
|
||||
func (s *Service) UpdateUserConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateUserConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := io.Write(fmt.Sprintf("%s/server/postgresql/data/pg_hba.conf", app.Root), req.Config, 0644); err != nil {
|
||||
if err = io.Write(fmt.Sprintf("%s/server/postgresql/data/pg_hba.conf", app.Root), req.Config, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入PostgreSQL配置失败")
|
||||
return
|
||||
}
|
||||
|
||||
if err := systemctl.Reload("postgresql"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重载服务失败")
|
||||
if err = systemctl.Reload("postgresql"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重载服务失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,7 +98,7 @@ func (s *Service) UpdateUserConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Load 获取负载
|
||||
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
status, _ := systemctl.Status("postgresql")
|
||||
if !status {
|
||||
service.Success(w, []types.NV{})
|
||||
@@ -131,13 +143,12 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Log 获取日志
|
||||
func (s *Service) Log(w http.ResponseWriter, r *http.Request) {
|
||||
log, _ := shell.Execf("tail -n 100 %s/server/postgresql/logs/postgresql-%s.log", app.Root, time.Now().Format(time.DateOnly))
|
||||
service.Success(w, log)
|
||||
func (s *App) Log(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, fmt.Sprintf("%s/server/postgresql/logs/postgresql-%s.log", app.Root, time.Now().Format(time.DateOnly)))
|
||||
}
|
||||
|
||||
// ClearLog 清空日志
|
||||
func (s *Service) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf("rm -rf %s/server/postgresql/logs/postgresql-*.log", app.Root); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -1,24 +0,0 @@
|
||||
package postgresql
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "postgresql",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
r.Get("/userConfig", service.GetUserConfig)
|
||||
r.Post("/userConfig", service.UpdateUserConfig)
|
||||
r.Get("/load", service.Load)
|
||||
r.Get("/log", service.Log)
|
||||
r.Post("/clearLog", service.ClearLog)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -5,25 +5,35 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/firewall"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/firewall"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
type App struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/users", s.List)
|
||||
r.Post("/users", s.Create)
|
||||
r.Delete("/users/{username}", s.Delete)
|
||||
r.Post("/users/{username}/password", s.ChangePassword)
|
||||
r.Get("/port", s.GetPort)
|
||||
r.Post("/port", s.UpdatePort)
|
||||
}
|
||||
|
||||
// List 获取用户列表
|
||||
func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) List(w http.ResponseWriter, r *http.Request) {
|
||||
listRaw, err := shell.Execf("pure-pw list")
|
||||
if err != nil {
|
||||
service.Success(w, chix.M{
|
||||
@@ -55,7 +65,7 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Create 创建用户
|
||||
func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Create(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Create](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -91,7 +101,7 @@ func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Delete 删除用户
|
||||
func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Delete](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -111,7 +121,7 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
func (s *Service) ChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ChangePassword](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -131,7 +141,7 @@ func (s *Service) ChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// GetPort 获取端口
|
||||
func (s *Service) GetPort(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetPort(w http.ResponseWriter, r *http.Request) {
|
||||
port, err := shell.Execf(`cat %s/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`, app.Root)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取PureFtpd端口失败")
|
||||
@@ -142,7 +152,7 @@ func (s *Service) GetPort(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdatePort 设置端口
|
||||
func (s *Service) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdatePort](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -156,9 +166,11 @@ func (s *Service) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
fw := firewall.NewFirewall()
|
||||
err = fw.Port(firewall.FireInfo{
|
||||
Type: firewall.TypeNormal,
|
||||
PortStart: req.Port,
|
||||
PortEnd: req.Port,
|
||||
Protocol: "tcp",
|
||||
Direction: firewall.DirectionIn,
|
||||
Strategy: firewall.StrategyAccept,
|
||||
}, firewall.OperationAdd)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -1,23 +0,0 @@
|
||||
package pureftpd
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "pureftpd",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/users", service.List)
|
||||
r.Post("/users", service.Create)
|
||||
r.Delete("/users/{username}", service.Delete)
|
||||
r.Post("/users/{username}/password", service.ChangePassword)
|
||||
r.Get("/port", service.GetPort)
|
||||
r.Post("/port", service.UpdatePort)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package pureftpd
|
||||
|
||||
type Create struct {
|
||||
Username string `form:"username" json:"username" validate:"required"`
|
||||
Password string `form:"password" json:"password" validate:"required"`
|
||||
Password string `form:"password" json:"password" validate:"required|password"`
|
||||
Path string `form:"path" json:"path" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ type Delete struct {
|
||||
|
||||
type ChangePassword struct {
|
||||
Username string `form:"username" json:"username" validate:"required"`
|
||||
Password string `form:"password" json:"password" validate:"required"`
|
||||
Password string `form:"password" json:"password" validate:"required|password"`
|
||||
}
|
||||
|
||||
type UpdatePort struct {
|
||||
Port uint `form:"port" json:"port" validate:"required"`
|
||||
Port uint `form:"port" json:"port" validate:"required|number|min:1|max:65535"`
|
||||
}
|
||||
|
||||
@@ -3,23 +3,32 @@ package redis
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
type App struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/load", s.Load)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
}
|
||||
|
||||
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := systemctl.Status("redis")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 Redis 状态失败")
|
||||
@@ -30,7 +39,20 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
raw, err := shell.Execf("redis-cli info")
|
||||
// 检查 Redis 密码
|
||||
withPassword := ""
|
||||
config, err := io.Read(fmt.Sprintf("%s/server/redis/redis.conf", app.Root))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
re := regexp.MustCompile(`^requirepass\s+(.+)`)
|
||||
matches := re.FindStringSubmatch(config)
|
||||
if len(matches) == 2 {
|
||||
withPassword = " -a " + matches[1]
|
||||
}
|
||||
|
||||
raw, err := shell.Execf("redis-cli%s info", withPassword)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 Redis 负载失败")
|
||||
return
|
||||
@@ -65,7 +87,7 @@ func (s *Service) Load(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, data)
|
||||
}
|
||||
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read(fmt.Sprintf("%s/server/redis/redis.conf", app.Root))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -75,7 +97,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -1,20 +0,0 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "redis",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/load", service.Load)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -6,22 +6,32 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/go-rat/utils/str"
|
||||
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
type App struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/modules", s.List)
|
||||
r.Post("/modules", s.Create)
|
||||
r.Post("/modules/{name}", s.Update)
|
||||
r.Delete("/modules/{name}", s.Delete)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
}
|
||||
|
||||
func (s *App) List(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read("/etc/rsyncd.conf")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -86,7 +96,7 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Create(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Create](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -114,11 +124,11 @@ secrets file = /etc/rsyncd.secrets
|
||||
# ` + req.Name + `-END
|
||||
`
|
||||
|
||||
if err = io.WriteAppend("/etc/rsyncd.conf", conf); err != nil {
|
||||
if err = io.WriteAppend("/etc/rsyncd.conf", conf, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if err = io.WriteAppend("/etc/rsyncd.secrets", fmt.Sprintf(`%s:%s\n`, req.AuthUser, req.Secret)); err != nil {
|
||||
if err = io.WriteAppend("/etc/rsyncd.secrets", fmt.Sprintf(`%s:%s\n`, req.AuthUser, req.Secret), 0600); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -131,7 +141,7 @@ secrets file = /etc/rsyncd.secrets
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Delete](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -173,7 +183,7 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) Update(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Update(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Update](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -216,7 +226,7 @@ secrets file = /etc/rsyncd.secrets
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if err = io.WriteAppend("/etc/rsyncd.secrets", fmt.Sprintf(`%s:%s\n`, req.AuthUser, req.Secret)); err != nil {
|
||||
if err = io.WriteAppend("/etc/rsyncd.secrets", fmt.Sprintf(`%s:%s\n`, req.AuthUser, req.Secret), 0600); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -229,7 +239,7 @@ secrets file = /etc/rsyncd.secrets
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
config, err := io.Read("/etc/rsyncd.conf")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -239,7 +249,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, config)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -1,23 +0,0 @@
|
||||
package rsync
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "rsync",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/modules", service.List)
|
||||
r.Post("/modules", service.Create)
|
||||
r.Post("/modules/{name}", service.Update)
|
||||
r.Delete("/modules/{name}", service.Delete)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -3,40 +3,47 @@ package s3fs
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/internal/biz"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
type App struct {
|
||||
settingRepo biz.SettingRepo
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{
|
||||
settingRepo: data.NewSettingRepo(),
|
||||
func NewApp(setting biz.SettingRepo) *App {
|
||||
return &App{
|
||||
settingRepo: setting,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/mounts", s.List)
|
||||
r.Post("/mounts", s.Create)
|
||||
r.Delete("/mounts", s.Delete)
|
||||
}
|
||||
|
||||
// List 所有 S3fs 挂载
|
||||
func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) List(w http.ResponseWriter, r *http.Request) {
|
||||
var s3fsList []Mount
|
||||
list, err := s.settingRepo.Get("s3fs", "[]")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to get s3fs list")
|
||||
return
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(list), &s3fsList); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to get s3fs list")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -49,7 +56,7 @@ func (s *Service) List(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Create 添加 S3fs 挂载
|
||||
func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Create(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Create](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -58,36 +65,36 @@ func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// 检查下地域节点中是否包含bucket,如果包含了,肯定是错误的
|
||||
if strings.Contains(req.URL, req.Bucket) {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "地域节点不能包含 Bucket 名称")
|
||||
service.Error(w, http.StatusUnprocessableEntity, "endpoint should not contain bucket")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查挂载目录是否存在且为空
|
||||
if !io.Exists(req.Path) {
|
||||
if err = io.Mkdir(req.Path, 0755); err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "挂载目录创建失败")
|
||||
if err = os.MkdirAll(req.Path, 0755); err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "failed to create mount path: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if !io.Empty(req.Path) {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "挂载目录必须为空")
|
||||
service.Error(w, http.StatusUnprocessableEntity, "mount path is not empty")
|
||||
return
|
||||
}
|
||||
|
||||
var s3fsList []Mount
|
||||
list, err := s.settingRepo.Get("s3fs", "[]")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to get s3fs list")
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal([]byte(list), &s3fsList); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to get s3fs list")
|
||||
return
|
||||
}
|
||||
|
||||
for _, s := range s3fsList {
|
||||
if s.Path == req.Path {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "路径已存在")
|
||||
service.Error(w, http.StatusUnprocessableEntity, "mount path already exists")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -95,21 +102,21 @@ func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
|
||||
id := time.Now().UnixMicro()
|
||||
password := req.Ak + ":" + req.Sk
|
||||
if err = io.Write("/etc/passwd-s3fs-"+cast.ToString(id), password, 0600); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "添加 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to create passwd file: %v", err)
|
||||
return
|
||||
}
|
||||
if _, err = shell.Execf(`echo 's3fs#%s %s fuse _netdev,allow_other,nonempty,url=%s,passwd_file=/etc/passwd-s3fs-%s 0 0' >> /etc/fstab`, req.Bucket, req.Path, req.URL, cast.ToString(id)); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if mountCheck, err := shell.Execf("mount -a"); err != nil {
|
||||
if _, err = shell.Execf("mount -a"); err != nil {
|
||||
_, _ = shell.Execf(`sed -i 's@^s3fs#%s\s%s.*$@@g' /etc/fstab`, req.Bucket, req.Path)
|
||||
service.Error(w, http.StatusInternalServerError, "/etc/fstab有误:%s", mountCheck)
|
||||
service.Error(w, http.StatusInternalServerError, "invalid /etc/fstab: %v", err)
|
||||
return
|
||||
}
|
||||
if _, err := shell.Execf(`df -h | grep '%s'`, req.Path); err != nil {
|
||||
if _, err = shell.Execf(`df -h | grep '%s'`, req.Path); err != nil {
|
||||
_, _ = shell.Execf(`sed -i 's@^s3fs#%s\s%s.*$@@g' /etc/fstab`, req.Bucket, req.Path)
|
||||
service.Error(w, http.StatusInternalServerError, "挂载失败,请检查配置是否正确")
|
||||
service.Error(w, http.StatusInternalServerError, "mount failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -121,12 +128,12 @@ func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
encoded, err := json.Marshal(s3fsList)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "添加 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to add s3fs mount")
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.settingRepo.Set("s3fs", string(encoded)); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "添加 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to add s3fs mount")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -134,7 +141,7 @@ func (s *Service) Create(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Delete 删除 S3fs 挂载
|
||||
func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Delete](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -144,11 +151,11 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
var s3fsList []Mount
|
||||
list, err := s.settingRepo.Get("s3fs", "[]")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to get s3fs list")
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal([]byte(list), &s3fsList); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to get s3fs list")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -160,15 +167,15 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
if mount.ID == 0 {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "挂载不存在")
|
||||
service.Error(w, http.StatusUnprocessableEntity, "mount not found")
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`fusermount -u '%s'`, mount.Path); err != nil {
|
||||
if _, err = shell.Execf(`fusermount -uz '%s'`, mount.Path); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if _, err = shell.Execf(`umount '%s'`, mount.Path); err != nil {
|
||||
if _, err = shell.Execf(`umount -lfq '%s'`, mount.Path); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -176,8 +183,8 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if mountCheck, err := shell.Execf("mount -a"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "/etc/fstab有误:%s", mountCheck)
|
||||
if _, err = shell.Execf("mount -a"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "invalid /etc/fstab: %v", err)
|
||||
return
|
||||
}
|
||||
if err = io.Remove("/etc/passwd-s3fs-" + cast.ToString(mount.ID)); err != nil {
|
||||
@@ -193,11 +200,11 @@ func (s *Service) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
encoded, err := json.Marshal(newS3fsList)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "删除 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to delete s3fs mount")
|
||||
return
|
||||
}
|
||||
if err = s.settingRepo.Set("s3fs", string(encoded)); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "删除 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, "failed to delete s3fs mount")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package s3fs
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "s3fs",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/mounts", service.List)
|
||||
r.Post("/mounts", service.Create)
|
||||
r.Delete("/mounts", service.Delete)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -4,21 +4,22 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/os"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/os"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
type App struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
func NewApp() *App {
|
||||
var name string
|
||||
if os.IsRHEL() {
|
||||
name = "supervisord"
|
||||
@@ -26,24 +27,35 @@ func NewService() *Service {
|
||||
name = "supervisor"
|
||||
}
|
||||
|
||||
return &Service{
|
||||
return &App{
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/service", s.Service)
|
||||
r.Post("/clearLog", s.ClearLog)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
r.Get("/processes", s.Processes)
|
||||
r.Post("/processes/{process}/start", s.StartProcess)
|
||||
r.Post("/processes/{process}/stop", s.StopProcess)
|
||||
r.Post("/processes/{process}/restart", s.RestartProcess)
|
||||
r.Get("/processes/{process}/log", s.ProcessLog)
|
||||
r.Post("/processes/{process}/clearLog", s.ClearProcessLog)
|
||||
r.Get("/processes/{process}", s.ProcessConfig)
|
||||
r.Post("/processes/{process}", s.UpdateProcessConfig)
|
||||
r.Delete("/processes/{process}", s.DeleteProcess)
|
||||
r.Post("/processes", s.CreateProcess)
|
||||
}
|
||||
|
||||
// Service 获取服务名称
|
||||
func (s *Service) Service(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Service(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, s.name)
|
||||
}
|
||||
|
||||
// Log 日志
|
||||
func (s *Service) Log(w http.ResponseWriter, r *http.Request) {
|
||||
log, _ := shell.Execf(`tail -n 200 /var/log/supervisor/supervisord.log`)
|
||||
service.Success(w, log)
|
||||
}
|
||||
|
||||
// ClearLog 清空日志
|
||||
func (s *Service) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf(`echo "" > /var/log/supervisor/supervisord.log`); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -53,7 +65,7 @@ func (s *Service) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// GetConfig 获取配置
|
||||
func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
var config string
|
||||
var err error
|
||||
if os.IsRHEL() {
|
||||
@@ -71,7 +83,7 @@ func (s *Service) GetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdateConfig 保存配置
|
||||
func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -90,7 +102,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = systemctl.Restart(s.name); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重启 %s 服务失败", s.name)
|
||||
service.Error(w, http.StatusInternalServerError, "重启 %s 服务失败: %v", s.name, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -98,7 +110,7 @@ func (s *Service) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Processes 进程列表
|
||||
func (s *Service) Processes(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) Processes(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := shell.Execf(`supervisorctl status | awk '{print $1}'`)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -137,7 +149,7 @@ func (s *Service) Processes(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// StartProcess 启动进程
|
||||
func (s *Service) StartProcess(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) StartProcess(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ProcessName](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -153,7 +165,7 @@ func (s *Service) StartProcess(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// StopProcess 停止进程
|
||||
func (s *Service) StopProcess(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) StopProcess(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ProcessName](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -169,7 +181,7 @@ func (s *Service) StopProcess(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// RestartProcess 重启进程
|
||||
func (s *Service) RestartProcess(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) RestartProcess(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ProcessName](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -185,7 +197,7 @@ func (s *Service) RestartProcess(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// ProcessLog 进程日志
|
||||
func (s *Service) ProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ProcessName](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -204,12 +216,11 @@ func (s *Service) ProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log, _ := shell.Execf(`tail -n 200 '%s'`, logPath)
|
||||
service.Success(w, log)
|
||||
service.Success(w, logPath)
|
||||
}
|
||||
|
||||
// ClearProcessLog 清空进程日志
|
||||
func (s *Service) ClearProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ClearProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ProcessName](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -237,7 +248,7 @@ func (s *Service) ClearProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// ProcessConfig 获取进程配置
|
||||
func (s *Service) ProcessConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) ProcessConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ProcessName](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -260,7 +271,7 @@ func (s *Service) ProcessConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdateProcessConfig 保存进程配置
|
||||
func (s *Service) UpdateProcessConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateProcessConfig(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[UpdateProcessConfig](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -286,7 +297,7 @@ func (s *Service) UpdateProcessConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// CreateProcess 添加进程
|
||||
func (s *Service) CreateProcess(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) CreateProcess(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[CreateProcess](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -325,7 +336,7 @@ stdout_logfile_maxbytes=2MB
|
||||
}
|
||||
|
||||
// DeleteProcess 删除进程
|
||||
func (s *Service) DeleteProcess(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) DeleteProcess(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[ProcessName](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -1,32 +0,0 @@
|
||||
package supervisor
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "supervisor",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/service", service.Service)
|
||||
r.Get("/log", service.Log)
|
||||
r.Post("/clearLog", service.ClearLog)
|
||||
r.Get("/config", service.GetConfig)
|
||||
r.Post("/config", service.UpdateConfig)
|
||||
r.Get("/processes", service.Processes)
|
||||
r.Post("/processes/{process}/start", service.StartProcess)
|
||||
r.Post("/processes/{process}/stop", service.StopProcess)
|
||||
r.Post("/processes/{process}/restart", service.RestartProcess)
|
||||
r.Get("/processes/{process}/log", service.ProcessLog)
|
||||
r.Post("/processes/{process}/clearLog", service.ClearProcessLog)
|
||||
r.Get("/processes/{process}", service.ProcessConfig)
|
||||
r.Post("/processes/{process}", service.UpdateProcessConfig)
|
||||
r.Delete("/processes/{process}", service.DeleteProcess)
|
||||
r.Post("/processes", service.CreateProcess)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -18,5 +18,5 @@ type CreateProcess struct {
|
||||
User string `form:"user" json:"user" validate:"required"`
|
||||
Path string `form:"path" json:"path" validate:"required"`
|
||||
Command string `form:"command" json:"command" validate:"required"`
|
||||
Num int `form:"num" json:"num" validate:"required,gte=1"`
|
||||
Num int `form:"num" json:"num" validate:"required|min:1"`
|
||||
}
|
||||
|
||||
@@ -6,25 +6,43 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/service"
|
||||
"github.com/tnb-labs/panel/pkg/io"
|
||||
"github.com/tnb-labs/panel/pkg/ntp"
|
||||
"github.com/tnb-labs/panel/pkg/shell"
|
||||
"github.com/tnb-labs/panel/pkg/tools"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Service struct{}
|
||||
type App struct{}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/dns", s.GetDNS)
|
||||
r.Post("/dns", s.UpdateDNS)
|
||||
r.Get("/swap", s.GetSWAP)
|
||||
r.Post("/swap", s.UpdateSWAP)
|
||||
r.Get("/timezone", s.GetTimezone)
|
||||
r.Post("/timezone", s.UpdateTimezone)
|
||||
r.Post("/time", s.UpdateTime)
|
||||
r.Post("/syncTime", s.SyncTime)
|
||||
r.Get("/hostname", s.GetHostname)
|
||||
r.Post("/hostname", s.UpdateHostname)
|
||||
r.Get("/hosts", s.GetHosts)
|
||||
r.Post("/hosts", s.UpdateHosts)
|
||||
r.Post("/rootPassword", s.UpdateRootPassword)
|
||||
}
|
||||
|
||||
// GetDNS 获取 DNS 信息
|
||||
func (s *Service) GetDNS(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetDNS(w http.ResponseWriter, r *http.Request) {
|
||||
raw, err := io.Read("/etc/resolv.conf")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -45,7 +63,7 @@ func (s *Service) GetDNS(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdateDNS 设置 DNS 信息
|
||||
func (s *Service) UpdateDNS(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateDNS(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[DNS](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -65,7 +83,7 @@ func (s *Service) UpdateDNS(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// GetSWAP 获取 SWAP 信息
|
||||
func (s *Service) GetSWAP(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetSWAP(w http.ResponseWriter, r *http.Request) {
|
||||
var total, used, free string
|
||||
var size int64
|
||||
if io.Exists(filepath.Join(app.Root, "swap")) {
|
||||
@@ -76,7 +94,7 @@ func (s *Service) GetSWAP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
size = file.Size() / 1024 / 1024
|
||||
total = str.FormatBytes(float64(file.Size()))
|
||||
total = tools.FormatBytes(float64(file.Size()))
|
||||
} else {
|
||||
size = 0
|
||||
total = "0.00 B"
|
||||
@@ -90,8 +108,8 @@ func (s *Service) GetSWAP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
match := regexp.MustCompile(`Swap:\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
|
||||
if len(match) >= 4 {
|
||||
used = str.FormatBytes(cast.ToFloat64(match[2]) * 1024)
|
||||
free = str.FormatBytes(cast.ToFloat64(match[3]) * 1024)
|
||||
used = tools.FormatBytes(cast.ToFloat64(match[2]) * 1024)
|
||||
free = tools.FormatBytes(cast.ToFloat64(match[3]) * 1024)
|
||||
}
|
||||
|
||||
service.Success(w, chix.M{
|
||||
@@ -103,7 +121,7 @@ func (s *Service) GetSWAP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdateSWAP 设置 SWAP 信息
|
||||
func (s *Service) UpdateSWAP(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateSWAP(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[SWAP](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -133,7 +151,7 @@ func (s *Service) UpdateSWAP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if cast.ToInt64(free)*1024 < req.Size*1024*1024 {
|
||||
service.Error(w, http.StatusInternalServerError, "硬盘空间不足,当前剩余%s", str.FormatBytes(cast.ToFloat64(free)))
|
||||
service.Error(w, http.StatusInternalServerError, "硬盘空间不足,当前剩余%s", tools.FormatBytes(cast.ToFloat64(free)))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -171,7 +189,7 @@ func (s *Service) UpdateSWAP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// GetTimezone 获取时区
|
||||
func (s *Service) GetTimezone(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetTimezone(w http.ResponseWriter, r *http.Request) {
|
||||
raw, err := shell.Execf("timedatectl | grep zone")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取时区信息失败")
|
||||
@@ -206,7 +224,7 @@ func (s *Service) GetTimezone(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdateTimezone 设置时区
|
||||
func (s *Service) UpdateTimezone(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateTimezone(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Timezone](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -221,14 +239,72 @@ func (s *Service) UpdateTimezone(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// UpdateTime 设置时间
|
||||
func (s *App) UpdateTime(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Time](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = ntp.UpdateSystemTime(req.Time); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
|
||||
}
|
||||
|
||||
// SyncTime 同步时间
|
||||
func (s *App) SyncTime(w http.ResponseWriter, r *http.Request) {
|
||||
now, err := ntp.Now()
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = ntp.UpdateSystemTime(now); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// GetHostname 获取主机名
|
||||
func (s *App) GetHostname(w http.ResponseWriter, r *http.Request) {
|
||||
hostname, _ := io.Read("/etc/hostname")
|
||||
service.Success(w, strings.TrimSpace(hostname))
|
||||
}
|
||||
|
||||
// UpdateHostname 设置主机名
|
||||
func (s *App) UpdateHostname(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Hostname](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf("hostnamectl set-hostname '%s'", req.Hostname); err != nil {
|
||||
// 直接写 /etc/hostname
|
||||
if err = io.Write("/etc/hostname", req.Hostname, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入主机名失败")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// GetHosts 获取 hosts 信息
|
||||
func (s *Service) GetHosts(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) GetHosts(w http.ResponseWriter, r *http.Request) {
|
||||
hosts, _ := io.Read("/etc/hosts")
|
||||
service.Success(w, hosts)
|
||||
}
|
||||
|
||||
// UpdateHosts 设置 hosts 信息
|
||||
func (s *Service) UpdateHosts(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateHosts(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Hosts](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
@@ -244,18 +320,13 @@ func (s *Service) UpdateHosts(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// UpdateRootPassword 设置 root 密码
|
||||
func (s *Service) UpdateRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *App) UpdateRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
req, err := service.Bind[Password](r)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !regexp.MustCompile(`^[a-zA-Z0-9·~!@#$%^&*()_+-=\[\]{};:'",./<>?]{6,20}$`).MatchString(req.Password) {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "密码必须为 6-20 位字母、数字或特殊字符")
|
||||
return
|
||||
}
|
||||
|
||||
req.Password = strings.ReplaceAll(req.Password, `'`, `\'`)
|
||||
if _, err = shell.Execf(`yes '%s' | passwd root`, req.Password); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
@@ -1,26 +0,0 @@
|
||||
package toolbox
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/apploader"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apploader.Register(&types.App{
|
||||
Slug: "toolbox",
|
||||
Route: func(r chi.Router) {
|
||||
service := NewService()
|
||||
r.Get("/dns", service.GetDNS)
|
||||
r.Post("/dns", service.UpdateDNS)
|
||||
r.Get("/swap", service.GetSWAP)
|
||||
r.Post("/swap", service.UpdateSWAP)
|
||||
r.Get("/timezone", service.GetTimezone)
|
||||
r.Post("/timezone", service.UpdateTimezone)
|
||||
r.Get("/hosts", service.GetHosts)
|
||||
r.Post("/hosts", service.UpdateHosts)
|
||||
r.Post("/rootPassword", service.UpdateRootPassword)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,22 +1,32 @@
|
||||
package toolbox
|
||||
|
||||
import "time"
|
||||
|
||||
type DNS struct {
|
||||
DNS1 string `form:"dns1" json:"dns1" validate:"required"`
|
||||
DNS2 string `form:"dns2" json:"dns2" validate:"required"`
|
||||
}
|
||||
|
||||
type SWAP struct {
|
||||
Size int64 `form:"size" json:"size" validate:"gte=0"`
|
||||
Size int64 `form:"size" json:"size" validate:"min:0"`
|
||||
}
|
||||
|
||||
type Timezone struct {
|
||||
Timezone string `form:"timezone" json:"timezone" validate:"required"`
|
||||
}
|
||||
|
||||
type Time struct {
|
||||
Time time.Time `form:"time" json:"time" validate:"required"`
|
||||
}
|
||||
|
||||
type Hostname struct {
|
||||
Hostname string `form:"hostname" json:"hostname" validate:"required|regex:^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$"`
|
||||
}
|
||||
|
||||
type Hosts struct {
|
||||
Hosts string `form:"hosts" json:"hosts"`
|
||||
}
|
||||
|
||||
type Password struct {
|
||||
Password string `form:"password" json:"password" validate:"required"`
|
||||
Password string `form:"password" json:"password" validate:"required|password"`
|
||||
}
|
||||
|
||||
+1
-2
@@ -3,7 +3,7 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/api"
|
||||
"github.com/tnb-labs/panel/pkg/api"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
@@ -30,5 +30,4 @@ type AppRepo interface {
|
||||
UnInstall(slug string) error
|
||||
Update(slug string) error
|
||||
UpdateShow(slug string, show bool) error
|
||||
UpdateCache() error
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package biz
|
||||
|
||||
import "github.com/TheTNB/panel/pkg/types"
|
||||
import "github.com/tnb-labs/panel/pkg/types"
|
||||
|
||||
type BackupType string
|
||||
|
||||
@@ -21,4 +21,6 @@ type BackupRepo interface {
|
||||
ClearExpired(path, prefix string, save int) error
|
||||
CutoffLog(path, target string) error
|
||||
GetPath(typ BackupType) (string, error)
|
||||
FixPanel() error
|
||||
UpdatePanel(version, url, checksum string) error
|
||||
}
|
||||
|
||||
@@ -19,4 +19,6 @@ type Cache struct {
|
||||
type CacheRepo interface {
|
||||
Get(key CacheKey, defaultValue ...string) (string, error)
|
||||
Set(key CacheKey, value string) error
|
||||
UpdateApps() error
|
||||
UpdateRewrites() error
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/acme"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/pkg/acme"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Cert struct {
|
||||
@@ -12,12 +13,13 @@ type Cert struct {
|
||||
AccountID uint `gorm:"not null" json:"account_id"` // 关联的 ACME 账户 ID
|
||||
WebsiteID uint `gorm:"not null" json:"website_id"` // 关联的网站 ID
|
||||
DNSID uint `gorm:"not null" json:"dns_id"` // 关联的 DNS ID
|
||||
Type string `gorm:"not null" json:"type"` // 证书类型 (P256, P384, 2048, 4096)
|
||||
Type string `gorm:"not null" json:"type"` // 证书类型 (P256, P384, 2048, 3072, 4096)
|
||||
Domains []string `gorm:"not null;serializer:json" json:"domains"`
|
||||
AutoRenew bool `gorm:"not null" json:"auto_renew"` // 自动续签
|
||||
CertURL string `gorm:"not null" json:"cert_url"` // 证书 URL (续签时使用)
|
||||
Cert string `gorm:"not null" json:"cert"` // 证书内容
|
||||
Key string `gorm:"not null" json:"key"` // 私钥内容
|
||||
Script string `gorm:"not null" json:"script"` // 部署脚本
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
@@ -27,13 +29,16 @@ type Cert struct {
|
||||
}
|
||||
|
||||
type CertRepo interface {
|
||||
List(page, limit uint) ([]*Cert, int64, error)
|
||||
List(page, limit uint) ([]*types.CertList, int64, error)
|
||||
Get(id uint) (*Cert, error)
|
||||
GetByWebsite(WebsiteID uint) (*Cert, error)
|
||||
Upload(req *request.CertUpload) (*Cert, error)
|
||||
Create(req *request.CertCreate) (*Cert, error)
|
||||
Update(req *request.CertUpdate) error
|
||||
Delete(id uint) error
|
||||
ObtainAuto(id uint) (*acme.Certificate, error)
|
||||
ObtainManual(id uint) (*acme.Certificate, error)
|
||||
ObtainSelfSigned(id uint) error
|
||||
Renew(id uint) (*acme.Certificate, error)
|
||||
ManualDNS(id uint) ([]acme.DNSRecord, error)
|
||||
Deploy(ID, WebsiteID uint) error
|
||||
|
||||
@@ -3,7 +3,7 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type CertAccount struct {
|
||||
@@ -13,7 +13,7 @@ type CertAccount struct {
|
||||
Kid string `gorm:"not null" json:"kid"`
|
||||
HmacEncoded string `gorm:"not null" json:"hmac_encoded"`
|
||||
PrivateKey string `gorm:"not null" json:"private_key"`
|
||||
KeyType string `gorm:"not null" json:"key_type"`
|
||||
KeyType string `gorm:"not null" json:"key_type"` // 密钥类型 (P256, P384, 2048, 3072, 4096)
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
@@ -22,6 +22,7 @@ type CertAccount struct {
|
||||
|
||||
type CertAccountRepo interface {
|
||||
List(page, limit uint) ([]*CertAccount, int64, error)
|
||||
GetDefault(userID uint) (*CertAccount, error)
|
||||
Get(id uint) (*CertAccount, error)
|
||||
Create(req *request.CertAccountCreate) (*CertAccount, error)
|
||||
Update(req *request.CertAccountUpdate) error
|
||||
|
||||
@@ -3,14 +3,14 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/acme"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/pkg/acme"
|
||||
)
|
||||
|
||||
type CertDNS struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"not null" json:"name"` // 备注名称
|
||||
Type string `gorm:"not null" json:"type"` // DNS 提供商 (dnspod, tencent, aliyun, cloudflare)
|
||||
Type string `gorm:"not null" json:"type"` // DNS 提供商 (tencent, aliyun, cloudflare)
|
||||
Data acme.DNSParam `gorm:"not null;serializer:json" json:"dns_param"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerRepo interface {
|
||||
ListAll() ([]types.Container, error)
|
||||
ListByNames(names []string) ([]types.Container, error)
|
||||
ListByName(name string) ([]types.Container, error)
|
||||
Create(req *request.ContainerCreate) (string, error)
|
||||
Remove(id string) error
|
||||
Start(id string) error
|
||||
@@ -17,12 +15,8 @@ type ContainerRepo interface {
|
||||
Restart(id string) error
|
||||
Pause(id string) error
|
||||
Unpause(id string) error
|
||||
Inspect(id string) (types.ContainerJSON, error)
|
||||
Kill(id string) error
|
||||
Rename(id string, newName string) error
|
||||
Stats(id string) (container.StatsResponseReader, error)
|
||||
Exist(name string) (bool, error)
|
||||
Update(id string, config container.UpdateConfig) error
|
||||
Logs(id string) (string, error)
|
||||
Prune() error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package biz
|
||||
|
||||
import "github.com/tnb-labs/panel/pkg/types"
|
||||
|
||||
type ContainerComposeRepo interface {
|
||||
List() ([]types.ContainerCompose, error)
|
||||
Get(name string) (string, []types.KV, error)
|
||||
Create(name, compose string, envs []types.KV) error
|
||||
Update(name, compose string, envs []types.KV) error
|
||||
Up(name string, force bool) error
|
||||
Down(name string) error
|
||||
Remove(name string) error
|
||||
}
|
||||
@@ -1,17 +1,13 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerImageRepo interface {
|
||||
List() ([]image.Summary, error)
|
||||
Exist(id string) (bool, error)
|
||||
List() ([]types.ContainerImage, error)
|
||||
Pull(req *request.ContainerImagePull) error
|
||||
Remove(id string) error
|
||||
Prune() error
|
||||
Inspect(id string) (types.ImageInspect, error)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types/network"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerNetworkRepo interface {
|
||||
List() ([]network.Inspect, error)
|
||||
List() ([]types.ContainerNetwork, error)
|
||||
Create(req *request.ContainerNetworkCreate) (string, error)
|
||||
Remove(id string) error
|
||||
Exist(name string) (bool, error)
|
||||
Inspect(id string) (network.Inspect, error)
|
||||
Connect(networkID string, containerID string) error
|
||||
Disconnect(networkID string, containerID string) error
|
||||
Prune() error
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerVolumeRepo interface {
|
||||
List() ([]*volume.Volume, error)
|
||||
Create(req *request.ContainerVolumeCreate) (volume.Volume, error)
|
||||
Exist(name string) (bool, error)
|
||||
Inspect(id string) (volume.Volume, error)
|
||||
List() ([]types.ContainerVolume, error)
|
||||
Create(req *request.ContainerVolumeCreate) (string, error)
|
||||
Remove(id string) error
|
||||
Prune() error
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type Cron struct {
|
||||
@@ -26,5 +26,4 @@ type CronRepo interface {
|
||||
Update(req *request.CronUpdate) error
|
||||
Delete(id uint) error
|
||||
Status(id uint, status bool) error
|
||||
Log(id uint) (string, error)
|
||||
}
|
||||
|
||||
+26
-11
@@ -1,16 +1,31 @@
|
||||
package biz
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type DatabaseType string
|
||||
|
||||
const (
|
||||
DatabaseTypeMysql DatabaseType = "mysql"
|
||||
DatabaseTypePostgresql DatabaseType = "postgresql"
|
||||
DatabaseTypeMongoDB DatabaseType = "mongodb"
|
||||
DatabaseSQLite DatabaseType = "sqlite"
|
||||
DatabaseTypeRedis DatabaseType = "redis"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"not null;unique" json:"name"`
|
||||
Type string `gorm:"not null" json:"type"`
|
||||
Host string `gorm:"not null" json:"host"`
|
||||
Port int `gorm:"not null" json:"port"`
|
||||
Username string `gorm:"not null" json:"username"`
|
||||
Password string `gorm:"not null" json:"password"`
|
||||
Remark string `gorm:"not null" json:"remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Type DatabaseType `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Server string `json:"server"`
|
||||
ServerID uint `json:"server_id"`
|
||||
Encoding string `json:"encoding"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
type DatabaseRepo interface {
|
||||
List(page, limit uint) ([]*Database, int64, error)
|
||||
Create(req *request.DatabaseCreate) error
|
||||
Delete(serverID uint, name string) error
|
||||
Comment(req *request.DatabaseComment) error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-rat/utils/crypt"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type DatabaseServerStatus string
|
||||
|
||||
const (
|
||||
DatabaseServerStatusValid DatabaseServerStatus = "valid"
|
||||
DatabaseServerStatusInvalid DatabaseServerStatus = "invalid"
|
||||
)
|
||||
|
||||
type DatabaseServer struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"not null;unique" json:"name"`
|
||||
Type DatabaseType `gorm:"not null" json:"type"`
|
||||
Host string `gorm:"not null" json:"host"`
|
||||
Port uint `gorm:"not null" json:"port"`
|
||||
Username string `gorm:"not null" json:"username"`
|
||||
Password string `gorm:"not null" json:"password"`
|
||||
Status DatabaseServerStatus `gorm:"-:all" json:"status"`
|
||||
Remark string `gorm:"not null" json:"remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (r *DatabaseServer) BeforeSave(tx *gorm.DB) error {
|
||||
crypter, err := crypt.NewXChacha20Poly1305([]byte(app.Key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Password, err = crypter.Encrypt([]byte(r.Password))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *DatabaseServer) AfterFind(tx *gorm.DB) error {
|
||||
crypter, err := crypt.NewXChacha20Poly1305([]byte(app.Key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
password, err := crypter.Decrypt(r.Password)
|
||||
if err == nil {
|
||||
r.Password = string(password)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DatabaseServerRepo interface {
|
||||
Count() (int64, error)
|
||||
List(page, limit uint) ([]*DatabaseServer, int64, error)
|
||||
Get(id uint) (*DatabaseServer, error)
|
||||
GetByName(name string) (*DatabaseServer, error)
|
||||
Create(req *request.DatabaseServerCreate) error
|
||||
Update(req *request.DatabaseServerUpdate) error
|
||||
UpdateRemark(req *request.DatabaseServerUpdateRemark) error
|
||||
Delete(id uint) error
|
||||
ClearUsers(id uint) error
|
||||
Sync(id uint) error
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-rat/utils/crypt"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnb-labs/panel/internal/app"
|
||||
"github.com/tnb-labs/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type DatabaseUserStatus string
|
||||
|
||||
const (
|
||||
DatabaseUserStatusValid DatabaseUserStatus = "valid"
|
||||
DatabaseUserStatusInvalid DatabaseUserStatus = "invalid"
|
||||
)
|
||||
|
||||
type DatabaseUser struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
ServerID uint `gorm:"not null" json:"server_id"`
|
||||
Username string `gorm:"not null" json:"username"`
|
||||
Password string `gorm:"not null" json:"password"`
|
||||
Host string `gorm:"not null" json:"host"` // 仅 mysql
|
||||
Status DatabaseUserStatus `gorm:"-:all" json:"status"` // 仅显示
|
||||
Privileges []string `gorm:"-:all" json:"privileges"` // 仅显示
|
||||
Remark string `gorm:"not null" json:"remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
Server *DatabaseServer `gorm:"foreignKey:ServerID;references:ID" json:"server"`
|
||||
}
|
||||
|
||||
func (r *DatabaseUser) BeforeSave(tx *gorm.DB) error {
|
||||
crypter, err := crypt.NewXChacha20Poly1305([]byte(app.Key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Password, err = crypter.Encrypt([]byte(r.Password))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *DatabaseUser) AfterFind(tx *gorm.DB) error {
|
||||
crypter, err := crypt.NewXChacha20Poly1305([]byte(app.Key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
password, err := crypter.Decrypt(r.Password)
|
||||
if err == nil {
|
||||
r.Password = string(password)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DatabaseUserRepo interface {
|
||||
Count() (int64, error)
|
||||
List(page, limit uint) ([]*DatabaseUser, int64, error)
|
||||
Get(id uint) (*DatabaseUser, error)
|
||||
Create(req *request.DatabaseUserCreate) error
|
||||
Update(req *request.DatabaseUserUpdate) error
|
||||
UpdateRemark(req *request.DatabaseUserUpdateRemark) error
|
||||
Delete(id uint) error
|
||||
DeleteByNames(serverID uint, names []string) error
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user