mirror of
https://github.com/tnb-labs/panel.git
synced 2026-08-31 01:12:17 +08:00
Compare commits
924 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f6c5805ef6 | |||
| c2001a477c | |||
| 8e76c6c3b2 | |||
| 4ff67bca0d | |||
| 8f3eb90c2b | |||
| ccf696dc55 | |||
| 6f23f3f3a9 | |||
| 5576426f33 | |||
| d81ed39459 | |||
| f9238b6b59 | |||
| 34df623e0c | |||
| c87f52f696 | |||
| bc573dca1a | |||
| ecd23f4733 | |||
| 19ef9f4a14 | |||
| aacb01e731 | |||
| ec3c508b88 | |||
| e0cd0e390c | |||
| b690779caa | |||
| 3a1997d1f8 | |||
| dd5c201d92 | |||
| e23847148e | |||
| 19ca32f2b4 | |||
| 2c018c9faa | |||
| d527a5170b | |||
| 2e6c8324f1 | |||
| 2892b4f35b | |||
| 84c4bb71be | |||
| 0e74344280 | |||
| 8d5d0ac90d | |||
| 96454df9a5 | |||
| 4674cdd34c | |||
| 1beb2b26b7 | |||
| 726e937779 | |||
| c82c68da9d | |||
| e1274c28f7 | |||
| afa40be261 | |||
| ccd73ebd0d | |||
| 1917f8e8d4 | |||
| 072d847190 | |||
| 29d32a099d | |||
| 0854c3b053 | |||
| 9967b469f1 | |||
| a0a7656a30 | |||
| 32a68b9a0b | |||
| 6926753f93 | |||
| b2158eb8da | |||
| f29b5357e7 | |||
| 822d550760 | |||
| aa420de64e | |||
| e398654bb8 | |||
| 2cd9596a58 | |||
| 138f681260 | |||
| 61bb7c86ec | |||
| 14d4b53369 | |||
| 3d585df346 | |||
| fa9d1feffd | |||
| 81f75f0852 | |||
| f63f8bccaf | |||
| e35a09c1ef | |||
| 4b98079778 | |||
| 4ea4f0c455 | |||
| 30e54f59eb | |||
| abfa10323c | |||
| 7b66868b80 | |||
| ebe9cf53c8 | |||
| 4ec683ba69 | |||
| 6cc9cdc15c | |||
| 8907adf967 | |||
| 1fd42eb55f | |||
| 55979ed385 | |||
| eaa79126db | |||
| 6d4b0628d0 | |||
| e1af869ee4 | |||
| 3a73192fac | |||
| fdc1dace8f | |||
| 21167c392f | |||
| 5584cc18cb | |||
| 596c8c8385 | |||
| c4704614de | |||
| c57d12d53a | |||
| 28d0bb4707 | |||
| 29b8dda4ac | |||
| b74adcf174 | |||
| 268ce78cda | |||
| 831b73f91a | |||
| 31d1bb0ebe | |||
| 307099db4e | |||
| 8daeedf2fa | |||
| 23570d143b | |||
| 5ee8a36d75 | |||
| 507e90168d | |||
| 62780777b7 | |||
| 796b48ca8b | |||
| 7d80e874f1 | |||
| 93e0ca6a57 | |||
| 73f5a05811 | |||
| db6f362cce | |||
| 4d7018876c | |||
| bba7e89e53 | |||
| c3eeffc780 | |||
| c547b00a5f | |||
| e5f837799f | |||
| 25194d8c50 | |||
| e1f6b291bb | |||
| df9425b202 | |||
| c50a33f323 | |||
| b264381b84 | |||
| d5ee789a61 | |||
| 5fa4219a31 | |||
| 430640e93c | |||
| 135c5c844f | |||
| 05721dbb47 | |||
| 0235679821 | |||
| 11e206b41a | |||
| 4985eb2e1f | |||
| 2181653ff7 | |||
| 26f7edaa24 | |||
| 75078c88d9 | |||
| 329aecb444 | |||
| 1559c9f8d3 | |||
| 26add61117 | |||
| 763c625b83 | |||
| 1375de2caa | |||
| c4b84632b4 | |||
| c20bfdc3ea | |||
| cc55aef306 | |||
| ed5c74c753 | |||
| 5a9ccddabd | |||
| e6b82ab9ae | |||
| 8868a125e2 | |||
| 04474acf5f | |||
| 6a17173b3b | |||
| 711fb33b48 | |||
| 3ed8fb4f95 | |||
| 65944a72ec | |||
| 24d18df86b | |||
| b330763746 | |||
| 2e914ea41a | |||
| 85a5d22861 | |||
| 9f31a2061d | |||
| d1b54c2690 | |||
| 029f86d9d5 | |||
| 9de30f30b8 | |||
| 8582bfc36b | |||
| 4a2af3434c | |||
| 7f697ca734 | |||
| 7f08ee92ff | |||
| 2f09043915 | |||
| 50aab262a8 | |||
| 99b033f070 | |||
| 6277a17d53 | |||
| 8bb6bd1882 | |||
| 3161c61001 | |||
| e68a882e75 | |||
| 24066f05fc | |||
| 28325f4af6 | |||
| 0c57e9d628 | |||
| 8fb7ddcf7a | |||
| 76185a97f3 | |||
| 567ef561c7 | |||
| b70b9df0b7 | |||
| 0e4fbea166 | |||
| 6cf4e0c0cc | |||
| 68da337cc0 | |||
| c492639897 | |||
| 5e10b2267e | |||
| 68bbbc76e2 | |||
| 7cd1c764d2 | |||
| a053a4212a | |||
| 2d0974a5d5 | |||
| d90396c8ea | |||
| 877d9c57c8 | |||
| 275760c18a | |||
| 9dd6063611 | |||
| 0acbb05d3f | |||
| d860e9361a | |||
| 97d76f62ae | |||
| 910a2d92cc | |||
| 76ef54fe0e | |||
| bf41b98339 | |||
| f3cec5e476 | |||
| bb5b66958c | |||
| 04fb0a1de8 | |||
| f7752387da | |||
| a7420eef34 | |||
| 28847bcc1f | |||
| cb24946aea | |||
| 371424114a | |||
| 60086b9839 | |||
| 6d55088a0d | |||
| c4234a11f2 | |||
| 7644e1a5f6 | |||
| f8a54b2860 | |||
| 8c1813b9e5 | |||
| 7a9a803451 | |||
| 681d0b5324 | |||
| a33490b316 | |||
| fb95ff2861 | |||
| 326c2585f3 | |||
| 61cc26cdb0 | |||
| 2ecd3a020b | |||
| c5b23feeac | |||
| 4f39f62208 | |||
| 146401e9f9 | |||
| d544ca6a90 | |||
| 43248c1d88 | |||
| 2b5bb04bbd | |||
| 30a5427778 | |||
| c547200c05 | |||
| 7f223812dc | |||
| 0e86c77d6d | |||
| fb24407cd1 | |||
| ec7791b73d | |||
| 7987cec9b9 | |||
| 7be3e799be | |||
| 5667bb236f | |||
| 5a391a459e | |||
| 60b0761de2 | |||
| 952546f845 | |||
| b7c055c43f | |||
| ba4df0c76f | |||
| dfae3f1d8d | |||
| 310d83a2aa | |||
| 77eb802076 | |||
| 913a6776dc | |||
| f245ff06c5 | |||
| 79901878e6 | |||
| c597e6b99c | |||
| 3c98e6ff37 | |||
| 447b0332e3 | |||
| 1459b287fa | |||
| 252b67ce36 | |||
| 8119eca0c6 | |||
| f0b96417bf | |||
| b8018284c6 | |||
| 737986f218 | |||
| 200815bf10 | |||
| 8cff4e3575 | |||
| 64c39bd462 | |||
| 4a5e629ab1 | |||
| 368519f3fa | |||
| 8fa7f4a22b | |||
| a8e4360c29 | |||
| 719a9a580d | |||
| fb32151b76 | |||
| c50d892e52 | |||
| b31ccace85 | |||
| e57da7e41a | |||
| 38141c3f4e | |||
| 69d39e6c67 | |||
| 4d8a330644 | |||
| 26c417f18d | |||
| f6b7fcb4af | |||
| 212e989782 | |||
| d0ece55dd1 | |||
| 03c0b191fe | |||
| adacee1851 | |||
| 2725c4902d | |||
| 77d7539880 | |||
| 560b80038e | |||
| c9d4c79bf8 | |||
| 905f13f0d3 | |||
| e7128c65d9 | |||
| c15e3cc3ae | |||
| d9f1145d93 | |||
| 0aafe8baa3 | |||
| 7b0e9db8dc | |||
| 07881311b3 | |||
| 7d9b84c746 | |||
| 548165ab4a | |||
| a997d548f7 | |||
| aab5cf7e94 | |||
| 382bf4daaa | |||
| 3e048a8683 | |||
| 7fa7bb4bd0 | |||
| d41ddb0ad0 | |||
| cdbadcbb65 | |||
| b14d63c54c | |||
| 3d47855cc1 | |||
| 819db43aaa | |||
| de859afbe7 | |||
| 066c840efe | |||
| bc4fcfe6ed | |||
| d43090b84f | |||
| 919c37d1f9 | |||
| 319b689d79 | |||
| 9a6badd46c | |||
| ee94b01c2d | |||
| 7b471967a2 | |||
| 6e039a7fad | |||
| 2f56606789 | |||
| a3210c0a92 | |||
| 9876821b9a | |||
| ee7f846151 | |||
| 6b1478594a | |||
| 6448e7c5c6 | |||
| 68c088ca03 | |||
| c5ec454b26 | |||
| 12f9820ec5 | |||
| d5d941c62b | |||
| e5f26d2f23 | |||
| 433f3f99f6 | |||
| b866f01985 | |||
| 07ba6624bf | |||
| 855c8cfb51 | |||
| 70175d309e | |||
| 04d286ca03 | |||
| 1a821b361f | |||
| ec101ba9f7 | |||
| 830fa5aa86 | |||
| aed6bb7856 | |||
| 7df9b92c58 | |||
| 9faa2a74df | |||
| 94497f226f | |||
| cff90ef444 | |||
| f8ae9136c0 | |||
| 7716866849 | |||
| c690353f3e | |||
| 373827f8e5 | |||
| 587436781d | |||
| 069353d617 | |||
| 7f87030cc8 | |||
| 9064e41775 | |||
| 04c8eb5cad | |||
| 867c14349e | |||
| a59fe6fe16 | |||
| 71f518c6f9 | |||
| a7192efda0 | |||
| f14b606fda | |||
| 82e310542f | |||
| 16f31120f5 | |||
| 05ffab5d54 | |||
| f39cceda18 | |||
| ec8bf8fdad | |||
| c0f82356aa | |||
| 14c46eccca | |||
| 354e8a1270 | |||
| 924e69070a | |||
| 137624815c | |||
| 1adba2da49 | |||
| 00c02e4b16 | |||
| 813dcaedaf | |||
| 0ef8bddf98 | |||
| 6276009025 | |||
| 2dc7096a2c | |||
| b84655512c | |||
| 271a2cad23 | |||
| a7dd9d411b | |||
| 103529a9b4 | |||
| ccef25314d | |||
| 9f24e1e8a6 | |||
| 7399e1632e | |||
| 90715bcfaf | |||
| a64176e363 | |||
| 910104d400 | |||
| 9e87be88c7 | |||
| fae5d136d4 | |||
| 13f090e2b7 | |||
| 5896ba3c87 | |||
| 65030866e5 | |||
| 5fd00acd48 | |||
| 462d6c0789 | |||
| c6b3b9e0c4 | |||
| 63b6349a74 | |||
| 781c5cf11e | |||
| 8697c893c4 | |||
| 896e242827 | |||
| b891d21c85 | |||
| 399b6f3537 | |||
| cf8237b9c7 | |||
| daccf651b6 | |||
| 4386334c31 | |||
| 0a445a4247 | |||
| 76cb8c46ba | |||
| d27a91570c | |||
| 554183caab | |||
| 275bba850a | |||
| 86c5239569 | |||
| 681f1e0805 | |||
| b767044199 | |||
| 67265ef969 | |||
| 8410b48ac0 | |||
| e6567e5e95 | |||
| 715b42dfbb | |||
| 69ddca44bf | |||
| 71d9151fa0 | |||
| 3fd768a99c | |||
| c02eab6bfc | |||
| 148e5946cf | |||
| 3590191ef9 | |||
| b91c1a02f5 | |||
| d50ef7d62c | |||
| 6ee4d67c2e | |||
| 3e325b13b4 | |||
| d6fb57f9ea | |||
| bffdf2821c | |||
| c82d7bd48a | |||
| e4eb97e9dd | |||
| cbb168b3d5 | |||
| 60e817d9ea | |||
| e514d45486 | |||
| 995d5524ea | |||
| b1d5276813 | |||
| 97ee651948 | |||
| 5ddc25acc7 | |||
| 96ae14eefb | |||
| 820728861c | |||
| b476e9aec3 | |||
| 5252854963 | |||
| fe6e9efd09 | |||
| c0be351b95 | |||
| 573fc88c7a | |||
| 9032450be8 | |||
| bf0cdbe493 | |||
| 446d2410dc | |||
| 17febfa9e6 | |||
| 86eaef0ae3 | |||
| 874ecbc886 | |||
| 0c4b4b0a31 | |||
| 5d8e773827 | |||
| c9e1adc1e9 | |||
| 27ce52ea13 | |||
| dea9702c36 | |||
| 794fa29581 | |||
| 30686bf5a1 | |||
| ae9f3c4d15 | |||
| 13fc5d2a5b | |||
| 7bc2173edb | |||
| 5317e19d6e | |||
| 0910b7c068 | |||
| c800f6642b | |||
| 1b80c85ebf | |||
| 714a33313e | |||
| a70b18b6f4 | |||
| 1a3d2cdd78 | |||
| 45081b5535 | |||
| 600287c246 | |||
| a2607b4846 | |||
| 13bb84f4f4 | |||
| e702f7813a | |||
| ec8156b851 | |||
| 9da24a3238 | |||
| e390102a7c | |||
| 2271084c02 | |||
| dbb4a3514b | |||
| 3eeefcdc9c | |||
| c0be6addbc | |||
| c344ade9fc | |||
| 6c2a3a26ab | |||
| 073f42a3bc | |||
| 0e3007827e | |||
| 8aa23cdd48 | |||
| 9765f7157d | |||
| ee3aabe3c8 | |||
| d39b3f66d6 | |||
| cc26427417 | |||
| 592af262da | |||
| cf64103e36 | |||
| 044b3664ed | |||
| 99eb46c2a9 | |||
| 2cae2fc807 | |||
| bfc0ff5c28 | |||
| 504210cee1 | |||
| 1d6d388d37 | |||
| 727ff7a52d | |||
| 8638dcb9c8 | |||
| d641c78000 | |||
| 49131d1d00 | |||
| ccc43cd175 | |||
| 0131f464cd | |||
| 752bc0a64e | |||
| fac981e1ee | |||
| c6ce1a403d | |||
| 79f7b4a510 | |||
| 815418627f | |||
| d82e59b4eb | |||
| a6a42f0f11 | |||
| 4d6b62ac18 | |||
| 0547157c69 | |||
| 493da1552d | |||
| 0402427b71 | |||
| 623dcbce1c | |||
| 98d9af89ec | |||
| b4546ec0ba | |||
| f140eb3f5f | |||
| 432b5466e8 | |||
| 402562ba93 | |||
| 77bcf6e086 | |||
| d9fee73e87 | |||
| e512187b60 | |||
| 31a0617383 | |||
| 98d80cd359 | |||
| b0cae832c5 | |||
| 00b9a859f8 | |||
| 8b5a7ece43 | |||
| 391b2a9c68 | |||
| cf6deb665c | |||
| 096890bd2f | |||
| 4dba4e5216 | |||
| 2b63c50434 | |||
| 0a2d9262c9 | |||
| 4693cc8fe3 | |||
| b6bad9ea64 | |||
| a3b0bf10e9 | |||
| a627816694 | |||
| a4ea9298a4 | |||
| 233bb6e23b | |||
| bdd20107c7 | |||
| e296008112 | |||
| 3dd01e6c1f | |||
| 5b633e6528 | |||
| 2da260bba0 | |||
| acd8403e4b | |||
| 90e66175ef | |||
| 4f1a7cc1bf | |||
| 053d7d3ca5 | |||
| dae0e8274a | |||
| 2b3d1cd220 | |||
| 5f0a4ca89e | |||
| 67d350ebe2 | |||
| 66c30f3e33 | |||
| 82d763fdee | |||
| 7159c4cccf | |||
| 60357222df | |||
| f6c43cf5a0 | |||
| f1341c5364 | |||
| b8cb8d70d3 | |||
| fa7fca1f11 | |||
| 59cfa096b4 | |||
| 706c04a21f | |||
| 255284caad | |||
| 85b34fc017 | |||
| d42deb54eb | |||
| 002104b0eb | |||
| 4dc943438e | |||
| 81a320cd93 | |||
| df32b3b5de | |||
| db0679cd92 | |||
| 20bbbc6202 | |||
| ce652f45e6 | |||
| c0f7183bc8 | |||
| 60d8ccd1a0 | |||
| e9561af93a | |||
| 7e0ad2c799 | |||
| fe9f1c6e95 | |||
| 193ec9a6ae | |||
| 72c3bed203 | |||
| 2ae259e53a | |||
| 3cd25e19de | |||
| 9bdffedb7e | |||
| c298a7182e | |||
| d613a06ad2 | |||
| cc2cdaa76b | |||
| 2c38a51515 | |||
| d2e89353c5 | |||
| 998bc02f8c | |||
| 42e3e20eb8 | |||
| 225795db74 | |||
| bd35c42eb0 | |||
| d2587e77d4 | |||
| b0018ee71b | |||
| 985f4a6e6b | |||
| cf910c111f | |||
| d7e0cb02cd | |||
| c9c6c8b23a | |||
| 3d0cb594ae | |||
| b614c9097b | |||
| 78b94fe89c | |||
| 90140f4229 | |||
| d36c3b0dae | |||
| 88b64d6fc8 | |||
| ce4725ed09 | |||
| a2efa9a70b | |||
| 5126e2030a | |||
| b7b5744f17 | |||
| 0f08c10f02 | |||
| 6b4154f4a6 | |||
| 2d45e84b66 | |||
| 3f90402164 | |||
| 53fdcfb715 | |||
| 2ff01579d6 | |||
| 774d13980c | |||
| ae76a0171d | |||
| 137665b77d | |||
| 3b7bf35bf3 | |||
| 0b0c4da3eb | |||
| 7dfcfb98d5 | |||
| 00b9be6bc9 | |||
| 771a9d1f28 | |||
| 3021f466f0 | |||
| 486a9fb58b | |||
| dbc3050f2b | |||
| dc584a9382 | |||
| e1f6afe08f | |||
| a7c95d929f | |||
| 9ff5596aca | |||
| 4369eedfe5 | |||
| 5af46a5039 | |||
| 4e854515c8 | |||
| 552487b9d3 | |||
| 8cadfe3e53 | |||
| 15c0f28252 | |||
| 8e4b73c695 | |||
| cb8f2a0ee1 | |||
| e6b3858937 | |||
| 1bdcf0473e | |||
| edb5e858e4 | |||
| cfedad8a1b | |||
| 08a5c66e62 | |||
| 1d59fad7ad | |||
| e0145ee9e6 | |||
| 4703d8c3ca | |||
| 8b95095e55 | |||
| 709c345201 | |||
| 288968006a | |||
| 7f619f786d | |||
| c9c3de1348 | |||
| 841b1d7d1b | |||
| 6f9d5370e0 | |||
| c8f12b3a89 | |||
| daa3407505 | |||
| f5d2d2204f | |||
| 04ef3ce442 | |||
| aff68bd1c1 | |||
| fca7065dca | |||
| 29330ec457 | |||
| cc15852944 | |||
| ecc44b2e92 | |||
| 2b1e21ba4b | |||
| 7a22469fb1 | |||
| 31c063ba68 | |||
| dad868b64c | |||
| 16de31c838 | |||
| 3c1062a006 | |||
| 7017aff841 | |||
| 334c4bc4c1 | |||
| e07a69ab67 | |||
| 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 |
+1
-1
@@ -1 +1 @@
|
||||
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
|
||||
@@ -42,7 +41,7 @@ body:
|
||||
description: |
|
||||
请提供面板的版本号。
|
||||
Please provide the version number of the panel.
|
||||
placeholder: "2.3.0"
|
||||
placeholder: "2.4.0"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
name: ✨ 功能请求 (Feature Request)
|
||||
description: 为这个项目提出一个想法 (Suggest an idea for this project)
|
||||
title: "✨ [Feature] "
|
||||
labels: [ "✏️ Feature", "enhancement" ]
|
||||
type: ✨ Feature
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
+16
-15
@@ -1,15 +1,15 @@
|
||||
name: Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
frontend:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
@@ -27,6 +27,7 @@ jobs:
|
||||
run: |
|
||||
cp .env.production .env
|
||||
cp settings/proxy-config.example.ts settings/proxy-config.ts
|
||||
pnpm run gettext:compile
|
||||
pnpm build
|
||||
- name: Upload frontend
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -35,14 +36,14 @@ jobs:
|
||||
path: web/dist/
|
||||
backend:
|
||||
needs: frontend
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goarch: [ amd64, arm64 ]
|
||||
fail-fast: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
@@ -53,10 +54,10 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: go mod tidy
|
||||
- name: Download frontend
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: frontend
|
||||
path: internal/embed/frontend
|
||||
path: pkg/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
|
||||
@@ -73,13 +74,13 @@ jobs:
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
run: |
|
||||
LDFLAGS="-s -w --extldflags '-static'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/TheTNB/panel/internal/app.Version=${VERSION}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/TheTNB/panel/internal/app.BuildTime=${BUILD_TIME}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/TheTNB/panel/internal/app.CommitHash=${COMMIT_HASH}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/TheTNB/panel/internal/app.GoVersion=${GO_VERSION}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/TheTNB/panel/internal/app.BuildID=${BUILD_ID}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/TheTNB/panel/internal/app.BuildUser=${BUILD_USER}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/TheTNB/panel/internal/app.BuildHost=${BUILD_HOST}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.Version=${VERSION}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildTime=${BUILD_TIME}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.CommitHash=${COMMIT_HASH}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.GoVersion=${GO_VERSION}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildID=${BUILD_ID}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildUser=${BUILD_USER}'"
|
||||
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/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 }}
|
||||
|
||||
@@ -7,10 +7,10 @@ permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
@@ -35,6 +35,7 @@ jobs:
|
||||
run: |
|
||||
cp .env.production .env
|
||||
cp settings/proxy-config.example.ts settings/proxy-config.ts
|
||||
pnpm run gettext:compile
|
||||
pnpm build
|
||||
- name: Set environment variables
|
||||
run: |
|
||||
|
||||
@@ -11,6 +11,7 @@ permissions:
|
||||
jobs:
|
||||
issue-lock:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Set GH_REPO
|
||||
run: echo "GH_REPO=${{ github.repository }}" >> $GITHUB_ENV
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
name: Issue Auto Reply
|
||||
on:
|
||||
issues:
|
||||
types: [ labeled ]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
issue-reply:
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: ☢️ Bug
|
||||
if: github.event.label.name == '☢️ Bug'
|
||||
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 }} 👋
|
||||
|
||||
感谢提交 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.
|
||||
|
||||

|
||||
@@ -0,0 +1,41 @@
|
||||
name: L10n
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
l10n:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v5
|
||||
- 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: 22
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: web/pnpm-lock.yaml
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: true
|
||||
go-version: 'stable'
|
||||
- name: Install xgotext
|
||||
run: |
|
||||
go install github.com/leonelquinteros/gotext/cli/xgotext@latest
|
||||
- name: Generate pot files
|
||||
run: |
|
||||
~/go/bin/xgotext -default backend -pkg-tree ./cmd/web -out ./pkg/embed/locales
|
||||
cd web && pnpm run gettext:extract
|
||||
- uses: stefanzweifel/git-auto-commit-action@v6
|
||||
name: Commit changes
|
||||
with:
|
||||
commit_message: "chore(l10n): update pot files"
|
||||
@@ -9,26 +9,26 @@ permissions:
|
||||
jobs:
|
||||
golangci:
|
||||
name: golanci-lint
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
cache: true
|
||||
go-version: 'stable'
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
with:
|
||||
skip-cache: true
|
||||
version: latest
|
||||
args: --timeout=30m ./...
|
||||
govulncheck:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
@@ -39,10 +39,10 @@ jobs:
|
||||
- name: Run Govulncheck
|
||||
run: govulncheck ./...
|
||||
frontend:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
|
||||
@@ -3,20 +3,27 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
mockery:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- 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
|
||||
git pull
|
||||
- uses: stefanzweifel/git-auto-commit-action@v6
|
||||
name: Commit changes
|
||||
with:
|
||||
commit_message: "chore: update mocks"
|
||||
|
||||
@@ -4,12 +4,14 @@ on:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
unit:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
|
||||
@@ -63,3 +63,6 @@ out/
|
||||
*.sublime*
|
||||
__debug_bin
|
||||
.project
|
||||
|
||||
# 傻逼 node_modules
|
||||
node_modules/
|
||||
|
||||
+16
-16
@@ -14,13 +14,13 @@ builds:
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w --extldflags "-static"
|
||||
- -X 'github.com/TheTNB/panel/internal/app.Version={{ .Version }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.CommitHash={{ .ShortCommit }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.BuildUser={{ .Env.USER }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.Version={{ .Version }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.CommitHash={{ .ShortCommit }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.BuildUser={{ .Env.USER }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
|
||||
- id: cli
|
||||
main: ./cmd/cli
|
||||
binary: cli
|
||||
@@ -33,13 +33,13 @@ builds:
|
||||
- arm64
|
||||
ldflags:
|
||||
- -s -w --extldflags "-static"
|
||||
- -X 'github.com/TheTNB/panel/internal/app.Version={{ .Version }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.CommitHash={{ .ShortCommit }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.BuildUser={{ .Env.USER }}'
|
||||
- -X 'github.com/TheTNB/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.Version={{ .Version }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.CommitHash={{ .ShortCommit }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.BuildUser={{ .Env.USER }}'
|
||||
- -X 'github.com/tnborg/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
|
||||
|
||||
upx:
|
||||
- enabled: true
|
||||
@@ -57,10 +57,10 @@ upx:
|
||||
|
||||
archives:
|
||||
- id: panel
|
||||
builds:
|
||||
ids:
|
||||
- web
|
||||
- cli
|
||||
format: zip
|
||||
formats: ["zip"]
|
||||
wrap_in_directory: false
|
||||
strip_binary_directory: true
|
||||
files:
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@ outpkg: "{{.PackageName}}"
|
||||
filename: "{{.InterfaceName}}.go"
|
||||
all: True
|
||||
packages:
|
||||
github.com/TheTNB/panel/internal/biz:
|
||||
github.com/tnborg/panel/internal/biz:
|
||||
config:
|
||||
recursive: True
|
||||
|
||||
@@ -659,3 +659,15 @@ specific requirements.
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
免责声明
|
||||
|
||||
本软件是按照现有技术和条件所能达到的现状提供的,用户出于自愿而使用本软件,树新峰(耗子科技)不作任何形式的保证,包括但不限于:
|
||||
|
||||
1. 树新峰不对于因使用或无法使用本软件而造成的任何直接、间接、偶然、附带、特殊及后续的「损害」承担责任,即使已被告知此类「损害」的可能性;
|
||||
2. 树新峰无组织本软件的交流社区的义务与责任,不承担因技术交流导致某一方故障而产生的「损害」;
|
||||
3. 树新峰不对本软件的无故障、适用性、可用性、准确性、质量满意度等做任何形式的保证;
|
||||
4. 树新峰不保证本软件可以满足您的要求,也不保证其内容、服务或功能无任何错误或不会中断,不保证任何缺陷将得到更正;
|
||||
5. 树新峰对本软件提供的任何信息或建议均不构成任何担保;
|
||||
6. 用户因使用本软件违反国家法律法规的,树新峰不承担任何责任;
|
||||
7. 本条款所指的「损害」包括但不限于经济损失、利润损失、收入损失、业务中断、数据丢失等。
|
||||
|
||||
@@ -4,15 +4,18 @@
|
||||
|
||||
<h1 align="center" style="font-size: 40px">耗子面板</h1>
|
||||
|
||||
<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>
|
||||
<div align="center">
|
||||
|
||||
企业级全能服务器运维管理面板。简单轻量,高效运维。
|
||||
[](https://go.dev/)
|
||||
[](https://github.com/tnborg/panel/releases)
|
||||
[](https://github.com/tnborg/panel/actions)
|
||||
[](https://goreportcard.com/report/github.com/tnborg/panel)
|
||||
[](https://github.com/tnborg/panel)
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
</div>
|
||||
|
||||
新一代全能服务器运维管理面板。简单轻量,高效运维。
|
||||
|
||||
官网:[panel.haozi.net](https://panel.haozi.net) | QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | 微信群:[复制此链接](https://work.weixin.qq.com/gm/d8ebf618553398d454e3378695c858b6)
|
||||
|
||||
@@ -23,55 +26,56 @@
|
||||
3. **追随时代:** 整体设计走在时代前沿,对新系统兼容性好,在同类产品中处于领先地位
|
||||
4. **高效运维:** 功能完善,自定义能力强,既可快速部署小型网站,也可基于定制化需求部署复杂应用
|
||||
5. **离线运行:** 支持离线模式,甚至可以在部署完成后停止面板进程,不会对已有服务造成任何影响
|
||||
6. **安全稳定:** 面板采用业界多种技术保障本体安全性,已在我们的多个生存环境中长期稳定运行
|
||||
6. **安全稳定:** 面板采用业界多种技术保障本体安全性,已在我们的多个生产环境中长期稳定运行
|
||||
7. **全面开源:** 少有的全开源面板,您可以在遵守开源协议的前提下对面板自由修改、二次开发
|
||||
8. **全面开源:** 承诺面板本体未来不会引入任何收费/授权功能,永久免费使用
|
||||
8. **永久免费:** 承诺面板本体未来不会引入任何收费/授权功能,永久免费使用
|
||||
|
||||
## 快速安装
|
||||
|
||||
支持 `amd64` | `arm64` 架构下的干净的主流系统,具体支持的系统请参考[安装文档](https://ratpanel.github.io/zh_CN/quickstart/install)。
|
||||
|
||||
```shell
|
||||
curl -sSLOm 10 https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
|
||||
```
|
||||
|
||||
## UI 截图
|
||||
|
||||

|
||||
|
||||
## 快速安装
|
||||
|
||||
[https://ratpanel.github.io/quickstart/install.html](https://ratpanel.github.io/quickstart/install.html)
|
||||
|
||||
## 合作伙伴
|
||||
|
||||
如果耗子面板对您有帮助,欢迎赞助我们,同时感谢以下支持者/赞助商的支持:
|
||||
|
||||
 
|
||||
|
||||
<a href="https://www.weixiaoduo.com/">
|
||||
<img height="80" src=".github/assets/wxd.png" alt="微晓朵">
|
||||
</a>
|
||||
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
|
||||
<img height="80" src=".github/assets/dk.png" alt="林枫云">
|
||||
</a>
|
||||
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
|
||||
<img height="80" src=".github/assets/sctes.png" alt="无畏云加速">
|
||||
</a>
|
||||
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
|
||||
<img height="80" src=".github/assets/wafpro.png" alt="WAFPRO">
|
||||
</a>
|
||||
<a href="https://scdn.ddunyun.com/">
|
||||
<img height="80" src=".github/assets/ddunyun.png" alt="盾云SCDN">
|
||||
</a>
|
||||
<a href="https://1ms.run">
|
||||
<img height="80" src=".github/assets/1ms.svg" alt="毫秒镜像提供经过审核的 Docker 镜像加速服务">
|
||||
</a>
|
||||
如果耗子面板对您有帮助,欢迎[赞助我们](https://github.com/tnborg/panel/issues/90),同时感谢以下支持者/赞助商的支持:
|
||||
|
||||
<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 href="https://www.weixiaoduo.com/">
|
||||
<img height="60" src=".github/assets/wxd.png" alt="微晓朵">
|
||||
</a>
|
||||
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
|
||||
<img height="60" src=".github/assets/dk.png" alt="林枫云">
|
||||
</a>
|
||||
<a href="https://waf.pro/">
|
||||
<img height="60" src=".github/assets/wafpro.png" alt="WAFPRO">
|
||||
</a>
|
||||
<a href="https://scdn.ddunyun.com/">
|
||||
<img height="60" src=".github/assets/ddunyun.png" alt="盾云SCDN">
|
||||
</a>
|
||||
<a href="https://1ms.run/">
|
||||
<img height="60" src=".github/assets/1ms.svg" alt="毫秒镜像">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://afdian.com/a/tnblabs">
|
||||
<img alt="sponsors" src="https://github.com/tnborg/sponsor/blob/main/sponsors.svg?raw=true"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Star 历史
|
||||
|
||||
<a href="https://star-history.com/#TheTNB/panel&Date">
|
||||
<a href="https://star-history.com/#tnborg/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" />
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
+44
-40
@@ -4,15 +4,18 @@
|
||||
|
||||
<h1 align="center" style="font-size: 40px">Rat Panel</h1>
|
||||
|
||||
<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>
|
||||
<div align="center">
|
||||
|
||||
Enterprise-level all-in-one server operation and maintenance management panel. Simple and lightweight, efficient operation and maintenance.
|
||||
[](https://go.dev/)
|
||||
[](https://github.com/tnborg/panel/releases)
|
||||
[](https://github.com/tnborg/panel/actions)
|
||||
[](https://goreportcard.com/report/github.com/tnborg/panel)
|
||||
[](https://github.com/tnborg/panel)
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
</div>
|
||||
|
||||
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)
|
||||
|
||||
@@ -27,51 +30,52 @@ Website: [panel.haozi.net](https://panel.haozi.net) | QQ group: [12370907](https
|
||||
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
|
||||
|
||||
## Quick Install
|
||||
|
||||
Supported clean mainstream systems under `amd64` | `arm64` architecture, please refer to the [installation documentation](https://ratpanel.github.io/quickstart/install) for specific supported systems.
|
||||
|
||||
```shell
|
||||
curl -sSLOm 10 https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
|
||||
```
|
||||
|
||||
## UI Screenshots
|
||||
|
||||

|
||||
|
||||
## Quick Install
|
||||
|
||||
[https://ratpanel.github.io/quickstart/install.html](https://ratpanel.github.io/quickstart/install.html)
|
||||
|
||||
## Partners
|
||||
|
||||
If the Rat Panel is helpful to you, welcome to sponsor us, also thanks to the following supporters/sponsors:
|
||||
|
||||
 
|
||||
|
||||
<a href="https://www.weixiaoduo.com/">
|
||||
<img height="80" src=".github/assets/wxd.png" alt="微晓朵">
|
||||
</a>
|
||||
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
|
||||
<img height="80" src=".github/assets/dk.png" alt="林枫云">
|
||||
</a>
|
||||
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
|
||||
<img height="80" src=".github/assets/sctes.png" alt="无畏云加速">
|
||||
</a>
|
||||
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
|
||||
<img height="80" src=".github/assets/wafpro.png" alt="WAFPRO">
|
||||
</a>
|
||||
<a href="https://scdn.ddunyun.com/">
|
||||
<img height="80" src=".github/assets/ddunyun.png" alt="盾云SCDN">
|
||||
</a>
|
||||
<a href="https://1ms.run">
|
||||
<img height="80" src=".github/assets/1ms.svg" alt="毫秒镜像提供经过审核的 Docker 镜像加速服务">
|
||||
</a>
|
||||
If the Rat Panel is helpful to you, welcome to [sponsor us](https://github.com/tnborg/panel/issues/90), also thanks to the following supporters/sponsors:
|
||||
|
||||
<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 href="https://www.weixiaoduo.com/">
|
||||
<img height="60" src=".github/assets/wxd.png" alt="微晓朵">
|
||||
</a>
|
||||
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
|
||||
<img height="60" src=".github/assets/dk.png" alt="林枫云">
|
||||
</a>
|
||||
<a href="https://waf.pro/">
|
||||
<img height="60" src=".github/assets/wafpro.png" alt="WAFPRO">
|
||||
</a>
|
||||
<a href="https://scdn.ddunyun.com/">
|
||||
<img height="60" src=".github/assets/ddunyun.png" alt="盾云SCDN">
|
||||
</a>
|
||||
<a href="https://1ms.run/">
|
||||
<img height="60" src=".github/assets/1ms.svg" alt="毫秒镜像">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://afdian.com/a/tnblabs">
|
||||
<img alt="sponsors" src="https://github.com/tnborg/sponsor/blob/main/sponsors.svg?raw=true"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://star-history.com/#TheTNB/panel&Date">
|
||||
<a href="https://star-history.com/#tnborg/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" />
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
+19
-14
@@ -1,25 +1,30 @@
|
||||
## 安全说明
|
||||
## Supported Versions
|
||||
|
||||
**我们不对面板的安全性做任何保证**,但安全性是我们最关心的问题之一,我们已在多个不同应用的生产环境广泛应用耗子面板,至今无任何安全事故。
|
||||
|
||||
如果您在面板中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
|
||||
|
||||
- 邮箱:[admin@haozi.net](mailto:admin@haozi.net)
|
||||
- QQ:826896000
|
||||
|
||||
提前感谢您的支持与帮助!
|
||||
|
||||
致某些安全初学者:通过面板 `session` / `access_token` 执行的任何操作(包括且不限于:获取 root 权限、读取/写入系统敏感文件、执行任意 shell 命令等)均不被认为是安全问题,请不要刷此类报告浪费彼此的时间,这类低水平的报告对你的简历也没有任何帮助。
|
||||
| Version | Supported |
|
||||
|---------|--------------------|
|
||||
| v2.x | :white_check_mark: |
|
||||
| v1.x | :x: |
|
||||
|
||||
## Security Policy
|
||||
|
||||
**We do not make any guarantees about the security of the panel**, but security is one of our top concerns. We have widely used the Rat panel in the production environment of multiple different applications, and there has been no security incidents to date.
|
||||
|
||||
If you find any security issues while using the panel, please do not submit an Issue. You can contact us directly through the following methods:
|
||||
|
||||
- (Recommend) [GitHub Security Advisories](https://github.com/tnborg/panel/security/advisories/new)
|
||||
- Email: [admin@haozi.net](mailto:admin@haozi.net)
|
||||
- Telegram: @devhaozi
|
||||
|
||||
Thank you in advance for your support and help!
|
||||
|
||||
To some security beginners: Any operation performed through the panel `session` / `access_token` (including but not limited to: obtaining root permissions, reading/writing sensitive system files, executing arbitrary shell commands, etc.) is not considered a security issue. Please do not waste each other's time by submitting such reports. Such low-level reports are of no help to your resume.
|
||||
To some security beginners: Any operation performed through an already logged-in panel's `session` / `access_token` (including but not limited to: obtaining root permissions, reading/writing sensitive system files, executing arbitrary shell commands, etc.) is not considered a security issue. Please do not waste each other's time by submitting such reports.
|
||||
|
||||
## 安全说明
|
||||
|
||||
如果您在面板中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
|
||||
|
||||
- (推荐)[GitHub 安全公告](https://github.com/tnborg/panel/security/advisories/new)
|
||||
- 邮箱:[admin@haozi.net](mailto:admin@haozi.net)
|
||||
- QQ:826896000
|
||||
|
||||
提前感谢您的支持与帮助!
|
||||
|
||||
致某些安全初学者:通过已登录面板的 `session` / `access_token` 执行的任何操作(包括且不限于:获取 root 权限、读取/写入系统敏感文件、执行任意 shell 命令等)均不被认为是安全问题,请不要刷此类报告浪费彼此的时间。
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# cmd
|
||||
|
||||
cmd 目录存放应用的入口文件。
|
||||
+7
-6
@@ -5,14 +5,15 @@ package main
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/bootstrap"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/route"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/apps"
|
||||
"github.com/tnborg/panel/internal/bootstrap"
|
||||
"github.com/tnborg/panel/internal/data"
|
||||
"github.com/tnborg/panel/internal/route"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
)
|
||||
|
||||
// initCli init command line.
|
||||
func initCli() (*app.Cli, error) {
|
||||
panic(wire.Build(bootstrap.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, app.NewCli))
|
||||
panic(wire.Build(bootstrap.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, apps.ProviderSet, app.NewCli))
|
||||
}
|
||||
|
||||
+71
-20
@@ -7,11 +7,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/bootstrap"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/route"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/apps/codeserver"
|
||||
"github.com/tnborg/panel/internal/apps/docker"
|
||||
"github.com/tnborg/panel/internal/apps/fail2ban"
|
||||
"github.com/tnborg/panel/internal/apps/frp"
|
||||
"github.com/tnborg/panel/internal/apps/gitea"
|
||||
"github.com/tnborg/panel/internal/apps/memcached"
|
||||
"github.com/tnborg/panel/internal/apps/minio"
|
||||
"github.com/tnborg/panel/internal/apps/mysql"
|
||||
"github.com/tnborg/panel/internal/apps/nginx"
|
||||
"github.com/tnborg/panel/internal/apps/php74"
|
||||
"github.com/tnborg/panel/internal/apps/php80"
|
||||
"github.com/tnborg/panel/internal/apps/php81"
|
||||
"github.com/tnborg/panel/internal/apps/php82"
|
||||
"github.com/tnborg/panel/internal/apps/php83"
|
||||
"github.com/tnborg/panel/internal/apps/php84"
|
||||
"github.com/tnborg/panel/internal/apps/phpmyadmin"
|
||||
"github.com/tnborg/panel/internal/apps/podman"
|
||||
"github.com/tnborg/panel/internal/apps/postgresql"
|
||||
"github.com/tnborg/panel/internal/apps/pureftpd"
|
||||
"github.com/tnborg/panel/internal/apps/redis"
|
||||
"github.com/tnborg/panel/internal/apps/rsync"
|
||||
"github.com/tnborg/panel/internal/apps/s3fs"
|
||||
"github.com/tnborg/panel/internal/apps/supervisor"
|
||||
"github.com/tnborg/panel/internal/bootstrap"
|
||||
"github.com/tnborg/panel/internal/data"
|
||||
"github.com/tnborg/panel/internal/route"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
)
|
||||
|
||||
import (
|
||||
@@ -26,6 +49,10 @@ func initCli() (*app.Cli, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
locale, err := bootstrap.NewT(koanf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger := bootstrap.NewLog(koanf)
|
||||
db, err := bootstrap.NewDB(koanf, logger)
|
||||
if err != nil {
|
||||
@@ -33,21 +60,45 @@ func initCli() (*app.Cli, error) {
|
||||
}
|
||||
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)
|
||||
taskRepo := data.NewTaskRepo(locale, db, logger, queue)
|
||||
appRepo := data.NewAppRepo(locale, koanf, db, cacheRepo, taskRepo)
|
||||
userRepo := data.NewUserRepo(locale, db)
|
||||
settingRepo := data.NewSettingRepo(locale, db, koanf, taskRepo)
|
||||
databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger)
|
||||
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, databaseServerRepo)
|
||||
databaseRepo := data.NewDatabaseRepo(locale, db, databaseServerRepo, databaseUserRepo)
|
||||
certRepo := data.NewCertRepo(locale, db, logger)
|
||||
certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger)
|
||||
websiteRepo := data.NewWebsiteRepo(locale, db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo)
|
||||
backupRepo := data.NewBackupRepo(locale, db, settingRepo, websiteRepo)
|
||||
cliService := service.NewCliService(locale, koanf, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo)
|
||||
cli := route.NewCli(locale, cliService)
|
||||
command := bootstrap.NewCli(locale, cli)
|
||||
gormigrate := bootstrap.NewMigrate(db)
|
||||
appCli := app.NewCli(command, gormigrate)
|
||||
codeserverApp := codeserver.NewApp()
|
||||
dockerApp := docker.NewApp()
|
||||
fail2banApp := fail2ban.NewApp(locale, websiteRepo)
|
||||
frpApp := frp.NewApp()
|
||||
giteaApp := gitea.NewApp()
|
||||
memcachedApp := memcached.NewApp(locale)
|
||||
minioApp := minio.NewApp()
|
||||
mysqlApp := mysql.NewApp(locale, settingRepo)
|
||||
nginxApp := nginx.NewApp(locale)
|
||||
php74App := php74.NewApp(locale, taskRepo)
|
||||
php80App := php80.NewApp(locale, taskRepo)
|
||||
php81App := php81.NewApp(locale, taskRepo)
|
||||
php82App := php82.NewApp(locale, taskRepo)
|
||||
php83App := php83.NewApp(locale, taskRepo)
|
||||
php84App := php84.NewApp(locale, taskRepo)
|
||||
phpmyadminApp := phpmyadmin.NewApp(locale)
|
||||
podmanApp := podman.NewApp()
|
||||
postgresqlApp := postgresql.NewApp(locale)
|
||||
pureftpdApp := pureftpd.NewApp(locale)
|
||||
redisApp := redis.NewApp(locale)
|
||||
rsyncApp := rsync.NewApp(locale)
|
||||
s3fsApp := s3fs.NewApp(locale)
|
||||
supervisorApp := supervisor.NewApp(locale)
|
||||
loader := bootstrap.NewLoader(codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp)
|
||||
appCli := app.NewCli(command, gormigrate, loader)
|
||||
return appCli, nil
|
||||
}
|
||||
|
||||
+8
-8
@@ -5,14 +5,14 @@ package main
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/apps"
|
||||
"github.com/TheTNB/panel/internal/bootstrap"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/http/middleware"
|
||||
"github.com/TheTNB/panel/internal/job"
|
||||
"github.com/TheTNB/panel/internal/route"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/apps"
|
||||
"github.com/tnborg/panel/internal/bootstrap"
|
||||
"github.com/tnborg/panel/internal/data"
|
||||
"github.com/tnborg/panel/internal/http/middleware"
|
||||
"github.com/tnborg/panel/internal/job"
|
||||
"github.com/tnborg/panel/internal/route"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
)
|
||||
|
||||
// initWeb init application.
|
||||
|
||||
+88
-68
@@ -7,32 +7,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/apps"
|
||||
"github.com/TheTNB/panel/internal/apps/benchmark"
|
||||
"github.com/TheTNB/panel/internal/apps/docker"
|
||||
"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/memcached"
|
||||
"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/internal/bootstrap"
|
||||
"github.com/TheTNB/panel/internal/data"
|
||||
"github.com/TheTNB/panel/internal/http/middleware"
|
||||
"github.com/TheTNB/panel/internal/job"
|
||||
"github.com/TheTNB/panel/internal/route"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/apps/codeserver"
|
||||
"github.com/tnborg/panel/internal/apps/docker"
|
||||
"github.com/tnborg/panel/internal/apps/fail2ban"
|
||||
"github.com/tnborg/panel/internal/apps/frp"
|
||||
"github.com/tnborg/panel/internal/apps/gitea"
|
||||
"github.com/tnborg/panel/internal/apps/memcached"
|
||||
"github.com/tnborg/panel/internal/apps/minio"
|
||||
"github.com/tnborg/panel/internal/apps/mysql"
|
||||
"github.com/tnborg/panel/internal/apps/nginx"
|
||||
"github.com/tnborg/panel/internal/apps/php74"
|
||||
"github.com/tnborg/panel/internal/apps/php80"
|
||||
"github.com/tnborg/panel/internal/apps/php81"
|
||||
"github.com/tnborg/panel/internal/apps/php82"
|
||||
"github.com/tnborg/panel/internal/apps/php83"
|
||||
"github.com/tnborg/panel/internal/apps/php84"
|
||||
"github.com/tnborg/panel/internal/apps/phpmyadmin"
|
||||
"github.com/tnborg/panel/internal/apps/podman"
|
||||
"github.com/tnborg/panel/internal/apps/postgresql"
|
||||
"github.com/tnborg/panel/internal/apps/pureftpd"
|
||||
"github.com/tnborg/panel/internal/apps/redis"
|
||||
"github.com/tnborg/panel/internal/apps/rsync"
|
||||
"github.com/tnborg/panel/internal/apps/s3fs"
|
||||
"github.com/tnborg/panel/internal/apps/supervisor"
|
||||
"github.com/tnborg/panel/internal/bootstrap"
|
||||
"github.com/tnborg/panel/internal/data"
|
||||
"github.com/tnborg/panel/internal/http/middleware"
|
||||
"github.com/tnborg/panel/internal/job"
|
||||
"github.com/tnborg/panel/internal/route"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
)
|
||||
|
||||
import (
|
||||
@@ -47,6 +51,10 @@ func initWeb() (*app.Web, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
locale, err := bootstrap.NewT(koanf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger := bootstrap.NewLog(koanf)
|
||||
db, err := bootstrap.NewDB(koanf, logger)
|
||||
if err != nil {
|
||||
@@ -58,75 +66,87 @@ func initWeb() (*app.Web, error) {
|
||||
}
|
||||
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)
|
||||
taskRepo := data.NewTaskRepo(locale, db, logger, queue)
|
||||
appRepo := data.NewAppRepo(locale, koanf, db, cacheRepo, taskRepo)
|
||||
userTokenRepo := data.NewUserTokenRepo(locale, db)
|
||||
middlewares := middleware.NewMiddlewares(koanf, logger, manager, appRepo, userTokenRepo)
|
||||
userRepo := data.NewUserRepo(locale, db)
|
||||
userService := service.NewUserService(locale, koanf, manager, userRepo)
|
||||
userTokenService := service.NewUserTokenService(locale, userTokenRepo)
|
||||
databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger)
|
||||
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, databaseServerRepo)
|
||||
databaseRepo := data.NewDatabaseRepo(locale, db, databaseServerRepo, databaseUserRepo)
|
||||
certRepo := data.NewCertRepo(locale, db, logger)
|
||||
certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger)
|
||||
websiteRepo := data.NewWebsiteRepo(locale, db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo)
|
||||
settingRepo := data.NewSettingRepo(locale, db, koanf, taskRepo)
|
||||
cronRepo := data.NewCronRepo(locale, db)
|
||||
backupRepo := data.NewBackupRepo(locale, db, settingRepo, websiteRepo)
|
||||
dashboardService := service.NewDashboardService(locale, 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)
|
||||
backupService := service.NewBackupService(locale, backupRepo)
|
||||
certService := service.NewCertService(locale, certRepo)
|
||||
certDNSRepo := data.NewCertDNSRepo(db)
|
||||
certDNSService := service.NewCertDNSService(certDNSRepo)
|
||||
certAccountService := service.NewCertAccountService(certAccountRepo)
|
||||
appService := service.NewAppService(appRepo, cacheRepo, settingRepo)
|
||||
appService := service.NewAppService(locale, appRepo, cacheRepo, settingRepo)
|
||||
cronService := service.NewCronService(cronRepo)
|
||||
processService := service.NewProcessService()
|
||||
safeRepo := data.NewSafeRepo()
|
||||
safeService := service.NewSafeService(safeRepo)
|
||||
firewallService := service.NewFirewallService()
|
||||
sshRepo := data.NewSSHRepo(db)
|
||||
sshRepo := data.NewSSHRepo(locale, 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)
|
||||
fileService := service.NewFileService(locale, taskRepo)
|
||||
monitorRepo := data.NewMonitorRepo(db, settingRepo)
|
||||
monitorService := service.NewMonitorService(settingRepo, monitorRepo)
|
||||
settingService := service.NewSettingService(settingRepo)
|
||||
systemctlService := service.NewSystemctlService()
|
||||
benchmarkApp := benchmark.NewApp()
|
||||
systemctlService := service.NewSystemctlService(locale)
|
||||
toolboxSystemService := service.NewToolboxSystemService(locale)
|
||||
toolboxBenchmarkService := service.NewToolboxBenchmarkService(locale)
|
||||
toolboxMigrationService := service.NewToolboxMigrationService(locale, koanf, logger, settingRepo, websiteRepo, databaseRepo, databaseServerRepo, databaseUserRepo, appRepo)
|
||||
codeserverApp := codeserver.NewApp()
|
||||
dockerApp := docker.NewApp()
|
||||
fail2banApp := fail2ban.NewApp(websiteRepo)
|
||||
fail2banApp := fail2ban.NewApp(locale, websiteRepo)
|
||||
frpApp := frp.NewApp()
|
||||
giteaApp := gitea.NewApp()
|
||||
memcachedApp := memcached.NewApp()
|
||||
mysqlApp := mysql.NewApp(settingRepo)
|
||||
nginxApp := nginx.NewApp()
|
||||
phpApp := php.NewApp(taskRepo)
|
||||
phpmyadminApp := phpmyadmin.NewApp()
|
||||
memcachedApp := memcached.NewApp(locale)
|
||||
minioApp := minio.NewApp()
|
||||
mysqlApp := mysql.NewApp(locale, settingRepo)
|
||||
nginxApp := nginx.NewApp(locale)
|
||||
php74App := php74.NewApp(locale, taskRepo)
|
||||
php80App := php80.NewApp(locale, taskRepo)
|
||||
php81App := php81.NewApp(locale, taskRepo)
|
||||
php82App := php82.NewApp(locale, taskRepo)
|
||||
php83App := php83.NewApp(locale, taskRepo)
|
||||
php84App := php84.NewApp(locale, taskRepo)
|
||||
phpmyadminApp := phpmyadmin.NewApp(locale)
|
||||
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 := apps.NewLoader(benchmarkApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, mysqlApp, nginxApp, phpApp, 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, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, loader)
|
||||
wsService := service.NewWsService(koanf, sshRepo)
|
||||
ws := route.NewWs(wsService)
|
||||
mux, err := bootstrap.NewRouter(middlewares, http, ws)
|
||||
postgresqlApp := postgresql.NewApp(locale)
|
||||
pureftpdApp := pureftpd.NewApp(locale)
|
||||
redisApp := redis.NewApp(locale)
|
||||
rsyncApp := rsync.NewApp(locale)
|
||||
s3fsApp := s3fs.NewApp(locale)
|
||||
supervisorApp := supervisor.NewApp(locale)
|
||||
loader := bootstrap.NewLoader(codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp)
|
||||
http := route.NewHttp(userService, userTokenService, 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, toolboxSystemService, toolboxBenchmarkService, toolboxMigrationService, loader)
|
||||
wsService := service.NewWsService(locale, koanf, sshRepo)
|
||||
ws := route.NewWs(wsService, toolboxMigrationService)
|
||||
mux, err := bootstrap.NewRouter(locale, middlewares, http, ws)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -135,12 +155,12 @@ func initWeb() (*app.Web, error) {
|
||||
return nil, err
|
||||
}
|
||||
gormigrate := bootstrap.NewMigrate(db)
|
||||
jobs := job.NewJobs(db, logger, settingRepo, certRepo, backupRepo, cacheRepo)
|
||||
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)
|
||||
validation := bootstrap.NewValidator(koanf, db)
|
||||
web := app.NewWeb(koanf, mux, server, gormigrate, cron, queue, validation)
|
||||
return web, nil
|
||||
}
|
||||
|
||||
@@ -11,3 +11,5 @@ http:
|
||||
tls: true
|
||||
database:
|
||||
debug: false
|
||||
session:
|
||||
lifetime: 120
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
commit_message: 'Update translations (%language%) %original_file_name%'
|
||||
pull_request_title: 'l10n: update translations'
|
||||
files:
|
||||
- source: /pkg/embed/locales/*.pot
|
||||
translation: /pkg/embed/locales/%locale_with_underscore%/%file_name%.po
|
||||
- source: /web/src/locales/*.pot
|
||||
translation: /web/src/locales/%locale_with_underscore%.po
|
||||
@@ -1,94 +1,100 @@
|
||||
module github.com/TheTNB/panel
|
||||
module github.com/tnborg/panel
|
||||
|
||||
go 1.23
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/bddjr/hlfhr v1.3.6
|
||||
github.com/bddjr/hlfhr v1.3.8
|
||||
github.com/beevik/ntp v1.4.3
|
||||
github.com/coder/websocket v1.8.14
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/expr-lang/expr v1.16.9
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-chi/chi/v5 v5.2.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.3
|
||||
github.com/go-rat/chix v1.1.4
|
||||
github.com/go-rat/gormstore v1.0.6
|
||||
github.com/go-rat/sessions v1.0.11
|
||||
github.com/go-rat/utils v1.0.7
|
||||
github.com/go-resty/resty/v2 v2.16.2
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a
|
||||
github.com/expr-lang/expr v1.17.6
|
||||
github.com/go-chi/chi/v5 v5.2.2
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.4
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/golang-cz/httplog v0.0.2
|
||||
github.com/gomodule/redigo v1.9.2
|
||||
github.com/google/wire v0.6.0
|
||||
github.com/gookit/validate v1.5.4
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/gookit/validate v1.5.5
|
||||
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.2
|
||||
github.com/knadh/koanf/parsers/yaml v1.1.0
|
||||
github.com/knadh/koanf/providers/file v1.2.0
|
||||
github.com/knadh/koanf/v2 v2.2.2
|
||||
github.com/leonelquinteros/gotext v1.7.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/huaweicloud v0.3.1
|
||||
github.com/libdns/libdns v0.2.2
|
||||
github.com/libdns/tencentcloud v1.1.0
|
||||
github.com/mholt/acmez/v3 v3.0.0
|
||||
github.com/libdns/alidns v1.0.5-libdns.v1.beta1
|
||||
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6
|
||||
github.com/libdns/cloudns v1.1.0
|
||||
github.com/libdns/gcore v0.0.0-20250427050847-9964da923833
|
||||
github.com/libdns/hetzner v1.0.0
|
||||
github.com/libdns/huaweicloud v1.0.0
|
||||
github.com/libdns/libdns v1.1.0
|
||||
github.com/libdns/namesilo v1.0.0
|
||||
github.com/libdns/porkbun v1.1.0
|
||||
github.com/libdns/tencentcloud v1.4.2
|
||||
github.com/libdns/westcn v1.0.2
|
||||
github.com/libtnb/chix v1.3.0
|
||||
github.com/libtnb/gormstore v1.1.0
|
||||
github.com/libtnb/sessions v1.2.0
|
||||
github.com/libtnb/utils v1.2.0
|
||||
github.com/mholt/acmez/v3 v3.1.2
|
||||
github.com/ncruces/go-sqlite3 v0.27.1
|
||||
github.com/ncruces/go-sqlite3/gormlite v0.24.0
|
||||
github.com/orandin/slog-gorm v1.4.0
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samber/lo v1.47.0
|
||||
github.com/sethvargo/go-limiter v1.0.0
|
||||
github.com/shirou/gopsutil v2.21.11+incompatible
|
||||
github.com/spf13/cast v1.7.1
|
||||
github.com/samber/lo v1.51.0
|
||||
github.com/sethvargo/go-limiter v1.0.1-0.20250412144437-fa26982c7e1a
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/spf13/cast v1.9.2
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20241225173012-5cc15aab65bd
|
||||
github.com/urfave/cli/v3 v3.0.0-beta1
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/net v0.33.0
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20250620092546-c3e307e36701
|
||||
github.com/urfave/cli/v3 v3.4.1
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/net v0.43.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/gorm v1.25.12
|
||||
gorm.io/gorm v1.30.1
|
||||
resty.dev/v3 v3.0.0-beta.6
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/G-Core/gcore-dns-sdk-go v0.3.2 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // 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.2.1 // indirect
|
||||
github.com/gofiber/schema v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gofiber/schema v1.6.0 // indirect
|
||||
github.com/gookit/filter v1.2.2 // indirect
|
||||
github.com/gookit/goutil v0.6.18 // indirect
|
||||
github.com/gookit/goutil v0.7.0 // indirect
|
||||
github.com/imega/luaformatter v0.0.0-20211025140405-86b0a68d6bef // 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/knadh/koanf/maps v0.1.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.2 // indirect
|
||||
github.com/libtnb/securecookie v1.2.0 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // 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/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1051 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
github.com/timtadh/data-structures v0.6.2 // indirect
|
||||
github.com/timtadh/lexmachine v0.2.3 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // 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-20250718183923-645b1fa84792 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/libdns/tencentcloud => github.com/devhaozi/tencentcloud v0.0.0-20241129095955-7d4e898ce504
|
||||
github.com/mholt/acmez/v3 => github.com/TheTNB/acmez/v3 v3.0.0-20241217024017-a2f2c48efdcc
|
||||
)
|
||||
replace github.com/mholt/acmez/v3 => github.com/libtnb/acmez/v3 v3.0.0-20250707093727-dc5aedd96413
|
||||
|
||||
@@ -1,175 +1,401 @@
|
||||
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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
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/TheTNB/acmez/v3 v3.0.0-20241217024017-a2f2c48efdcc h1:Nh7pc2CKHFF1lg+38slGKgJZ4A8cG30ERs7pwK/+V6Y=
|
||||
github.com/TheTNB/acmez/v3 v3.0.0-20241217024017-a2f2c48efdcc/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/bddjr/hlfhr v1.3.6 h1:XautNR6Vomud8SGh1V1Yjoj1L4YblsNM0ZUwZ7r3q4U=
|
||||
github.com/bddjr/hlfhr v1.3.6/go.mod h1:oyIv4Q9JpCgZFdtH3KyTNWp7YYRWl4zl8k4ozrMAB4g=
|
||||
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/G-Core/gcore-dns-sdk-go v0.3.2 h1:WCTKmoJ5uND69UFE318yxWuHy/h3AA28Z1OnqSYUxRk=
|
||||
github.com/G-Core/gcore-dns-sdk-go v0.3.2/go.mod h1:35t795gOfzfVanhzkFyUXEzaBuMXwETmJldPpP28MN4=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
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/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
|
||||
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
||||
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/devhaozi/tencentcloud v0.0.0-20241129095955-7d4e898ce504 h1:Wk7RQyamGDfAz+5hZIT/GIm6ay5cORztgYVXOJTobKY=
|
||||
github.com/devhaozi/tencentcloud v0.0.0-20241129095955-7d4e898ce504/go.mod h1:o0+WCxQ7LGLtyjnjYU4HbGW9uVjN44SdUDhxdUYLGPw=
|
||||
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/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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/expr-lang/expr v1.17.6 h1:1h6i8ONk9cexhDmowO/A64VPxHScu7qfSl2k8OlINec=
|
||||
github.com/expr-lang/expr v1.17.6/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
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.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
|
||||
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.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
|
||||
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
|
||||
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.4 h1:KOPEt27qy1cNzHfMZbp9YTmEuzkY4F4wrdsJW9WFk1U=
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.4/go.mod h1:y/6gPAH6QGAgP1UfHMiXcqGeJ88/GRQbfCReE1JJD5Y=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
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-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.7 h1:hZUe+n1W/tvF3j+oP/TeAHX3AF98SjVB1FuF11zkCAI=
|
||||
github.com/go-rat/utils v1.0.7/go.mod h1:b0S/vd2bXWvPUQpw8z6RmJrUG7G7BkMuNSrsz6JRBHs=
|
||||
github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
|
||||
github.com/go-resty/resty/v2 v2.16.2/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.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
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/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/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-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.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gofiber/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY=
|
||||
github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang-cz/httplog v0.0.2 h1:3d8iScWLeMWQG5/bfMZ5Dizh+zvRfNmLBZMe5N2HrGU=
|
||||
github.com/golang-cz/httplog v0.0.2/go.mod h1:bgk4Ij/0OQ89UeoFFAQrSNhbbr4rKJ0fwWfo7wc+TCc=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/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/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/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/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/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.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/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/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
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/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
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/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/gookit/goutil v0.7.0 h1:HD4PUDW2LOSKIEBJPFD8PzNGLsL46ztpfXWVU+WtAxk=
|
||||
github.com/gookit/goutil v0.7.0/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU=
|
||||
github.com/gookit/validate v1.5.5 h1:jzMrwAYy9tbQa0mH8YFnv3C3JQR/0N4pRTsYiySwRJM=
|
||||
github.com/gookit/validate v1.5.5/go.mod h1:p9sRPfpvYB4vXICBpEPzv8FoAky+XhUOhWQghgmmat4=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
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/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
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/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
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/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/imega/luaformatter v0.0.0-20211025140405-86b0a68d6bef h1:RC993DdTIHNItsyLj79fgZNLzrf9tBN0GR6W5ZPms6s=
|
||||
github.com/imega/luaformatter v0.0.0-20211025140405-86b0a68d6bef/go.mod h1:i2XCfvmO94HrEOQWllihhtPrkvNfuB2R2p/o6+OVnRU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
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.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
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.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
|
||||
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
|
||||
github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
|
||||
github.com/knadh/koanf/parsers/yaml v1.1.0 h1:3ltfm9ljprAHt4jxgeYLlFPmUaunuCgu1yILuTXRdM4=
|
||||
github.com/knadh/koanf/parsers/yaml v1.1.0/go.mod h1:HHmcHXUrp9cOPcuC+2wrr44GTUB0EC+PyfN3HZD9tFg=
|
||||
github.com/knadh/koanf/providers/file v1.2.0 h1:hrUJ6Y9YOA49aNu/RSYzOTFlqzXSCpmYIDXI7OJU6+U=
|
||||
github.com/knadh/koanf/providers/file v1.2.0/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=
|
||||
github.com/knadh/koanf/v2 v2.2.2 h1:ghbduIkpFui3L587wavneC9e3WIliCgiCgdxYO/wd7A=
|
||||
github.com/knadh/koanf/v2 v2.2.2/go.mod h1:abWQc0cBXLSF/PSOMCB/SK+T13NXDsPvOksbpi5e/9Q=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
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/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc=
|
||||
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
|
||||
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/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.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
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/libdns/alidns v1.0.5-libdns.v1.beta1 h1:txHK7UxDed3WFBDjrTZPuMn8X+WmhjBTTAMW5xdy5pQ=
|
||||
github.com/libdns/alidns v1.0.5-libdns.v1.beta1/go.mod h1:ystHmPwcGoWjPrGpensQSMY9VoCx4cpR2hXNlwk9H/g=
|
||||
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6 h1:3MGrVWs2COjMkQR17oUw1zMIPbm2YAzxDC3oGVZvQs8=
|
||||
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6/go.mod h1:w9uTmRCDlAoafAsTPnn2nJ0XHK/eaUMh86DUk8BWi60=
|
||||
github.com/libdns/cloudns v1.1.0 h1:W+1MadtxKySn3b5RITFTsXgTIvr5VoO5x97cewjlDcs=
|
||||
github.com/libdns/cloudns v1.1.0/go.mod h1:/22V6tYYDALDpM4pw/RGGJ+X2F1Luibty9kKpKvkqBM=
|
||||
github.com/libdns/gcore v0.0.0-20250427050847-9964da923833 h1:/gsawtsq03cI7qgK65v16tYHIh40omL9YEzHWqnc63I=
|
||||
github.com/libdns/gcore v0.0.0-20250427050847-9964da923833/go.mod h1:jZJEV7pCTOJFlaUhHty+YwR05dzoSmInXK/vT3wOeVg=
|
||||
github.com/libdns/hetzner v1.0.0 h1:dFcgqTIfdiKQTqoqBBtgU9CewD8JSnB7p6BKxQ5kheM=
|
||||
github.com/libdns/hetzner v1.0.0/go.mod h1:OmuTyXMHTfy2nCqbt9KYkf0KwQSvo0ZeFGxEQSl3r2w=
|
||||
github.com/libdns/huaweicloud v1.0.0 h1:BQUIkOAjF++ouiANRIE3jdMlCfeiAAr6tGqb+8hM1jo=
|
||||
github.com/libdns/huaweicloud v1.0.0/go.mod h1:W+XywkW+C93fM50Ayklf6KuoCHZTqbrmFUEtpQnVvcU=
|
||||
github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/libdns/libdns v1.1.0 h1:9ze/tWvt7Df6sbhOJRB8jT33GHEHpEQXdtkE3hPthbU=
|
||||
github.com/libdns/libdns v1.1.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/libdns/namesilo v1.0.0 h1:Shwbj9YnSp4NR617sBOSfDkFozEnWInWCxNwrDvPkCg=
|
||||
github.com/libdns/namesilo v1.0.0/go.mod h1:qdojVsogA6eZDjDPdpIUIfv4ymiX+FuqcVV1eNmHDss=
|
||||
github.com/libdns/porkbun v1.1.0 h1:X763NqXjW26VEl7GvBtF/3CGeuGt9JqoQ35mwIlx40E=
|
||||
github.com/libdns/porkbun v1.1.0/go.mod h1:JL6NfXkkSlLr24AI5Fv0t3/Oa6PXOSOerVsOmr8+URs=
|
||||
github.com/libdns/tencentcloud v1.4.2 h1:UXvNISlwmdAapYxMgwDZW7uvn8+Bor+puLx1p6VH0c0=
|
||||
github.com/libdns/tencentcloud v1.4.2/go.mod h1:Be9gY3tDa12DuAPU79RV9NZIcjY6qg5s7zKPsP26yAM=
|
||||
github.com/libdns/westcn v1.0.2 h1:PA2M3tME5/0T3klPMzSHvGk1EWnGNNkJiKxaJjFalnM=
|
||||
github.com/libdns/westcn v1.0.2/go.mod h1:iKpk8jjOU+793Yp8nHoihPTCR6N1KWtm4/r8BB9mVnk=
|
||||
github.com/libtnb/acmez/v3 v3.0.0-20250707093727-dc5aedd96413 h1:q6Qttk+i7r8JWw1kvt0gBkCa+9mEUZ3KV25rBs2Kiik=
|
||||
github.com/libtnb/acmez/v3 v3.0.0-20250707093727-dc5aedd96413/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/libtnb/chix v1.3.0 h1:/U+CyuxI41ooeB6M/762PHOjQlfHRg6BQjHKLZarlrM=
|
||||
github.com/libtnb/chix v1.3.0/go.mod h1:o8nQLEp/UrUojBKYzw8K8sltU/h0XxI2VLZ/z7AQCQg=
|
||||
github.com/libtnb/gormstore v1.1.0 h1:VbX8u0hhyl2YFSGfkSlF/xSfFG/LE29DCLrY8MJ+uxM=
|
||||
github.com/libtnb/gormstore v1.1.0/go.mod h1:8A5QzeZxi1MpSmjUVsHTDAL6KnU84feIXMutFLPawwA=
|
||||
github.com/libtnb/securecookie v1.2.0 h1:2uc0PBDm0foeSTrcZ9QTX1IEjf6kFEwfgEYSIXQSKrA=
|
||||
github.com/libtnb/securecookie v1.2.0/go.mod h1:ja+wNGnQzYqcqXQnJWu6icsaWi5JEBwNEMJ2ReTVDxA=
|
||||
github.com/libtnb/sessions v1.2.0 h1:g/RMcfGTC5P2BQE1IqrgppPEQ++x/QjOuiwbN9Frke8=
|
||||
github.com/libtnb/sessions v1.2.0/go.mod h1:45Bn9d6PseDINLIM1QaJrlCMbzSZ0NWpDbWkdrKJKw0=
|
||||
github.com/libtnb/utils v1.2.0 h1:6bTZrWn2OkNrODpCY4dhuHwbhsVRV7HICIgmZ31we98=
|
||||
github.com/libtnb/utils v1.2.0/go.mod h1:9gSEuhkADlvYbM3qJRQUAMC5ypMrhrYpX9YMuYJ6ws8=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
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/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
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/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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/ncruces/go-sqlite3 v0.27.1 h1:suqlM7xhSyDVMV9RgX99MCPqt9mB6YOCzHZuiI36K34=
|
||||
github.com/ncruces/go-sqlite3 v0.27.1/go.mod h1:gpF5s+92aw2MbDmZK0ZOnCdFlpe11BH20CTspVqri0c=
|
||||
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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
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/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
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/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
|
||||
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||
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/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
|
||||
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sethvargo/go-limiter v1.0.1-0.20250412144437-fa26982c7e1a h1:CdCoDHVynJVAQWN7ZQrAUOp0SV5TmRwNOSkF5KedDko=
|
||||
github.com/sethvargo/go-limiter v1.0.1-0.20250412144437-fa26982c7e1a/go.mod h1:01b6tW25Ap+MeLYBuD4aHunMrJoNO5PVUFdS9rac3II=
|
||||
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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
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/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.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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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.1051 h1:3mg0L9vv9eO8UN4Oa7vNawe6yUIuXf9D0Q79rUmnblo=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1051/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||
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-20241225173012-5cc15aab65bd h1:PYNV7LTzEzZ4lousHUM2yaDO2zI3FNt9NA0iuGrbK7w=
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20241225173012-5cc15aab65bd/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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/timtadh/data-structures v0.5.3/go.mod h1:9R4XODhJ8JdWFEI8P/HJKqxuJctfBQw6fDibMQny2oU=
|
||||
github.com/timtadh/data-structures v0.6.1/go.mod h1:uYUnI1cQi/5yMCc7s23I+x8Mn8BCMf4WgK+7/4QSEk4=
|
||||
github.com/timtadh/data-structures v0.6.2 h1:zybDnU5NLjJ7WKMDJpvVwczQuf1wSLBgdRHZ9O4AqJ0=
|
||||
github.com/timtadh/data-structures v0.6.2/go.mod h1:uYUnI1cQi/5yMCc7s23I+x8Mn8BCMf4WgK+7/4QSEk4=
|
||||
github.com/timtadh/getopt v1.0.0/go.mod h1:L3EL6YN2G0eIAhYBo9b7SB9d/kEQmdnwthIlMJfj210=
|
||||
github.com/timtadh/lexmachine v0.2.2/go.mod h1:GBJvD5OAfRn/gnp92zb9KTgHLB7akKyxmVivoYCcjQI=
|
||||
github.com/timtadh/lexmachine v0.2.3 h1:ZqlfHnfMcAygtbNM5Gv7jQf8hmM8LfVzDjfCrq235NQ=
|
||||
github.com/timtadh/lexmachine v0.2.3/go.mod h1:oK1NW+93fQSIF6s+J6sXBFWsCPCFbNmrwKV1i0aqvW0=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20250620092546-c3e307e36701 h1:JgeHIJzRSEdcuLXufZrni5+a4yDnBhQG+DdKhqCFhq0=
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20250620092546-c3e307e36701/go.mod h1:ALbEe81QPWOZjDKCKNWodG2iqCMtregG8+ebQgjx2+4=
|
||||
github.com/urfave/cli/v3 v3.4.1 h1:1M9UOCy5bLmGnuu1yn3t3CB4rG79Rtoxuv1sPhnm6qM=
|
||||
github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
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.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
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-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
|
||||
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/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.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/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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=
|
||||
@@ -177,88 +403,141 @@ 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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
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/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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.6.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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
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.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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
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/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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
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-20190328211700-ab21143f2384/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-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-20191112195655-aa38f8e97acc/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.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/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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/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/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/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=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/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-20200615113413-eeeca48fe776/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.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4=
|
||||
gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
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=
|
||||
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.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
resty.dev/v3 v3.0.0-beta.6 h1:ghRdNpoE8/wBCv+kTKIOauW1aCrSIeTq7GxtfYgtevU=
|
||||
resty.dev/v3 v3.0.0-beta.6/go.mod h1:NTOerrC/4T7/FE6tXIZGIysXXBdgNqwMZuKtxpea9NM=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
||||
+10
-4
@@ -5,7 +5,10 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"github.com/gookit/color"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"github.com/tnborg/panel/pkg/apploader"
|
||||
)
|
||||
|
||||
type Cli struct {
|
||||
@@ -13,7 +16,7 @@ type Cli struct {
|
||||
migrator *gormigrate.Gormigrate
|
||||
}
|
||||
|
||||
func NewCli(cmd *cli.Command, migrator *gormigrate.Gormigrate) *Cli {
|
||||
func NewCli(cmd *cli.Command, migrator *gormigrate.Gormigrate, _ *apploader.Loader) *Cli {
|
||||
IsCli = true
|
||||
return &Cli{
|
||||
cmd: cmd,
|
||||
@@ -23,9 +26,12 @@ func NewCli(cmd *cli.Command, migrator *gormigrate.Gormigrate) *Cli {
|
||||
|
||||
func (r *Cli) Run() error {
|
||||
// migrate database
|
||||
if err := r.migrator.Migrate(); err != nil {
|
||||
return err
|
||||
// 这里不处理错误,这么做是为了在异常时用户可以用 fix 命令尝试修复
|
||||
_ = r.migrator.Migrate()
|
||||
|
||||
if err := r.cmd.Run(context.TODO(), os.Args); err != nil {
|
||||
color.Errorf("|-%v\n", err)
|
||||
}
|
||||
|
||||
return r.cmd.Run(context.Background(), os.Args)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ const (
|
||||
|
||||
// 面板全局变量
|
||||
var (
|
||||
Key string
|
||||
Root string
|
||||
Locale string
|
||||
IsCli bool
|
||||
Status = StatusNormal
|
||||
Key string // 密钥
|
||||
Root string // 根目录
|
||||
Locale string // 语言
|
||||
IsCli bool // 是否命令行
|
||||
Status = StatusNormal // 面板状态
|
||||
)
|
||||
|
||||
// 自动注入
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/queue"
|
||||
"github.com/tnborg/panel/pkg/queue"
|
||||
)
|
||||
|
||||
type Web struct {
|
||||
|
||||
+31
-146
@@ -1,44 +1,49 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/TheTNB/panel/internal/apps/benchmark"
|
||||
"github.com/TheTNB/panel/internal/apps/docker"
|
||||
"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/memcached"
|
||||
"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/tnborg/panel/internal/apps/codeserver"
|
||||
"github.com/tnborg/panel/internal/apps/docker"
|
||||
"github.com/tnborg/panel/internal/apps/fail2ban"
|
||||
"github.com/tnborg/panel/internal/apps/frp"
|
||||
"github.com/tnborg/panel/internal/apps/gitea"
|
||||
"github.com/tnborg/panel/internal/apps/memcached"
|
||||
"github.com/tnborg/panel/internal/apps/minio"
|
||||
"github.com/tnborg/panel/internal/apps/mysql"
|
||||
"github.com/tnborg/panel/internal/apps/nginx"
|
||||
"github.com/tnborg/panel/internal/apps/php74"
|
||||
"github.com/tnborg/panel/internal/apps/php80"
|
||||
"github.com/tnborg/panel/internal/apps/php81"
|
||||
"github.com/tnborg/panel/internal/apps/php82"
|
||||
"github.com/tnborg/panel/internal/apps/php83"
|
||||
"github.com/tnborg/panel/internal/apps/php84"
|
||||
"github.com/tnborg/panel/internal/apps/phpmyadmin"
|
||||
"github.com/tnborg/panel/internal/apps/podman"
|
||||
"github.com/tnborg/panel/internal/apps/postgresql"
|
||||
"github.com/tnborg/panel/internal/apps/pureftpd"
|
||||
"github.com/tnborg/panel/internal/apps/redis"
|
||||
"github.com/tnborg/panel/internal/apps/rsync"
|
||||
"github.com/tnborg/panel/internal/apps/s3fs"
|
||||
"github.com/tnborg/panel/internal/apps/supervisor"
|
||||
)
|
||||
|
||||
var ProviderSet = wire.NewSet(
|
||||
NewLoader,
|
||||
benchmark.NewApp,
|
||||
codeserver.NewApp,
|
||||
docker.NewApp,
|
||||
fail2ban.NewApp,
|
||||
frp.NewApp,
|
||||
gitea.NewApp,
|
||||
memcached.NewApp,
|
||||
minio.NewApp,
|
||||
mysql.NewApp,
|
||||
nginx.NewApp,
|
||||
php.NewApp,
|
||||
php74.NewApp,
|
||||
php80.NewApp,
|
||||
php81.NewApp,
|
||||
php82.NewApp,
|
||||
php83.NewApp,
|
||||
php84.NewApp,
|
||||
phpmyadmin.NewApp,
|
||||
podman.NewApp,
|
||||
postgresql.NewApp,
|
||||
@@ -47,124 +52,4 @@ var ProviderSet = wire.NewSet(
|
||||
rsync.NewApp,
|
||||
s3fs.NewApp,
|
||||
supervisor.NewApp,
|
||||
toolbox.NewApp,
|
||||
)
|
||||
|
||||
var slugs []string
|
||||
|
||||
type Loader struct {
|
||||
benchmark *benchmark.App
|
||||
docker *docker.App
|
||||
fail2ban *fail2ban.App
|
||||
frp *frp.App
|
||||
gitea *gitea.App
|
||||
memcached *memcached.App
|
||||
mysql *mysql.App
|
||||
nginx *nginx.App
|
||||
php *php.App
|
||||
phpmyadmin *phpmyadmin.App
|
||||
podman *podman.App
|
||||
postgresql *postgresql.App
|
||||
pureftpd *pureftpd.App
|
||||
redis *redis.App
|
||||
rsync *rsync.App
|
||||
s3fs *s3fs.App
|
||||
supervisor *supervisor.App
|
||||
toolbox *toolbox.App
|
||||
}
|
||||
|
||||
func NewLoader(
|
||||
benchmark *benchmark.App,
|
||||
docker *docker.App,
|
||||
fail2ban *fail2ban.App,
|
||||
frp *frp.App,
|
||||
gitea *gitea.App,
|
||||
memcached *memcached.App,
|
||||
mysql *mysql.App,
|
||||
nginx *nginx.App,
|
||||
php *php.App,
|
||||
phpmyadmin *phpmyadmin.App,
|
||||
podman *podman.App,
|
||||
postgresql *postgresql.App,
|
||||
pureftpd *pureftpd.App,
|
||||
redis *redis.App,
|
||||
rsync *rsync.App,
|
||||
s3fs *s3fs.App,
|
||||
supervisor *supervisor.App,
|
||||
toolbox *toolbox.App,
|
||||
) *Loader {
|
||||
loader := &Loader{
|
||||
benchmark: benchmark,
|
||||
docker: docker,
|
||||
fail2ban: fail2ban,
|
||||
frp: frp,
|
||||
gitea: gitea,
|
||||
memcached: memcached,
|
||||
mysql: mysql,
|
||||
nginx: nginx,
|
||||
php: php,
|
||||
phpmyadmin: phpmyadmin,
|
||||
podman: podman,
|
||||
postgresql: postgresql,
|
||||
pureftpd: pureftpd,
|
||||
redis: redis,
|
||||
rsync: rsync,
|
||||
s3fs: s3fs,
|
||||
supervisor: supervisor,
|
||||
toolbox: toolbox,
|
||||
}
|
||||
|
||||
loader.initSlugs()
|
||||
return loader
|
||||
}
|
||||
|
||||
func (r *Loader) Register(mux chi.Router) {
|
||||
mux.Route("/benchmark", r.benchmark.Route)
|
||||
mux.Route("/docker", r.docker.Route)
|
||||
mux.Route("/fail2ban", r.fail2ban.Route)
|
||||
mux.Route("/frp", r.frp.Route)
|
||||
mux.Route("/gitea", r.gitea.Route)
|
||||
mux.Route("/memcached", r.memcached.Route)
|
||||
mux.Route("/mysql", r.mysql.Route)
|
||||
mux.Route("/nginx", r.nginx.Route)
|
||||
mux.Route("/php74", r.php.Route(74))
|
||||
mux.Route("/php80", r.php.Route(80))
|
||||
mux.Route("/php81", r.php.Route(81))
|
||||
mux.Route("/php82", r.php.Route(82))
|
||||
mux.Route("/php83", r.php.Route(83))
|
||||
mux.Route("/php84", r.php.Route(84))
|
||||
mux.Route("/phpmyadmin", r.phpmyadmin.Route)
|
||||
mux.Route("/podman", r.podman.Route)
|
||||
mux.Route("/postgresql", r.postgresql.Route)
|
||||
mux.Route("/pureftpd", r.pureftpd.Route)
|
||||
mux.Route("/redis", r.redis.Route)
|
||||
mux.Route("/rsync", r.rsync.Route)
|
||||
mux.Route("/s3fs", r.s3fs.Route)
|
||||
mux.Route("/supervisor", r.supervisor.Route)
|
||||
mux.Route("/toolbox", r.toolbox.Route)
|
||||
}
|
||||
|
||||
func (r *Loader) initSlugs() []string {
|
||||
if len(slugs) == 0 {
|
||||
v := reflect.Indirect(reflect.ValueOf(r))
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Field(i)
|
||||
slug := strings.ToLower(v.Type().Field(i).Name)
|
||||
if !field.IsNil() {
|
||||
slugs = append(slugs, slug)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理php
|
||||
slugs = slices.DeleteFunc(slugs, func(slug string) bool {
|
||||
return slug == "php"
|
||||
})
|
||||
slugs = append(slugs, "php74", "php80", "php81", "php82", "php83", "php84")
|
||||
}
|
||||
|
||||
return slugs
|
||||
}
|
||||
|
||||
func Slugs() []string {
|
||||
return slugs
|
||||
}
|
||||
|
||||
@@ -1,691 +0,0 @@
|
||||
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/TheTNB/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
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
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,47 @@
|
||||
package codeserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/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, _ := io.Read("/root/.config/code-server/config.yaml")
|
||||
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("/root/.config/code-server/config.yaml", req.Config, 0600); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = systemctl.Restart("code-server"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package codeserver
|
||||
|
||||
type UpdateConfig struct {
|
||||
Config string `form:"config" json:"config" validate:"required"`
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
|
||||
@@ -7,23 +7,26 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/go-rat/utils/str"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/libtnb/chix"
|
||||
"github.com/libtnb/utils/str"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
t *gotext.Locale
|
||||
websiteRepo biz.WebsiteRepo
|
||||
}
|
||||
|
||||
func NewApp(website biz.WebsiteRepo) *App {
|
||||
func NewApp(t *gotext.Locale, website biz.WebsiteRepo) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
websiteRepo: website,
|
||||
}
|
||||
}
|
||||
@@ -34,8 +37,8 @@ func (s *App) Route(r chi.Router) {
|
||||
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)
|
||||
r.Post("/white_list", s.SetWhiteList)
|
||||
r.Get("/white_list", s.GetWhiteList)
|
||||
}
|
||||
|
||||
// List 所有规则
|
||||
@@ -47,12 +50,8 @@ func (s *App) List(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
jailList := regexp.MustCompile(`\[(.*?)]`).FindAllStringSubmatch(raw, -1)
|
||||
if len(jailList) == 0 {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "Fail2ban 规则为空")
|
||||
return
|
||||
}
|
||||
|
||||
var jails []Jail
|
||||
jails := make([]Jail, 0)
|
||||
for i, jail := range jailList {
|
||||
if i == 0 {
|
||||
continue
|
||||
@@ -107,7 +106,7 @@ func (s *App) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if (strings.Contains(raw, "["+jailName+"]") && jailType == "service") || (strings.Contains(raw, "["+jailWebsiteName+"]"+"-cc") && jailType == "website" && jailWebsiteMode == "cc") || (strings.Contains(raw, "["+jailWebsiteName+"]"+"-path") && jailType == "website" && jailWebsiteMode == "path") {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "规则已存在")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("rule already exists"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -115,7 +114,7 @@ func (s *App) Create(w http.ResponseWriter, r *http.Request) {
|
||||
case "website":
|
||||
website, err := s.websiteRepo.GetByName(jailWebsiteName)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "获取网站配置失败:%v", err)
|
||||
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
|
||||
return
|
||||
}
|
||||
var ports string
|
||||
@@ -140,7 +139,7 @@ logpath = ` + app.Root + `/wwwlogs/` + website.Name + `.log
|
||||
`
|
||||
raw += rule
|
||||
if err = io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -159,7 +158,7 @@ ignoreregex =
|
||||
`
|
||||
}
|
||||
if err = io.Write("/etc/fail2ban/filter.d/haozi-"+jailWebsiteName+"-"+jailWebsiteMode+".conf", filter, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -178,11 +177,11 @@ ignoreregex =
|
||||
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:
|
||||
service.Error(w, http.StatusUnprocessableEntity, "未知服务")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("unknown service"))
|
||||
return
|
||||
}
|
||||
if len(port) == 0 || err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "获取服务端口失败,请检查是否安装")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("get service port failed, please check if it is installed"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -199,13 +198,13 @@ bantime = ` + jailBanTime + `
|
||||
`
|
||||
raw += rule
|
||||
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err = shell.Execf("fail2ban-client reload"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重载配置失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -226,20 +225,20 @@ func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if !strings.Contains(raw, "["+req.Name+"]") {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "规则不存在")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("rule not found"))
|
||||
return
|
||||
}
|
||||
|
||||
rule := str.Cut(raw, "# "+req.Name+"-START", "# "+req.Name+"-END")
|
||||
raw = strings.Replace(raw, "\n# "+req.Name+"-START"+rule+"# "+req.Name+"-END", "", -1)
|
||||
raw = strings.ReplaceAll(raw, "\n# "+req.Name+"-START"+rule+"# "+req.Name+"-END", "")
|
||||
raw = strings.TrimSpace(raw)
|
||||
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重载配置失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -256,17 +255,17 @@ func (s *App) BanList(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
currentlyBan, err := shell.Execf(`fail2ban-client status %s | grep "Currently banned" | awk '{print $4}'`, req.Name)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取封禁列表失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get current banned list"))
|
||||
return
|
||||
}
|
||||
totalBan, err := shell.Execf(`fail2ban-client status %s | grep "Total banned" | awk '{print $4}'`, req.Name)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取封禁列表失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get total banned list"))
|
||||
return
|
||||
}
|
||||
bannedIp, err := shell.Execf(`fail2ban-client status %s | grep "Banned IP list" | awk -F ":" '{print $2}'`, req.Name)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取封禁列表失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get banned ip list"))
|
||||
return
|
||||
}
|
||||
bannedIpList := strings.Split(bannedIp, " ")
|
||||
@@ -300,7 +299,7 @@ func (s *App) Unban(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if _, err = shell.Execf("fail2ban-client set %s unbanip %s", req.Name, req.IP); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "解封失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -325,17 +324,17 @@ func (s *App) SetWhiteList(w http.ResponseWriter, r *http.Request) {
|
||||
if reg.MatchString(raw) {
|
||||
raw = reg.ReplaceAllString(raw, "ignoreip = "+req.IP+"\n")
|
||||
} else {
|
||||
service.Error(w, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to parse the ignoreip of fail2ban"))
|
||||
return
|
||||
}
|
||||
|
||||
if err = io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf("fail2ban-client reload"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重载配置失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
service.Success(w, nil)
|
||||
@@ -353,7 +352,7 @@ func (s *App) GetWhiteList(w http.ResponseWriter, r *http.Request) {
|
||||
ignoreIp := reg.FindStringSubmatch(raw)[1]
|
||||
service.Success(w, ignoreIp)
|
||||
} else {
|
||||
service.Error(w, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to parse the ignoreip of fail2ban"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"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/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"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/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
|
||||
@@ -7,17 +7,22 @@ import (
|
||||
"regexp"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
type App struct {
|
||||
t *gotext.Locale
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
func NewApp(t *gotext.Locale) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
@@ -29,7 +34,7 @@ func (s *App) Route(r chi.Router) {
|
||||
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)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get Memcached status: %v", err))
|
||||
return
|
||||
}
|
||||
if !status {
|
||||
@@ -42,11 +47,13 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
service.Success(w, []types.NV{})
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
defer func(conn net.Conn) {
|
||||
_ = conn.Close()
|
||||
}(conn)
|
||||
|
||||
_, err = conn.Write([]byte("stats\nquit\n"))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "failed to write to Memcached: %v", err)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to write to Memcached: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,7 +74,7 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = scanner.Err(); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "failed to read from Memcached: %v", err)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to read from Memcached: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package minio
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/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"`
|
||||
}
|
||||
+55
-51
@@ -7,25 +7,28 @@ import (
|
||||
"regexp"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"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/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/db"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
"github.com/tnborg/panel/pkg/tools"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
t *gotext.Locale
|
||||
settingRepo biz.SettingRepo
|
||||
}
|
||||
|
||||
func NewApp(setting biz.SettingRepo) *App {
|
||||
func NewApp(t *gotext.Locale, setting biz.SettingRepo) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
settingRepo: setting,
|
||||
}
|
||||
}
|
||||
@@ -34,18 +37,18 @@ 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)
|
||||
r.Post("/clear_error_log", s.ClearErrorLog)
|
||||
r.Get("/slow_log", s.SlowLog)
|
||||
r.Post("/clear_slow_log", s.ClearSlowLog)
|
||||
r.Get("/root_password", s.GetRootPassword)
|
||||
r.Post("/root_password", s.SetRootPassword)
|
||||
}
|
||||
|
||||
// GetConfig 获取配置
|
||||
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, "获取配置失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -61,12 +64,12 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = io.Write(app.Root+"/server/mysql/conf/my.cnf", req.Config, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入配置失败:%v", err)
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = systemctl.Restart("mysqld"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "重启失败:%v", err)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to restart MySQL: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -77,12 +80,12 @@ func (s *App) UpdateConfig(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密码失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to load MySQL root password: %v", err))
|
||||
return
|
||||
|
||||
}
|
||||
if len(rootPassword) == 0 {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "root密码为空")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("MySQL root password is empty"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -93,39 +96,42 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = os.Setenv("MYSQL_PWD", rootPassword); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "设置环境变量失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set MYSQL_PWD env: %v", err))
|
||||
return
|
||||
}
|
||||
raw, err := shell.Execf(`mysqladmin -u root extended-status`)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取负载失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get MySQL status: %v", err))
|
||||
return
|
||||
}
|
||||
if err = os.Unsetenv("MYSQL_PWD"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to unset MYSQL_PWD env: %v", err))
|
||||
return
|
||||
}
|
||||
_ = os.Unsetenv("MYSQL_PWD")
|
||||
|
||||
var load []map[string]string
|
||||
expressions := []struct {
|
||||
regex string
|
||||
name string
|
||||
}{
|
||||
{`Uptime\s+\|\s+(\d+)\s+\|`, "运行时间"},
|
||||
{`Queries\s+\|\s+(\d+)\s+\|`, "总查询次数"},
|
||||
{`Connections\s+\|\s+(\d+)\s+\|`, "总连接次数"},
|
||||
{`Com_commit\s+\|\s+(\d+)\s+\|`, "每秒事务"},
|
||||
{`Com_rollback\s+\|\s+(\d+)\s+\|`, "每秒回滚"},
|
||||
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, "发送"},
|
||||
{`Bytes_received\s+\|\s+(\d+)\s+\|`, "接收"},
|
||||
{`Threads_connected\s+\|\s+(\d+)\s+\|`, "活动连接数"},
|
||||
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, "峰值连接数"},
|
||||
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, "索引命中率"},
|
||||
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, "Innodb索引命中率"},
|
||||
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, "创建临时表到硬盘"},
|
||||
{`Open_tables\s+\|\s+(\d+)\s+\|`, "已打开的表"},
|
||||
{`Select_full_join\s+\|\s+(\d+)\s+\|`, "没有使用索引的量"},
|
||||
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, "没有索引的JOIN量"},
|
||||
{`Select_range_check\s+\|\s+(\d+)\s+\|`, "没有索引的子查询量"},
|
||||
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, "排序后的合并次数"},
|
||||
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, "锁表次数"},
|
||||
{`Uptime\s+\|\s+(\d+)\s+\|`, s.t.Get("Uptime")},
|
||||
{`Queries\s+\|\s+(\d+)\s+\|`, s.t.Get("Total Queries")},
|
||||
{`Connections\s+\|\s+(\d+)\s+\|`, s.t.Get("Total Connections")},
|
||||
{`Com_commit\s+\|\s+(\d+)\s+\|`, s.t.Get("Transactions per Second")},
|
||||
{`Com_rollback\s+\|\s+(\d+)\s+\|`, s.t.Get("Rollbacks per Second")},
|
||||
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, s.t.Get("Bytes Sent")},
|
||||
{`Bytes_received\s+\|\s+(\d+)\s+\|`, s.t.Get("Bytes Received")},
|
||||
{`Threads_connected\s+\|\s+(\d+)\s+\|`, s.t.Get("Active Connections")},
|
||||
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, s.t.Get("Peak Connections")},
|
||||
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, s.t.Get("Index Hit Rate")},
|
||||
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, s.t.Get("Innodb Index Hit Rate")},
|
||||
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, s.t.Get("Temporary Tables Created on Disk")},
|
||||
{`Open_tables\s+\|\s+(\d+)\s+\|`, s.t.Get("Open Tables")},
|
||||
{`Select_full_join\s+\|\s+(\d+)\s+\|`, s.t.Get("Full Joins without Index")},
|
||||
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, s.t.Get("Full Range Joins without Index")},
|
||||
{`Select_range_check\s+\|\s+(\d+)\s+\|`, s.t.Get("Subqueries without Index")},
|
||||
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, s.t.Get("Sort Merge Passes")},
|
||||
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, s.t.Get("Table Locks Waited")},
|
||||
}
|
||||
|
||||
for _, expression := range expressions {
|
||||
@@ -133,7 +139,7 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
matches := re.FindStringSubmatch(raw)
|
||||
if len(matches) > 1 {
|
||||
d := map[string]string{"name": expression.name, "value": matches[1]}
|
||||
if expression.name == "发送" || expression.name == "接收" {
|
||||
if expression.name == s.t.Get("Bytes Sent") || expression.name == s.t.Get("Bytes Received") {
|
||||
d["value"] = tools.FormatBytes(cast.ToFloat64(matches[1]))
|
||||
}
|
||||
|
||||
@@ -155,7 +161,7 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// ClearErrorLog 清空错误日志
|
||||
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
|
||||
if err := systemctl.LogsClear("mysqld"); err != nil {
|
||||
if err := systemctl.LogClear("mysqld"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -170,7 +176,7 @@ func (s *App) SlowLog(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// ClearSlowLog 清空慢查询日志
|
||||
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 {
|
||||
if _, err := shell.Execf("cat /dev/null > %s/server/mysql/mysql-slow.log", app.Root); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -182,11 +188,7 @@ func (s *App) ClearSlowLog(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密码失败")
|
||||
return
|
||||
}
|
||||
if len(rootPassword) == 0 {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "root密码为空")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to load MySQL root password: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -210,14 +212,16 @@ func (s *App) SetRootPassword(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
defer mysql.Close()
|
||||
defer func(mysql *db.MySQL) {
|
||||
_ = mysql.Close()
|
||||
}(mysql)
|
||||
if err = mysql.UserPassword("root", req.Password, "localhost"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = s.settingRepo.Set(biz.SettingKeyMySQLRootPassword, req.Password); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "设置保存失败:%v", err)
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+31
-27
@@ -8,37 +8,40 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"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/systemctl"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
"github.com/tnborg/panel/pkg/tools"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
// Dependent services
|
||||
t *gotext.Locale
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
func NewApp(t *gotext.Locale) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
r.Get("/error_log", s.ErrorLog)
|
||||
r.Post("/clear_error_log", 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, "获取配置失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,13 +56,13 @@ func (s *App) SaveConfig(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = io.Write(fmt.Sprintf("%s/server/nginx/conf/nginx.conf", app.Root), req.Config, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "保存配置失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = systemctl.Reload("nginx"); err != nil {
|
||||
_, err = shell.Execf("nginx -t")
|
||||
service.Error(w, http.StatusInternalServerError, "重载服务失败:%v", err)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -71,7 +74,7 @@ func (s *App) ErrorLog(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 {
|
||||
if _, err := shell.Execf("cat /dev/null > %s/%s", app.Root, "wwwlogs/nginx-error.log"); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -84,6 +87,7 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
resp, err := client.R().Get("http://127.0.0.1/nginx_status")
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
service.Success(w, []types.NV{})
|
||||
return
|
||||
}
|
||||
|
||||
raw := resp.String()
|
||||
@@ -91,29 +95,29 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
workers, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | wc -l")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取负载失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get nginx workers: %v", err))
|
||||
return
|
||||
}
|
||||
data = append(data, types.NV{
|
||||
Name: "工作进程",
|
||||
Name: s.t.Get("Workers"),
|
||||
Value: workers,
|
||||
})
|
||||
|
||||
out, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取负载失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get nginx workers: %v", err))
|
||||
return
|
||||
}
|
||||
mem := tools.FormatBytes(cast.ToFloat64(out))
|
||||
data = append(data, types.NV{
|
||||
Name: "内存占用",
|
||||
Name: s.t.Get("Memory"),
|
||||
Value: mem,
|
||||
})
|
||||
|
||||
match := regexp.MustCompile(`Active connections:\s+(\d+)`).FindStringSubmatch(raw)
|
||||
if len(match) == 2 {
|
||||
data = append(data, types.NV{
|
||||
Name: "活跃连接数",
|
||||
Name: s.t.Get("Active connections"),
|
||||
Value: match[1],
|
||||
})
|
||||
}
|
||||
@@ -121,15 +125,15 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
match = regexp.MustCompile(`server accepts handled requests\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
|
||||
if len(match) == 4 {
|
||||
data = append(data, types.NV{
|
||||
Name: "总连接次数",
|
||||
Name: s.t.Get("Total connections"),
|
||||
Value: match[1],
|
||||
})
|
||||
data = append(data, types.NV{
|
||||
Name: "总握手次数",
|
||||
Name: s.t.Get("Total handshakes"),
|
||||
Value: match[2],
|
||||
})
|
||||
data = append(data, types.NV{
|
||||
Name: "总请求次数",
|
||||
Name: s.t.Get("Total requests"),
|
||||
Value: match[3],
|
||||
})
|
||||
}
|
||||
@@ -137,15 +141,15 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
match = regexp.MustCompile(`Reading:\s+(\d+)\s+Writing:\s+(\d+)\s+Waiting:\s+(\d+)`).FindStringSubmatch(raw)
|
||||
if len(match) == 4 {
|
||||
data = append(data, types.NV{
|
||||
Name: "请求数",
|
||||
Name: s.t.Get("Reading"),
|
||||
Value: match[1],
|
||||
})
|
||||
data = append(data, types.NV{
|
||||
Name: "响应数",
|
||||
Name: s.t.Get("Writing"),
|
||||
Value: match[2],
|
||||
})
|
||||
data = append(data, types.NV{
|
||||
Name: "驻留进程",
|
||||
Name: s.t.Get("Waiting"),
|
||||
Value: match[3],
|
||||
})
|
||||
}
|
||||
|
||||
+242
-153
@@ -10,23 +10,26 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"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/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
version uint
|
||||
t *gotext.Locale
|
||||
taskRepo biz.TaskRepo
|
||||
}
|
||||
|
||||
func NewApp(task biz.TaskRepo) *App {
|
||||
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
taskRepo: task,
|
||||
}
|
||||
}
|
||||
@@ -35,17 +38,18 @@ func (s *App) Route(version uint) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
php := new(App)
|
||||
php.version = version
|
||||
php.t = s.t
|
||||
php.taskRepo = s.taskRepo
|
||||
r.Post("/setCli", php.SetCli)
|
||||
r.Post("/set_cli", php.SetCli)
|
||||
r.Get("/config", php.GetConfig)
|
||||
r.Post("/config", php.UpdateConfig)
|
||||
r.Get("/fpmConfig", php.GetFPMConfig)
|
||||
r.Post("/fpmConfig", php.UpdateFPMConfig)
|
||||
r.Get("/fpm_config", php.GetFPMConfig)
|
||||
r.Post("/fpm_config", 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("/error_log", php.ErrorLog)
|
||||
r.Get("/slow_log", php.SlowLog)
|
||||
r.Post("/clear_error_log", php.ClearErrorLog)
|
||||
r.Post("/clear_slow_log", php.ClearSlowLog)
|
||||
r.Get("/extensions", php.ExtensionList)
|
||||
r.Post("/extensions", php.InstallExtension)
|
||||
r.Delete("/extensions", php.UninstallExtension)
|
||||
@@ -120,8 +124,36 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
dataKeys := []string{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"}
|
||||
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"}
|
||||
dataKeys := []string{
|
||||
s.t.Get("Application Pool"),
|
||||
s.t.Get("Process Manager"),
|
||||
s.t.Get("Start Time"),
|
||||
s.t.Get("Accepted Connections"),
|
||||
s.t.Get("Listen Queue"),
|
||||
s.t.Get("Max Listen Queue"),
|
||||
s.t.Get("Listen Queue Length"),
|
||||
s.t.Get("Idle Processes"),
|
||||
s.t.Get("Active Processes"),
|
||||
s.t.Get("Total Processes"),
|
||||
s.t.Get("Max Active Processes"),
|
||||
s.t.Get("Max Children Reached"),
|
||||
s.t.Get("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, 0)
|
||||
for i := range dataKeys {
|
||||
@@ -146,7 +178,7 @@ func (s *App) SlowLog(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 {
|
||||
if _, err := shell.Execf("cat /dev/null > %s/server/php/%d/var/log/php-fpm.log", app.Root, s.version); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -155,7 +187,7 @@ func (s *App) ClearErrorLog(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 {
|
||||
if _, err := shell.Execf("cat /dev/null > %s/server/php/%d/var/log/slow.log", app.Root, s.version); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -194,18 +226,18 @@ func (s *App) InstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if !s.checkExtension(req.Slug) {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("extension %s does not exist", req.Slug))
|
||||
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.PathEscape(req.Slug), s.version, req.Slug)
|
||||
cmd := fmt.Sprintf(`curl -sSLm 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 -sSLm 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)
|
||||
task.Name = fmt.Sprintf("安装PHP-%d扩展 %s", s.version, req.Slug)
|
||||
task.Name = s.t.Get("Install PHP-%d %s extension", s.version, req.Slug)
|
||||
task.Status = biz.TaskStatusWaiting
|
||||
task.Shell = cmd
|
||||
task.Log = "/tmp/" + req.Slug + ".log"
|
||||
@@ -225,18 +257,18 @@ func (s *App) UninstallExtension(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if !s.checkExtension(req.Slug) {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "扩展不存在")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("extension %s does not exist", req.Slug))
|
||||
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.PathEscape(req.Slug), s.version, req.Slug)
|
||||
cmd := fmt.Sprintf(`curl -sSLm 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 -sSLm 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)
|
||||
task.Name = fmt.Sprintf("卸载PHP-%d扩展 %s", s.version, req.Slug)
|
||||
task.Name = s.t.Get("Uninstall PHP-%d %s extension", s.version, req.Slug)
|
||||
task.Status = biz.TaskStatusWaiting
|
||||
task.Shell = cmd
|
||||
task.Log = "/tmp/" + req.Slug + ".log"
|
||||
@@ -253,149 +285,206 @@ func (s *App) getExtensions() []Extension {
|
||||
{
|
||||
Name: "fileinfo",
|
||||
Slug: "fileinfo",
|
||||
Description: "Fileinfo 是一个用于识别文件类型的库",
|
||||
Description: s.t.Get("Fileinfo is a library used to identify file types"),
|
||||
},
|
||||
{
|
||||
Name: "OPcache",
|
||||
Slug: "Zend OPcache",
|
||||
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销",
|
||||
},
|
||||
{
|
||||
Name: "Redis",
|
||||
Slug: "redis",
|
||||
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据(需先安装 igbinary 拓展)",
|
||||
},
|
||||
{
|
||||
Name: "Memcached",
|
||||
Slug: "memcached",
|
||||
Description: "Memcached 使用 libmemcached 库连接 Memcached 服务器",
|
||||
},
|
||||
{
|
||||
Name: "ImageMagick",
|
||||
Slug: "imagick",
|
||||
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件",
|
||||
},
|
||||
{
|
||||
Name: "exif",
|
||||
Slug: "exif",
|
||||
Description: "通过 exif 扩展,您可以操作图像元数据",
|
||||
},
|
||||
{
|
||||
Name: "pgsql",
|
||||
Slug: "pgsql",
|
||||
Description: "pgsql 是一个驱动程序,它启用从 PHP 到 PostgreSQL 数据库的访问(需先安装 PostgreSQL 应用)",
|
||||
},
|
||||
{
|
||||
Name: "pdo_pgsql",
|
||||
Slug: "pdo_pgsql",
|
||||
Description: "pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问(需先安装 PostgreSQL 应用)",
|
||||
},
|
||||
{
|
||||
Name: "imap",
|
||||
Slug: "imap",
|
||||
Description: "IMAP 扩展允许 PHP 读取、搜索、删除、下载和管理邮件",
|
||||
},
|
||||
{
|
||||
Name: "zip",
|
||||
Slug: "zip",
|
||||
Description: "Zip 是一个用于处理 ZIP 文件的库",
|
||||
},
|
||||
{
|
||||
Name: "bz2",
|
||||
Slug: "bz2",
|
||||
Description: "Bzip2 是一个用于压缩和解压缩文件的库",
|
||||
},
|
||||
{
|
||||
Name: "readline",
|
||||
Slug: "readline",
|
||||
Description: "Readline 是一个库,它提供了一种用于处理文本的接口",
|
||||
},
|
||||
{
|
||||
Name: "snmp",
|
||||
Slug: "snmp",
|
||||
Description: "SNMP 是一种用于网络管理的协议",
|
||||
},
|
||||
{
|
||||
Name: "ldap",
|
||||
Slug: "ldap",
|
||||
Description: "LDAP 是一种用于访问目录服务的协议",
|
||||
},
|
||||
{
|
||||
Name: "enchant",
|
||||
Slug: "enchant",
|
||||
Description: "Enchant 是一个拼写检查库",
|
||||
},
|
||||
{
|
||||
Name: "pspell",
|
||||
Slug: "pspell",
|
||||
Description: "Pspell 是一个拼写检查库",
|
||||
},
|
||||
{
|
||||
Name: "calendar",
|
||||
Slug: "calendar",
|
||||
Description: "Calendar 是一个用于处理日期的库",
|
||||
},
|
||||
{
|
||||
Name: "gmp",
|
||||
Slug: "gmp",
|
||||
Description: "GMP 是一个用于处理大整数的库",
|
||||
},
|
||||
{
|
||||
Name: "sysvmsg",
|
||||
Slug: "sysvmsg",
|
||||
Description: "Sysvmsg 是一个用于处理 System V 消息队列的库",
|
||||
},
|
||||
{
|
||||
Name: "sysvsem",
|
||||
Slug: "sysvsem",
|
||||
Description: "Sysvsem 是一个用于处理 System V 信号量的库",
|
||||
},
|
||||
{
|
||||
Name: "sysvshm",
|
||||
Slug: "sysvshm",
|
||||
Description: "Sysvshm 是一个用于处理 System V 共享内存的库",
|
||||
},
|
||||
{
|
||||
Name: "xsl",
|
||||
Slug: "xsl",
|
||||
Description: "XSL 是一个用于处理 XML 文档的库",
|
||||
},
|
||||
{
|
||||
Name: "intl",
|
||||
Slug: "intl",
|
||||
Description: "Intl 是一个用于处理国际化和本地化的库",
|
||||
},
|
||||
{
|
||||
Name: "gettext",
|
||||
Slug: "gettext",
|
||||
Description: "Gettext 是一个用于处理多语言的库",
|
||||
Description: s.t.Get("OPcache stores precompiled PHP script bytecode in shared memory to improve PHP performance"),
|
||||
},
|
||||
{
|
||||
Name: "igbinary",
|
||||
Slug: "igbinary",
|
||||
Description: "Igbinary 是一个用于序列化和反序列化数据的库",
|
||||
Description: s.t.Get("Igbinary is a library for serializing and deserializing data"),
|
||||
},
|
||||
{
|
||||
Name: "Redis",
|
||||
Slug: "redis",
|
||||
Description: s.t.Get("PhpRedis connects to and operates on data in Redis databases (requires the igbinary extension installed above)"),
|
||||
},
|
||||
{
|
||||
Name: "Memcached",
|
||||
Slug: "memcached",
|
||||
Description: s.t.Get("Memcached is a driver for connecting to Memcached servers"),
|
||||
},
|
||||
{
|
||||
Name: "ImageMagick",
|
||||
Slug: "imagick",
|
||||
Description: s.t.Get("ImageMagick is free software for creating, editing, and composing images"),
|
||||
},
|
||||
{
|
||||
Name: "exif",
|
||||
Slug: "exif",
|
||||
Description: s.t.Get("Exif is a library for reading and writing image metadata"),
|
||||
},
|
||||
{
|
||||
Name: "pgsql",
|
||||
Slug: "pgsql",
|
||||
Description: s.t.Get("pgsql is a driver for connecting to PostgreSQL (requires PostgreSQL installed)"),
|
||||
},
|
||||
{
|
||||
Name: "pdo_pgsql",
|
||||
Slug: "pdo_pgsql",
|
||||
Description: s.t.Get("pdo_pgsql is a PDO driver for connecting to PostgreSQL (requires PostgreSQL installed)"),
|
||||
},
|
||||
{
|
||||
Name: "sqlsrv",
|
||||
Slug: "sqlsrv",
|
||||
Description: s.t.Get("sqlsrv is a driver for connecting to SQL Server"),
|
||||
},
|
||||
{
|
||||
Name: "pdo_sqlsrv",
|
||||
Slug: "pdo_sqlsrv",
|
||||
Description: s.t.Get("pdo_sqlsrv is a PDO driver for connecting to SQL Server"),
|
||||
},
|
||||
{
|
||||
Name: "imap",
|
||||
Slug: "imap",
|
||||
Description: s.t.Get("IMAP extension allows PHP to read, search, delete, download, and manage emails"),
|
||||
},
|
||||
{
|
||||
Name: "zip",
|
||||
Slug: "zip",
|
||||
Description: s.t.Get("Zip is a library for handling ZIP files"),
|
||||
},
|
||||
{
|
||||
Name: "bz2",
|
||||
Slug: "bz2",
|
||||
Description: s.t.Get("Bzip2 is a library for compressing and decompressing files"),
|
||||
},
|
||||
{
|
||||
Name: "ssh2",
|
||||
Slug: "ssh2",
|
||||
Description: s.t.Get("SSH2 is a library for connecting to SSH servers"),
|
||||
},
|
||||
{
|
||||
Name: "event",
|
||||
Slug: "event",
|
||||
Description: s.t.Get("Event is a library for handling events"),
|
||||
},
|
||||
{
|
||||
Name: "readline",
|
||||
Slug: "readline",
|
||||
Description: s.t.Get("Readline is a library for processing text"),
|
||||
},
|
||||
{
|
||||
Name: "snmp",
|
||||
Slug: "snmp",
|
||||
Description: s.t.Get("SNMP is a protocol for network management"),
|
||||
},
|
||||
{
|
||||
Name: "ldap",
|
||||
Slug: "ldap",
|
||||
Description: s.t.Get("LDAP is a protocol for accessing directory services"),
|
||||
},
|
||||
{
|
||||
Name: "enchant",
|
||||
Slug: "enchant",
|
||||
Description: s.t.Get("Enchant is a spell-checking library"),
|
||||
},
|
||||
{
|
||||
Name: "pspell",
|
||||
Slug: "pspell",
|
||||
Description: s.t.Get("Pspell is a spell-checking library"),
|
||||
},
|
||||
{
|
||||
Name: "calendar",
|
||||
Slug: "calendar",
|
||||
Description: s.t.Get("Calendar is a library for handling dates"),
|
||||
},
|
||||
{
|
||||
Name: "gmp",
|
||||
Slug: "gmp",
|
||||
Description: s.t.Get("GMP is a library for handling large integers"),
|
||||
},
|
||||
{
|
||||
Name: "xlswriter",
|
||||
Slug: "xlswriter",
|
||||
Description: s.t.Get("XLSWriter is a high-performance library for reading and writing Excel files"),
|
||||
},
|
||||
{
|
||||
Name: "xsl",
|
||||
Slug: "xsl",
|
||||
Description: s.t.Get("XSL is a library for processing XML documents"),
|
||||
},
|
||||
{
|
||||
Name: "intl",
|
||||
Slug: "intl",
|
||||
Description: s.t.Get("Intl is a library for handling internationalization and localization"),
|
||||
},
|
||||
{
|
||||
Name: "gettext",
|
||||
Slug: "gettext",
|
||||
Description: s.t.Get("Gettext is a library for handling multilingual support"),
|
||||
},
|
||||
{
|
||||
Name: "grpc",
|
||||
Slug: "grpc",
|
||||
Description: s.t.Get("gRPC is a high-performance, open-source, and general-purpose RPC framework"),
|
||||
},
|
||||
{
|
||||
Name: "protobuf",
|
||||
Slug: "protobuf",
|
||||
Description: s.t.Get("protobuf is a library for serializing and deserializing data"),
|
||||
},
|
||||
{
|
||||
Name: "rdkafka",
|
||||
Slug: "rdkafka",
|
||||
Description: s.t.Get("rdkafka is a library for connecting to Apache Kafka"),
|
||||
},
|
||||
{
|
||||
Name: "xhprof",
|
||||
Slug: "xhprof",
|
||||
Description: s.t.Get("xhprof is a library for performance profiling"),
|
||||
},
|
||||
{
|
||||
Name: "xdebug",
|
||||
Slug: "xdebug",
|
||||
Description: s.t.Get("xdebug is a library for debugging and profiling PHP code"),
|
||||
},
|
||||
{
|
||||
Name: "yaml",
|
||||
Slug: "yaml",
|
||||
Description: s.t.Get("yaml is a library for handling YAML"),
|
||||
},
|
||||
{
|
||||
Name: "zstd",
|
||||
Slug: "zstd",
|
||||
Description: s.t.Get("zstd is a library for compressing and decompressing files"),
|
||||
},
|
||||
{
|
||||
Name: "sysvmsg",
|
||||
Slug: "sysvmsg",
|
||||
Description: s.t.Get("Sysvmsg is a library for handling System V message queues"),
|
||||
},
|
||||
{
|
||||
Name: "sysvsem",
|
||||
Slug: "sysvsem",
|
||||
Description: s.t.Get("Sysvsem is a library for handling System V semaphores"),
|
||||
},
|
||||
{
|
||||
Name: "sysvshm",
|
||||
Slug: "sysvshm",
|
||||
Description: s.t.Get("Sysvshm is a library for handling System V shared memory"),
|
||||
},
|
||||
{
|
||||
Name: "ionCube",
|
||||
Slug: "ionCube Loader",
|
||||
Description: s.t.Get("ionCube is a professional-grade PHP encryption and decryption tool (must be installed after OPcache)"),
|
||||
},
|
||||
{
|
||||
Name: "Swoole",
|
||||
Slug: "swoole",
|
||||
Description: s.t.Get("Swoole is a PHP extension for building high-performance asynchronous concurrent servers"),
|
||||
},
|
||||
}
|
||||
|
||||
// ionCube Swoole 不支持 PHP 8.4
|
||||
if cast.ToUint(s.version) < 84 {
|
||||
extensions = append(extensions, Extension{
|
||||
Name: "ionCube",
|
||||
Slug: "ionCube Loader",
|
||||
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 扩展",
|
||||
Description: s.t.Get("Swow is a PHP extension for building high-performance asynchronous concurrent servers"),
|
||||
})
|
||||
}
|
||||
// PHP 8.4 移除了 pspell 和 imap 并且不再建议使用
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package php74
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnborg/panel/internal/apps/php"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(t, task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(74)(r)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package php80
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnborg/panel/internal/apps/php"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(t, task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(80)(r)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package php81
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnborg/panel/internal/apps/php"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(t, task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(81)(r)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package php82
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnborg/panel/internal/apps/php"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(t, task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(82)(r)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package php83
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnborg/panel/internal/apps/php"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(t, task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(83)(r)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package php84
|
||||
|
||||
import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnborg/panel/internal/apps/php"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
php *php.App
|
||||
}
|
||||
|
||||
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
|
||||
return &App{
|
||||
php: php.NewApp(t, task),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
s.php.Route(84)(r)
|
||||
}
|
||||
@@ -3,25 +3,31 @@ package phpmyadmin
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/libtnb/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/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/firewall"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
type App struct {
|
||||
t *gotext.Locale
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
func NewApp(t *gotext.Locale) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
@@ -32,9 +38,9 @@ func (s *App) Route(r chi.Router) {
|
||||
}
|
||||
|
||||
func (s *App) Info(w http.ResponseWriter, r *http.Request) {
|
||||
files, err := io.ReadDir(fmt.Sprintf("%s/server/phpmyadmin", app.Root))
|
||||
files, err := os.ReadDir(fmt.Sprintf("%s/server/phpmyadmin", app.Root))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("phpMyAdmin directory not found"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -45,7 +51,7 @@ func (s *App) Info(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
if len(phpmyadmin) == 0 {
|
||||
service.Error(w, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("phpMyAdmin directory not found"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -56,7 +62,7 @@ func (s *App) Info(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
match := regexp.MustCompile(`listen\s+(\d+);`).FindStringSubmatch(conf)
|
||||
if len(match) == 0 {
|
||||
service.Error(w, http.StatusInternalServerError, "找不到 phpMyAdmin 端口")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("phpMyAdmin port not found"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,7 +86,7 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
conf = regexp.MustCompile(`listen\s+(\d+);`).ReplaceAllString(conf, "listen "+cast.ToString(req.Port)+";")
|
||||
if err = io.Write(fmt.Sprintf("%s/server/vhost/phpmyadmin.conf", app.Root), conf, 0644); err != nil {
|
||||
service.ErrorSystem(w)
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -99,7 +105,7 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err = systemctl.Reload("nginx"); err != nil {
|
||||
_, err = shell.Execf("nginx -t")
|
||||
service.Error(w, http.StatusInternalServerError, "重载OpenResty失败:%v", err)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -130,7 +136,7 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err = systemctl.Reload("nginx"); err != nil {
|
||||
_, err = shell.Execf("nginx -t")
|
||||
service.Error(w, http.StatusInternalServerError, "重载OpenResty失败:%v", err)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
@@ -17,10 +17,10 @@ func NewApp() *App {
|
||||
}
|
||||
|
||||
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)
|
||||
r.Get("/registry_config", s.GetRegistryConfig)
|
||||
r.Post("/registry_config", s.UpdateRegistryConfig)
|
||||
r.Get("/storage_config", s.GetStorageConfig)
|
||||
r.Post("/storage_config", s.UpdateStorageConfig)
|
||||
}
|
||||
|
||||
func (s *App) GetRegistryConfig(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -6,29 +6,34 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"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/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
type App struct {
|
||||
t *gotext.Locale
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
func NewApp(t *gotext.Locale) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
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("/user_config", s.GetUserConfig)
|
||||
r.Post("/user_config", s.UpdateUserConfig)
|
||||
r.Get("/load", s.Load)
|
||||
r.Get("/log", s.Log)
|
||||
r.Post("/clearLog", s.ClearLog)
|
||||
r.Post("/clear_log", s.ClearLog)
|
||||
}
|
||||
|
||||
// GetConfig 获取配置
|
||||
@@ -36,7 +41,7 @@ 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 {
|
||||
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL配置失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -51,13 +56,13 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
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配置失败")
|
||||
if err = io.Write(fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root), req.Config, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
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, s.t.Get("failed to reload PostgreSQL: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,7 +74,7 @@ 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 {
|
||||
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL配置失败")
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,13 +89,13 @@ func (s *App) UpdateUserConfig(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
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配置失败")
|
||||
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, "%v", err)
|
||||
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, s.t.Get("failed to reload PostgreSQL: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -107,36 +112,36 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
start, err := shell.Execf(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL启动时间失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL start time: %v", err))
|
||||
return
|
||||
}
|
||||
pid, err := shell.Execf(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL进程PID失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL backend pid: %v", err))
|
||||
return
|
||||
}
|
||||
process, err := shell.Execf(`ps aux | grep postgres | grep -v grep | wc -l`)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL进程数失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL process: %v", err))
|
||||
return
|
||||
}
|
||||
connections, err := shell.Execf(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL连接数失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL connections: %v", err))
|
||||
return
|
||||
}
|
||||
storage, err := shell.Execf(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL空间占用失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL database size: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
data := []types.NV{
|
||||
{Name: "启动时间", Value: start},
|
||||
{Name: "进程 PID", Value: pid},
|
||||
{Name: "进程数", Value: process},
|
||||
{Name: "总连接数", Value: connections},
|
||||
{Name: "空间占用", Value: storage},
|
||||
{Name: s.t.Get("Start Time"), Value: start},
|
||||
{Name: s.t.Get("Process PID"), Value: pid},
|
||||
{Name: s.t.Get("Process Count"), Value: process},
|
||||
{Name: s.t.Get("Total Connections"), Value: connections},
|
||||
{Name: s.t.Get("Storage Usage"), Value: storage},
|
||||
}
|
||||
|
||||
service.Success(w, data)
|
||||
|
||||
@@ -6,21 +6,26 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/libtnb/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/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/firewall"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
type App struct {
|
||||
t *gotext.Locale
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
func NewApp(t *gotext.Locale) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
@@ -76,18 +81,10 @@ func (s *App) Create(w http.ResponseWriter, r *http.Request) {
|
||||
req.Path = "/" + req.Path
|
||||
}
|
||||
if !io.Exists(req.Path) {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "目录不存在")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("directory %s does not exist", req.Path))
|
||||
return
|
||||
}
|
||||
|
||||
if err = io.Chmod(req.Path, 0755); err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "修改目录权限失败")
|
||||
return
|
||||
}
|
||||
if err = io.Chown(req.Path, "www", "www"); err != nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "修改目录权限失败")
|
||||
return
|
||||
}
|
||||
if _, err = shell.Execf(`yes '%s' | pure-pw useradd '%s' -u www -g www -d '%s'`, req.Password, req.Username, req.Path); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
@@ -144,7 +141,7 @@ func (s *App) ChangePassword(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端口失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get port: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+44
-25
@@ -3,22 +3,28 @@ package redis
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"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/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
type App struct {
|
||||
t *gotext.Locale
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
func NewApp(t *gotext.Locale) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
@@ -30,7 +36,7 @@ func (s *App) Route(r chi.Router) {
|
||||
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := systemctl.Status("redis")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 Redis 状态失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get redis status: %v", err))
|
||||
return
|
||||
}
|
||||
if !status {
|
||||
@@ -38,9 +44,22 @@ func (s *App) 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, "获取 Redis 负载失败")
|
||||
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, s.t.Get("failed to get redis info: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -55,19 +74,19 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
data := []types.NV{
|
||||
{Name: "TCP 端口", Value: dataRaw["tcp_port"]},
|
||||
{Name: "已运行天数", Value: dataRaw["uptime_in_days"]},
|
||||
{Name: "连接的客户端数", Value: dataRaw["connected_clients"]},
|
||||
{Name: "已分配的内存总量", Value: dataRaw["used_memory_human"]},
|
||||
{Name: "占用内存总量", Value: dataRaw["used_memory_rss_human"]},
|
||||
{Name: "占用内存峰值", Value: dataRaw["used_memory_peak_human"]},
|
||||
{Name: "内存碎片比率", Value: dataRaw["mem_fragmentation_ratio"]},
|
||||
{Name: "运行以来连接过的客户端的总数", Value: dataRaw["total_connections_received"]},
|
||||
{Name: "运行以来执行过的命令的总数", Value: dataRaw["total_commands_processed"]},
|
||||
{Name: "每秒执行的命令数", Value: dataRaw["instantaneous_ops_per_sec"]},
|
||||
{Name: "查找数据库键成功次数", Value: dataRaw["keyspace_hits"]},
|
||||
{Name: "查找数据库键失败次数", Value: dataRaw["keyspace_misses"]},
|
||||
{Name: "最近一次 fork() 操作耗费的毫秒数", Value: dataRaw["latest_fork_usec"]},
|
||||
{Name: s.t.Get("TCP Port"), Value: dataRaw["tcp_port"]},
|
||||
{Name: s.t.Get("Uptime in Days"), Value: dataRaw["uptime_in_days"]},
|
||||
{Name: s.t.Get("Connected Clients"), Value: dataRaw["connected_clients"]},
|
||||
{Name: s.t.Get("Total Allocated Memory"), Value: dataRaw["used_memory_human"]},
|
||||
{Name: s.t.Get("Total Memory Usage"), Value: dataRaw["used_memory_rss_human"]},
|
||||
{Name: s.t.Get("Peak Memory Usage"), Value: dataRaw["used_memory_peak_human"]},
|
||||
{Name: s.t.Get("Memory Fragmentation Ratio"), Value: dataRaw["mem_fragmentation_ratio"]},
|
||||
{Name: s.t.Get("Total Connections Received"), Value: dataRaw["total_connections_received"]},
|
||||
{Name: s.t.Get("Total Commands Processed"), Value: dataRaw["total_commands_processed"]},
|
||||
{Name: s.t.Get("Commands Per Second"), Value: dataRaw["instantaneous_ops_per_sec"]},
|
||||
{Name: s.t.Get("Keyspace Hits"), Value: dataRaw["keyspace_hits"]},
|
||||
{Name: s.t.Get("Keyspace Misses"), Value: dataRaw["keyspace_misses"]},
|
||||
{Name: s.t.Get("Latest Fork Time (ms)"), Value: dataRaw["latest_fork_usec"]},
|
||||
}
|
||||
|
||||
service.Success(w, data)
|
||||
|
||||
+20
-15
@@ -7,19 +7,24 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/go-rat/utils/str"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/libtnb/chix"
|
||||
"github.com/libtnb/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/systemctl"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
type App struct {
|
||||
t *gotext.Locale
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
func NewApp(t *gotext.Locale) *App {
|
||||
return &App{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
@@ -74,7 +79,7 @@ func (s *App) List(w http.ResponseWriter, r *http.Request) {
|
||||
currentModule.AuthUser = value
|
||||
currentModule.Secret, err = shell.Execf(`grep -E '^%s:.*$' /etc/rsyncd.secrets | awk -F ':' '{print $2}'`, currentModule.AuthUser)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取模块%s的密钥失败", currentModule.AuthUser)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get the secret key for module %s", currentModule.AuthUser))
|
||||
return
|
||||
}
|
||||
case "hosts allow":
|
||||
@@ -109,7 +114,7 @@ func (s *App) Create(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if strings.Contains(config, "["+req.Name+"]") {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "模块%s已存在", req.Name)
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("module %s already exists", req.Name))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -154,12 +159,12 @@ func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if !strings.Contains(config, "["+req.Name+"]") {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "模块%s不存在", req.Name)
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("module %s does not exist", req.Name))
|
||||
return
|
||||
}
|
||||
|
||||
module := str.Cut(config, "# "+req.Name+"-START", "# "+req.Name+"-END")
|
||||
config = strings.Replace(config, "\n# "+req.Name+"-START"+module+"# "+req.Name+"-END", "", -1)
|
||||
config = strings.ReplaceAll(config, "\n# "+req.Name+"-START"+module+"# "+req.Name+"-END", "")
|
||||
|
||||
match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module)
|
||||
if len(match) == 2 {
|
||||
@@ -196,7 +201,7 @@ func (s *App) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if !strings.Contains(config, "["+req.Name+"]") {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "模块%s不存在", req.Name)
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("module %s does not exist", req.Name))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -211,7 +216,7 @@ secrets file = /etc/rsyncd.secrets
|
||||
# ` + req.Name + `-END`
|
||||
|
||||
module := str.Cut(config, "# "+req.Name+"-START", "# "+req.Name+"-END")
|
||||
config = strings.Replace(config, "# "+req.Name+"-START"+module+"# "+req.Name+"-END", newConf, -1)
|
||||
config = strings.ReplaceAll(config, "# "+req.Name+"-START"+module+"# "+req.Name+"-END", newConf)
|
||||
|
||||
match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module)
|
||||
if len(match) == 2 {
|
||||
|
||||
+91
-87
@@ -1,28 +1,29 @@
|
||||
package s3fs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/libtnb/chix"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/service"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
settingRepo biz.SettingRepo
|
||||
t *gotext.Locale
|
||||
}
|
||||
|
||||
func NewApp(setting biz.SettingRepo) *App {
|
||||
func NewApp(t *gotext.Locale) *App {
|
||||
return &App{
|
||||
settingRepo: setting,
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,19 +35,13 @@ func (s *App) Route(r chi.Router) {
|
||||
|
||||
// List 所有 S3fs 挂载
|
||||
func (s *App) List(w http.ResponseWriter, r *http.Request) {
|
||||
var s3fsList []Mount
|
||||
list, err := s.settingRepo.Get("s3fs", "[]")
|
||||
list, err := s.mounts()
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get s3fs list: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(list), &s3fsList); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
|
||||
paged, total := service.Paginate(r, s3fsList)
|
||||
paged, total := service.Paginate(r, list)
|
||||
|
||||
service.Success(w, chix.M{
|
||||
"total": total,
|
||||
@@ -64,36 +59,31 @@ func (s *App) 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, s.t.Get("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, s.t.Get("failed to create mount path: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
if !io.Empty(req.Path) {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "挂载目录必须为空")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("mount path is not empty"))
|
||||
return
|
||||
}
|
||||
|
||||
var s3fsList []Mount
|
||||
list, err := s.settingRepo.Get("s3fs", "[]")
|
||||
list, err := s.mounts()
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal([]byte(list), &s3fsList); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get s3fs list: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, s := range s3fsList {
|
||||
if s.Path == req.Path {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "路径已存在")
|
||||
for _, item := range list {
|
||||
if item.Path == req.Path {
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("mount path already exists"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -101,38 +91,21 @@ func (s *App) 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, s.t.Get("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, "%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, "挂载失败,请检查配置是否正确")
|
||||
return
|
||||
}
|
||||
|
||||
s3fsList = append(s3fsList, Mount{
|
||||
ID: id,
|
||||
Path: req.Path,
|
||||
Bucket: req.Bucket,
|
||||
Url: req.URL,
|
||||
})
|
||||
encoded, err := json.Marshal(s3fsList)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "添加 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
|
||||
if err = s.settingRepo.Set("s3fs", string(encoded)); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "添加 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("mount failed: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -147,43 +120,38 @@ func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var s3fsList []Mount
|
||||
list, err := s.settingRepo.Get("s3fs", "[]")
|
||||
list, err := s.mounts()
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal([]byte(list), &s3fsList); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 S3fs 挂载失败")
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get s3fs list: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
var mount Mount
|
||||
for _, item := range s3fsList {
|
||||
for _, item := range list {
|
||||
if item.ID == req.ID {
|
||||
mount = item
|
||||
break
|
||||
}
|
||||
}
|
||||
if mount.ID == 0 {
|
||||
service.Error(w, http.StatusUnprocessableEntity, "挂载不存在")
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("mount not found"))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`fusermount -u '%s'`, mount.Path); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if _, err = shell.Execf(`umount '%s'`, mount.Path); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
_, _ = shell.Execf(`fusermount -uz '%s'`, mount.Path)
|
||||
_, err2 := shell.Execf(`umount -lf '%s'`, mount.Path)
|
||||
// 卸载之后再检查下是否还有挂载
|
||||
if _, err = shell.Execf(`df -h | grep '%s'`, mount.Path); err == nil {
|
||||
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("failed to unmount: %v", err2))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`sed -i 's@^s3fs#%s\s%s.*$@@g' /etc/fstab`, mount.Bucket, mount.Path); err != nil {
|
||||
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, "%v", err)
|
||||
return
|
||||
}
|
||||
if err = io.Remove("/etc/passwd-s3fs-" + cast.ToString(mount.ID)); err != nil {
|
||||
@@ -191,21 +159,57 @@ func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var newS3fsList []Mount
|
||||
for _, item := range s3fsList {
|
||||
if item.ID != mount.ID {
|
||||
newS3fsList = append(newS3fsList, item)
|
||||
}
|
||||
}
|
||||
encoded, err := json.Marshal(newS3fsList)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "删除 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
if err = s.settingRepo.Set("s3fs", string(encoded)); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "删除 S3fs 挂载失败")
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
func (s *App) mounts() ([]Mount, error) {
|
||||
re := regexp.MustCompile(`^s3fs#(.*?)\s+(.*?)\s+fuse.*?url=(.*?),passwd_file=/etc/passwd-s3fs-(.*?)\s+`)
|
||||
fstab, err := os.ReadFile("/etc/fstab")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lines := strings.Split(string(fstab), "\n")
|
||||
|
||||
var mounts []Mount
|
||||
|
||||
ids, err := shell.Exec("find /etc -maxdepth 1 -name 'passwd-s3fs-*'")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, id := range strings.Split(ids, "\n") {
|
||||
if id == "" {
|
||||
continue
|
||||
}
|
||||
id = strings.TrimPrefix(id, "/etc/passwd-s3fs-")
|
||||
id = strings.TrimSuffix(id, "\n")
|
||||
id = strings.TrimSpace(id)
|
||||
if id == "" {
|
||||
continue
|
||||
}
|
||||
mount := Mount{
|
||||
ID: cast.ToInt64(id),
|
||||
}
|
||||
for _, line := range lines {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, id) {
|
||||
matches := re.FindStringSubmatch(line)
|
||||
if len(matches) == 5 {
|
||||
mount.Bucket = matches[1]
|
||||
mount.Path = matches[2]
|
||||
mount.URL = matches[3]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mount.ID == 0 || mount.Path == "" || mount.Bucket == "" || mount.URL == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
mounts = append(mounts, mount)
|
||||
}
|
||||
|
||||
return mounts, nil
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ type Mount struct {
|
||||
ID int64 `json:"id"`
|
||||
Path string `json:"path"`
|
||||
Bucket string `json:"bucket"`
|
||||
Url string `json:"url"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
@@ -5,21 +5,23 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-rat/chix"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/libtnb/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/tnborg/panel/internal/service"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/os"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
t *gotext.Locale
|
||||
name string
|
||||
}
|
||||
|
||||
func NewApp() *App {
|
||||
func NewApp(t *gotext.Locale) *App {
|
||||
var name string
|
||||
if os.IsRHEL() {
|
||||
name = "supervisord"
|
||||
@@ -28,13 +30,14 @@ func NewApp() *App {
|
||||
}
|
||||
|
||||
return &App{
|
||||
t: t,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *App) Route(r chi.Router) {
|
||||
r.Get("/service", s.Service)
|
||||
r.Post("/clearLog", s.ClearLog)
|
||||
r.Post("/clear_log", s.ClearLog)
|
||||
r.Get("/config", s.GetConfig)
|
||||
r.Post("/config", s.UpdateConfig)
|
||||
r.Get("/processes", s.Processes)
|
||||
@@ -42,7 +45,7 @@ func (s *App) Route(r chi.Router) {
|
||||
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.Post("/processes/{process}/clear_log", s.ClearProcessLog)
|
||||
r.Get("/processes/{process}", s.ProcessConfig)
|
||||
r.Post("/processes/{process}", s.UpdateProcessConfig)
|
||||
r.Delete("/processes/{process}", s.DeleteProcess)
|
||||
@@ -56,7 +59,7 @@ func (s *App) Service(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// ClearLog 清空日志
|
||||
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := shell.Execf(`echo "" > /var/log/supervisor/supervisord.log`); err != nil {
|
||||
if _, err := shell.Execf(`cat /dev/null > /var/log/supervisor/supervisord.log`); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -102,7 +105,7 @@ func (s *App) 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.t.Get("failed to restart %s: %v", s.name, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -156,8 +159,8 @@ func (s *App) StartProcess(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`supervisorctl start %s`, req.Process); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
if out, err := shell.Execf(`supervisorctl start %s`, req.Process); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v %s", err, out)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -172,8 +175,8 @@ func (s *App) StopProcess(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`supervisorctl stop %s`, req.Process); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
if out, err := shell.Execf(`supervisorctl stop %s`, req.Process); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v %s", err, out)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -188,8 +191,8 @@ func (s *App) RestartProcess(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`supervisorctl restart %s`, req.Process); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
if out, err := shell.Execf(`supervisorctl restart %s`, req.Process); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v %s", err, out)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -212,7 +215,7 @@ func (s *App) ProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "无法从进程 %s 的配置文件中获取日志路径", req.Process)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get log path for process %s: %v", req.Process, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -235,11 +238,11 @@ func (s *App) ClearProcessLog(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "无法从进程 %s 的配置文件中获取日志路径", req.Process)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get log path for process %s: %v", req.Process, err))
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`echo "" > '%s'`, logPath); err != nil {
|
||||
if _, err = shell.Execf(`cat /dev/null > '%s'`, logPath); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -343,8 +346,8 @@ func (s *App) DeleteProcess(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`supervisorctl stop '%s'`, req.Process); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
if out, err := shell.Execf(`supervisorctl stop '%s'`, req.Process); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v %s", err, out)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -352,7 +355,7 @@ func (s *App) DeleteProcess(w http.ResponseWriter, r *http.Request) {
|
||||
if os.IsRHEL() {
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisord.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, req.Process)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "无法从进程 %s 的配置文件中获取日志路径", req.Process)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get log path for process %s: %v", req.Process, err))
|
||||
return
|
||||
}
|
||||
if err = io.Remove(`/etc/supervisord.d/` + req.Process + `.conf`); err != nil {
|
||||
@@ -362,7 +365,7 @@ func (s *App) DeleteProcess(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
logPath, err = shell.Execf(`cat '/etc/supervisor/conf.d/%s.conf' | grep stdout_logfile= | awk -F "=" '{print $2}'`, req.Process)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "无法从进程 %s 的配置文件中获取日志路径", req.Process)
|
||||
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get log path for process %s: %v", req.Process, err))
|
||||
return
|
||||
}
|
||||
if err = io.Remove(`/etc/supervisor/conf.d/` + req.Process + `.conf`); err != nil {
|
||||
|
||||
@@ -1,337 +0,0 @@
|
||||
package toolbox
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"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/ntp"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type App struct{}
|
||||
|
||||
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 *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)
|
||||
return
|
||||
}
|
||||
match := regexp.MustCompile(`nameserver\s+(\S+)`).FindAllStringSubmatch(raw, -1)
|
||||
if len(match) == 0 {
|
||||
service.Error(w, http.StatusInternalServerError, "找不到 DNS 信息")
|
||||
return
|
||||
}
|
||||
|
||||
var dns []string
|
||||
for _, m := range match {
|
||||
dns = append(dns, m[1])
|
||||
}
|
||||
|
||||
service.Success(w, dns)
|
||||
}
|
||||
|
||||
// UpdateDNS 设置 DNS 信息
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
var dns string
|
||||
dns += "nameserver " + req.DNS1 + "\n"
|
||||
dns += "nameserver " + req.DNS2 + "\n"
|
||||
|
||||
if err := io.Write("/etc/resolv.conf", dns, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入 DNS 信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// GetSWAP 获取 SWAP 信息
|
||||
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")) {
|
||||
file, err := io.FileInfo(filepath.Join(app.Root, "swap"))
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 SWAP 信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
size = file.Size() / 1024 / 1024
|
||||
total = tools.FormatBytes(float64(file.Size()))
|
||||
} else {
|
||||
size = 0
|
||||
total = "0.00 B"
|
||||
}
|
||||
|
||||
raw, err := shell.Execf("free | grep Swap")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取 SWAP 信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
match := regexp.MustCompile(`Swap:\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
|
||||
if len(match) >= 4 {
|
||||
used = tools.FormatBytes(cast.ToFloat64(match[2]) * 1024)
|
||||
free = tools.FormatBytes(cast.ToFloat64(match[3]) * 1024)
|
||||
}
|
||||
|
||||
service.Success(w, chix.M{
|
||||
"total": total,
|
||||
"size": size,
|
||||
"used": used,
|
||||
"free": free,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateSWAP 设置 SWAP 信息
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
if io.Exists(filepath.Join(app.Root, "swap")) {
|
||||
if _, err = shell.Execf("swapoff '%s'", filepath.Join(app.Root, "swap")); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if _, err = shell.Execf("rm -f '%s'", filepath.Join(app.Root, "swap")); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if _, err = shell.Execf(`sed -i "\|^%s|d" /etc/fstab`, filepath.Join(app.Root, "swap")); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if req.Size > 1 {
|
||||
var free string
|
||||
free, err = shell.Execf("df -k %s | awk '{print $4}' | tail -n 1", app.Root)
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取硬盘空间失败")
|
||||
return
|
||||
}
|
||||
if cast.ToInt64(free)*1024 < req.Size*1024*1024 {
|
||||
service.Error(w, http.StatusInternalServerError, "硬盘空间不足,当前剩余%s", tools.FormatBytes(cast.ToFloat64(free)))
|
||||
return
|
||||
}
|
||||
|
||||
btrfsCheck, _ := shell.Execf("df -T %s | awk '{print $2}' | tail -n 1", app.Root)
|
||||
if strings.Contains(btrfsCheck, "btrfs") {
|
||||
if _, err = shell.Execf("btrfs filesystem mkswapfile --size %dM --uuid clear %s", req.Size, filepath.Join(app.Root, "swap")); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if _, err = shell.Execf("dd if=/dev/zero of=%s bs=1M count=%d", filepath.Join(app.Root, "swap"), req.Size); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if _, err = shell.Execf("mkswap -f '%s'", filepath.Join(app.Root, "swap")); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if err = io.Chmod(filepath.Join(app.Root, "swap"), 0600); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "设置 SWAP 权限失败")
|
||||
return
|
||||
}
|
||||
}
|
||||
if _, err = shell.Execf("swapon '%s'", filepath.Join(app.Root, "swap")); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
if _, err = shell.Execf("echo '%s swap swap defaults 0 0' >> /etc/fstab", filepath.Join(app.Root, "swap")); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// GetTimezone 获取时区
|
||||
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, "获取时区信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
match := regexp.MustCompile(`zone:\s+(\S+)`).FindStringSubmatch(raw)
|
||||
if len(match) == 0 {
|
||||
service.Error(w, http.StatusInternalServerError, "找不到时区信息")
|
||||
return
|
||||
}
|
||||
|
||||
zonesRaw, err := shell.Execf("timedatectl list-timezones")
|
||||
if err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "获取时区列表失败")
|
||||
return
|
||||
}
|
||||
zones := strings.Split(zonesRaw, "\n")
|
||||
|
||||
var zonesList []types.LV
|
||||
for _, z := range zones {
|
||||
zonesList = append(zonesList, types.LV{
|
||||
Label: z,
|
||||
Value: z,
|
||||
})
|
||||
}
|
||||
|
||||
service.Success(w, chix.M{
|
||||
"timezone": match[1],
|
||||
"timezones": zonesList,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateTimezone 设置时区
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = shell.Execf("timedatectl set-timezone '%s'", req.Timezone); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
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 *App) GetHosts(w http.ResponseWriter, r *http.Request) {
|
||||
hosts, _ := io.Read("/etc/hosts")
|
||||
service.Success(w, hosts)
|
||||
}
|
||||
|
||||
// UpdateHosts 设置 hosts 信息
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
if err = io.Write("/etc/hosts", req.Hosts, 0644); err != nil {
|
||||
service.Error(w, http.StatusInternalServerError, "写入 hosts 信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
|
||||
// UpdateRootPassword 设置 root 密码
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
service.Success(w, nil)
|
||||
}
|
||||
+6
-6
@@ -3,16 +3,16 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/api"
|
||||
"github.com/tnborg/panel/pkg/api"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Slug string `gorm:"not null;unique" json:"slug"`
|
||||
Channel string `gorm:"not null" json:"channel"`
|
||||
Version string `gorm:"not null" json:"version"`
|
||||
Show bool `gorm:"not null" json:"show"`
|
||||
ShowOrder int `gorm:"not null" json:"show_order"`
|
||||
Slug string `gorm:"not null;default:'';unique" json:"slug"`
|
||||
Channel string `gorm:"not null;default:''" json:"channel"`
|
||||
Version string `gorm:"not null;default:''" json:"version"`
|
||||
Show bool `gorm:"not null;default:false" json:"show"`
|
||||
ShowOrder int `gorm:"not null;default:0" json:"show_order"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package biz
|
||||
|
||||
import "github.com/TheTNB/panel/pkg/types"
|
||||
import "github.com/tnborg/panel/pkg/types"
|
||||
|
||||
type BackupType string
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const (
|
||||
|
||||
type Cache struct {
|
||||
Key CacheKey `gorm:"primaryKey" json:"key"`
|
||||
Value string `gorm:"not null" json:"value"`
|
||||
Value string `gorm:"not null;default:''" json:"value"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
+13
-12
@@ -3,22 +3,23 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/acme"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/pkg/acme"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Cert struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
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, 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"` // 私钥内容
|
||||
AccountID uint `gorm:"not null;default:0" json:"account_id"` // 关联的 ACME 账户 ID
|
||||
WebsiteID uint `gorm:"not null;default:0" json:"website_id"` // 关联的网站 ID
|
||||
DNSID uint `gorm:"not null;default:0" json:"dns_id"` // 关联的 DNS ID
|
||||
Type string `gorm:"not null;default:''" json:"type"` // 证书类型 (P256, P384, 2048, 3072, 4096)
|
||||
Domains []string `gorm:"not null;default:'[]';serializer:json" json:"domains"`
|
||||
AutoRenew bool `gorm:"not null;default:false" json:"auto_renew"` // 自动续签
|
||||
CertURL string `gorm:"not null;default:''" json:"cert_url"` // 证书 URL (续签时使用)
|
||||
Cert string `gorm:"not null;default:''" json:"cert"` // 证书内容
|
||||
Key string `gorm:"not null;default:''" json:"key"` // 私钥内容
|
||||
Script string `gorm:"not null;default:''" json:"script"` // 部署脚本
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
|
||||
@@ -3,17 +3,17 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type CertAccount struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Email string `gorm:"not null" json:"email"`
|
||||
CA string `gorm:"not null" json:"ca"` // CA 提供商 (letsencrypt, zerossl, sslcom, google, buypass)
|
||||
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"` // 密钥类型 (P256, P384, 2048, 3072, 4096)
|
||||
Email string `gorm:"not null;default:''" json:"email"`
|
||||
CA string `gorm:"not null;default:'letsencrypt'" json:"ca"` // CA 提供商 (letsencrypt, zerossl, sslcom, google, buypass)
|
||||
Kid string `gorm:"not null;default:''" json:"kid"`
|
||||
HmacEncoded string `gorm:"not null;default:''" json:"hmac_encoded"`
|
||||
PrivateKey string `gorm:"not null;default:''" json:"private_key"`
|
||||
KeyType string `gorm:"not null;default:'P256'" json:"key_type"` // 密钥类型 (P256, P384, 2048, 3072, 4096)
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/acme"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/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 提供商 (tencent, aliyun, cloudflare)
|
||||
Name string `gorm:"not null;default:''" json:"name"` // 备注名称
|
||||
Type acme.DnsType `gorm:"not null;default:'aliyun'" json:"type"` // DNS 提供商
|
||||
Data acme.DNSParam `gorm:"not null;serializer:json" json:"dns_param"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerRepo interface {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package biz
|
||||
|
||||
import "github.com/tnborg/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,8 +1,8 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerImageRepo interface {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerNetworkRepo interface {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type ContainerVolumeRepo interface {
|
||||
|
||||
@@ -3,17 +3,17 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type Cron struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"not null;unique" json:"name"`
|
||||
Status bool `gorm:"not null" json:"status"`
|
||||
Type string `gorm:"not null" json:"type"`
|
||||
Time string `gorm:"not null" json:"time"`
|
||||
Shell string `gorm:"not null" json:"shell"`
|
||||
Log string `gorm:"not null" json:"log"`
|
||||
Name string `gorm:"not null;default:'';unique" json:"name"`
|
||||
Status bool `gorm:"not null;default:false" json:"status"`
|
||||
Type string `gorm:"not null;default:''" json:"type"`
|
||||
Time string `gorm:"not null;default:''" json:"time"`
|
||||
Shell string `gorm:"not null;default:''" json:"shell"`
|
||||
Log string `gorm:"not null;default:''" json:"log"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type DatabaseType string
|
||||
|
||||
@@ -3,11 +3,11 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-rat/utils/crypt"
|
||||
"github.com/libtnb/utils/crypt"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type DatabaseServerStatus string
|
||||
@@ -19,14 +19,14 @@ const (
|
||||
|
||||
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"`
|
||||
Name string `gorm:"not null;default:'';unique" json:"name"`
|
||||
Type DatabaseType `gorm:"not null;default:''" json:"type"`
|
||||
Host string `gorm:"not null;default:''" json:"host"`
|
||||
Port uint `gorm:"not null;default:0" json:"port"`
|
||||
Username string `gorm:"not null;default:''" json:"username"`
|
||||
Password string `gorm:"not null;default:''" json:"password"`
|
||||
Status DatabaseServerStatus `gorm:"-:all" json:"status"`
|
||||
Remark string `gorm:"not null" json:"remark"`
|
||||
Remark string `gorm:"not null;default:''" json:"remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-rat/utils/crypt"
|
||||
"github.com/libtnb/utils/crypt"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type DatabaseUserStatus string
|
||||
@@ -19,13 +19,13 @@ const (
|
||||
|
||||
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"`
|
||||
ServerID uint `gorm:"not null;default:0" json:"server_id"`
|
||||
Username string `gorm:"not null;default:''" json:"username"`
|
||||
Password string `gorm:"not null;default:''" json:"password"`
|
||||
Host string `gorm:"not null;default:''" json:"host"` // 仅 mysql
|
||||
Status DatabaseUserStatus `gorm:"-:all" json:"status"` // 仅显示
|
||||
Privileges []string `gorm:"-:all" json:"privileges"` // 仅显示
|
||||
Remark string `gorm:"not null;default:''" json:"remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Monitor struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Info types.CurrentInfo `gorm:"not null;serializer:json" json:"info"`
|
||||
Info types.CurrentInfo `gorm:"not null;default:'{}';serializer:json" json:"info"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
+11
-6
@@ -1,10 +1,9 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
)
|
||||
|
||||
type SettingKey string
|
||||
@@ -12,18 +11,20 @@ type SettingKey string
|
||||
const (
|
||||
SettingKeyName SettingKey = "name"
|
||||
SettingKeyVersion SettingKey = "version"
|
||||
SettingKeyChannel SettingKey = "channel"
|
||||
SettingKeyMonitor SettingKey = "monitor"
|
||||
SettingKeyMonitorDays SettingKey = "monitor_days"
|
||||
SettingKeyBackupPath SettingKey = "backup_path"
|
||||
SettingKeyWebsitePath SettingKey = "website_path"
|
||||
SettingKeyMySQLRootPassword SettingKey = "mysql_root_password"
|
||||
SettingKeyOfflineMode SettingKey = "offline_mode"
|
||||
SettingKeyAutoUpdate SettingKey = "auto_update"
|
||||
)
|
||||
|
||||
type Setting struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Key SettingKey `gorm:"not null;unique" json:"key"`
|
||||
Value string `gorm:"not null" json:"value"`
|
||||
Key SettingKey `gorm:"not null;default:'';unique" json:"key"`
|
||||
Value string `gorm:"not null;default:''" json:"value"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
@@ -31,8 +32,12 @@ type Setting struct {
|
||||
type SettingRepo interface {
|
||||
Get(key SettingKey, defaultValue ...string) (string, error)
|
||||
GetBool(key SettingKey, defaultValue ...bool) (bool, error)
|
||||
GetInt(key SettingKey, defaultValue ...int) (int, error)
|
||||
GetSlice(key SettingKey, defaultValue ...[]string) ([]string, error)
|
||||
Set(key SettingKey, value string) error
|
||||
SetSlice(key SettingKey, value []string) error
|
||||
Delete(key SettingKey) error
|
||||
GetPanelSetting(ctx context.Context) (*request.PanelSetting, error)
|
||||
UpdatePanelSetting(ctx context.Context, setting *request.PanelSetting) (bool, error)
|
||||
GetPanel() (*request.SettingPanel, error)
|
||||
UpdatePanel(req *request.SettingPanel) (bool, error)
|
||||
UpdateCert(req *request.SettingCert) error
|
||||
}
|
||||
|
||||
+9
-10
@@ -3,21 +3,21 @@ package biz
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-rat/utils/crypt"
|
||||
"github.com/libtnb/utils/crypt"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/ssh"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/pkg/ssh"
|
||||
)
|
||||
|
||||
type SSH struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"not null" json:"name"`
|
||||
Host string `gorm:"not null" json:"host"`
|
||||
Port uint `gorm:"not null" json:"port"`
|
||||
Config ssh.ClientConfig `gorm:"not null;serializer:json" json:"config"`
|
||||
Remark string `gorm:"not null" json:"remark"`
|
||||
Name string `gorm:"not null;default:''" json:"name"`
|
||||
Host string `gorm:"not null;default:''" json:"host"`
|
||||
Port uint `gorm:"not null;default:0" json:"port"`
|
||||
Config ssh.ClientConfig `gorm:"not null;default:'{}';serializer:json" json:"config"`
|
||||
Remark string `gorm:"not null;default:''" json:"remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
@@ -38,7 +38,6 @@ func (r *SSH) BeforeSave(tx *gorm.DB) error {
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *SSH) AfterFind(tx *gorm.DB) error {
|
||||
|
||||
@@ -13,10 +13,10 @@ const (
|
||||
|
||||
type Task struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"not null;index" json:"name"`
|
||||
Name string `gorm:"not null;default:'';index" json:"name"`
|
||||
Status TaskStatus `gorm:"not null;default:'waiting'" json:"status"`
|
||||
Shell string `gorm:"not null" json:"-"`
|
||||
Log string `gorm:"not null" json:"log"`
|
||||
Shell string `gorm:"not null;default:''" json:"-"`
|
||||
Log string `gorm:"not null;default:''" json:"log"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
+17
-6
@@ -1,6 +1,7 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"image"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -8,17 +9,27 @@ import (
|
||||
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Username string `gorm:"not null;unique" json:"username"`
|
||||
Password string `gorm:"not null" json:"password"`
|
||||
Email string `gorm:"not null" json:"email"`
|
||||
Username string `gorm:"not null;default:'';unique" json:"username"`
|
||||
Password string `gorm:"not null;default:''" json:"password"`
|
||||
Email string `gorm:"not null;default:''" json:"email"`
|
||||
TwoFA string `gorm:"not null;default:''" json:"two_fa"` // 2FA secret,为空表示未开启
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
|
||||
|
||||
Tokens []*UserToken `gorm:"foreignKey:UserID" json:"-"`
|
||||
}
|
||||
|
||||
type UserRepo interface {
|
||||
Create(username, password string) (*User, error)
|
||||
CheckPassword(username, password string) (*User, error)
|
||||
List(page, limit uint) ([]*User, int64, error)
|
||||
Get(id uint) (*User, error)
|
||||
Save(user *User) error
|
||||
Create(username, password, email string) (*User, error)
|
||||
UpdateUsername(id uint, username string) error
|
||||
UpdatePassword(id uint, password string) error
|
||||
UpdateEmail(id uint, email string) error
|
||||
Delete(id uint) error
|
||||
CheckPassword(username, password string) (*User, error)
|
||||
IsTwoFA(username string) (bool, error)
|
||||
GenerateTwoFA(id uint) (image.Image, string, string, error)
|
||||
UpdateTwoFA(id uint, code, secret string) error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/libtnb/utils/crypt"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
)
|
||||
|
||||
type UserToken struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
UserID uint `gorm:"index" json:"user_id"`
|
||||
Token string `gorm:"not null;default:'';unique" json:"-"`
|
||||
IPs []string `gorm:"not null;default:'[]';serializer:json" json:"ips"`
|
||||
ExpiredAt time.Time `json:"expired_at"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (r *UserToken) BeforeSave(tx *gorm.DB) error {
|
||||
crypter, err := crypt.NewXChacha20Poly1305([]byte(app.Key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Token, err = crypter.Encrypt([]byte(r.Token))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *UserToken) AfterFind(tx *gorm.DB) error {
|
||||
crypter, err := crypt.NewXChacha20Poly1305([]byte(app.Key))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
token, err := crypter.Decrypt(r.Token)
|
||||
if err == nil {
|
||||
r.Token = string(token)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type UserTokenRepo interface {
|
||||
List(userID, page, limit uint) ([]*UserToken, int64, error)
|
||||
Create(userID uint, ips []string, expired time.Time) (*UserToken, error)
|
||||
Get(id uint) (*UserToken, error)
|
||||
Delete(id uint) error
|
||||
Update(id uint, ips []string, expired time.Time) (*UserToken, error)
|
||||
ValidateReq(req *http.Request) (uint, error)
|
||||
}
|
||||
+10
-6
@@ -4,20 +4,23 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type Website struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"not null;unique" json:"name"`
|
||||
Name string `gorm:"not null;default:'';unique" json:"name"`
|
||||
Type string `gorm:"not null;default:'php'" json:"type"`
|
||||
Status bool `gorm:"not null;default:true" json:"status"`
|
||||
Path string `gorm:"not null" json:"path"`
|
||||
Https bool `gorm:"not null" json:"https"`
|
||||
Remark string `gorm:"not null" json:"remark"`
|
||||
Path string `gorm:"not null;default:''" json:"path"`
|
||||
Https bool `gorm:"not null;default:false" json:"https"`
|
||||
Remark string `gorm:"not null;default:''" json:"remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
CertExpire string `gorm:"-:all" json:"cert_expire"` // 仅显示
|
||||
|
||||
Cert *Cert `gorm:"foreignKey:WebsiteID" json:"cert"`
|
||||
}
|
||||
|
||||
@@ -35,5 +38,6 @@ type WebsiteRepo interface {
|
||||
UpdateRemark(id uint, remark string) error
|
||||
ResetConfig(id uint) error
|
||||
UpdateStatus(id uint, status bool) error
|
||||
UpdateCert(req *request.WebsiteUpdateCert) error
|
||||
ObtainCert(ctx context.Context, id uint) error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/tnborg/panel/internal/apps/codeserver"
|
||||
"github.com/tnborg/panel/internal/apps/docker"
|
||||
"github.com/tnborg/panel/internal/apps/fail2ban"
|
||||
"github.com/tnborg/panel/internal/apps/frp"
|
||||
"github.com/tnborg/panel/internal/apps/gitea"
|
||||
"github.com/tnborg/panel/internal/apps/memcached"
|
||||
"github.com/tnborg/panel/internal/apps/minio"
|
||||
"github.com/tnborg/panel/internal/apps/mysql"
|
||||
"github.com/tnborg/panel/internal/apps/nginx"
|
||||
"github.com/tnborg/panel/internal/apps/php74"
|
||||
"github.com/tnborg/panel/internal/apps/php80"
|
||||
"github.com/tnborg/panel/internal/apps/php81"
|
||||
"github.com/tnborg/panel/internal/apps/php82"
|
||||
"github.com/tnborg/panel/internal/apps/php83"
|
||||
"github.com/tnborg/panel/internal/apps/php84"
|
||||
"github.com/tnborg/panel/internal/apps/phpmyadmin"
|
||||
"github.com/tnborg/panel/internal/apps/podman"
|
||||
"github.com/tnborg/panel/internal/apps/postgresql"
|
||||
"github.com/tnborg/panel/internal/apps/pureftpd"
|
||||
"github.com/tnborg/panel/internal/apps/redis"
|
||||
"github.com/tnborg/panel/internal/apps/rsync"
|
||||
"github.com/tnborg/panel/internal/apps/s3fs"
|
||||
"github.com/tnborg/panel/internal/apps/supervisor"
|
||||
"github.com/tnborg/panel/pkg/apploader"
|
||||
)
|
||||
|
||||
func NewLoader(
|
||||
codeserver *codeserver.App,
|
||||
docker *docker.App,
|
||||
fail2ban *fail2ban.App,
|
||||
frp *frp.App,
|
||||
gitea *gitea.App,
|
||||
memcached *memcached.App,
|
||||
minio *minio.App,
|
||||
mysql *mysql.App,
|
||||
nginx *nginx.App,
|
||||
php74 *php74.App,
|
||||
php80 *php80.App,
|
||||
php81 *php81.App,
|
||||
php82 *php82.App,
|
||||
php83 *php83.App,
|
||||
php84 *php84.App,
|
||||
phpmyadmin *phpmyadmin.App,
|
||||
podman *podman.App,
|
||||
postgresql *postgresql.App,
|
||||
pureftpd *pureftpd.App,
|
||||
redis *redis.App,
|
||||
rsync *rsync.App,
|
||||
s3fs *s3fs.App,
|
||||
supervisor *supervisor.App,
|
||||
) *apploader.Loader {
|
||||
loader := new(apploader.Loader)
|
||||
loader.Add(codeserver, docker, fail2ban, frp, gitea, memcached, minio, mysql, nginx, php74, php80, php81, php82, php83, php84, phpmyadmin, podman, postgresql, pureftpd, redis, rsync, s3fs, supervisor)
|
||||
return loader
|
||||
}
|
||||
@@ -3,4 +3,4 @@ package bootstrap
|
||||
import "github.com/google/wire"
|
||||
|
||||
// ProviderSet is bootstrap providers.
|
||||
var ProviderSet = wire.NewSet(NewConf, NewLog, NewCli, NewValidator, NewRouter, NewHttp, NewDB, NewMigrate, NewSession, NewCron, NewQueue)
|
||||
var ProviderSet = wire.NewSet(NewConf, NewT, NewLog, NewCli, NewValidator, NewRouter, NewHttp, NewDB, NewMigrate, NewLoader, NewSession, NewCron, NewQueue)
|
||||
|
||||
+26
-25
@@ -3,39 +3,40 @@ package bootstrap
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/urfave/cli/v3"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/route"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/route"
|
||||
)
|
||||
|
||||
func NewCli(cmd *route.Cli) *cli.Command {
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "NAME", "名称")
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "USAGE", "用法")
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "VERSION", "版本")
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "DESCRIPTION", "描述")
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "AUTHOR", "作者")
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "COMMANDS", "命令")
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "GLOBAL OPTIONS", "全局选项")
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "COPYRIGHT", "版权")
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "NAME", "名称")
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "USAGE", "用法")
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "CATEGORY", "分类")
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "DESCRIPTION", "描述")
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "OPTIONS", "选项")
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "NAME", "名称")
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "USAGE", "用法")
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "DESCRIPTION", "描述")
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "COMMANDS", "命令")
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "OPTIONS", "选项")
|
||||
func NewCli(t *gotext.Locale, cmd *route.Cli) *cli.Command {
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "NAME", t.Get("NAME"))
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "USAGE", t.Get("USAGE"))
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "VERSION", t.Get("VERSION"))
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "DESCRIPTION", t.Get("DESCRIPTION"))
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "AUTHOR", t.Get("AUTHOR"))
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "COMMANDS", t.Get("COMMANDS"))
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "GLOBAL OPTIONS", t.Get("GLOBAL OPTIONS"))
|
||||
cli.RootCommandHelpTemplate = strings.ReplaceAll(cli.RootCommandHelpTemplate, "COPYRIGHT", t.Get("COPYRIGHT"))
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "NAME", t.Get("NAME"))
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "USAGE", t.Get("USAGE"))
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "CATEGORY", t.Get("CATEGORY"))
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "DESCRIPTION", t.Get("DESCRIPTION"))
|
||||
cli.CommandHelpTemplate = strings.ReplaceAll(cli.CommandHelpTemplate, "OPTIONS", t.Get("OPTIONS"))
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "NAME", t.Get("NAME"))
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "USAGE", t.Get("USAGE"))
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "DESCRIPTION", t.Get("USAGE"))
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "COMMANDS", t.Get("COMMANDS"))
|
||||
cli.SubcommandHelpTemplate = strings.ReplaceAll(cli.SubcommandHelpTemplate, "OPTIONS", t.Get("OPTIONS"))
|
||||
|
||||
cli.RootCommandHelpTemplate += "\n官网:https://panel.haozi.net"
|
||||
cli.RootCommandHelpTemplate += "\n论坛:https://bbs.haozi.net"
|
||||
cli.RootCommandHelpTemplate += "\nQ群:12370907\n"
|
||||
cli.RootCommandHelpTemplate += "\n" + t.Get("Website:https://panel.haozi.net")
|
||||
cli.RootCommandHelpTemplate += "\n" + t.Get("Forum:https://bbs.haozi.net")
|
||||
cli.RootCommandHelpTemplate += "\n" + t.Get("QQ Group:12370907") + "\n"
|
||||
|
||||
return &cli.Command{
|
||||
Name: "panel-cli",
|
||||
Usage: "耗子面板命令行工具",
|
||||
Usage: t.Get("Rat Panel CLI Tool"),
|
||||
Version: app.Version,
|
||||
Commands: cmd.Commands(),
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
"github.com/knadh/koanf/v2"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
)
|
||||
|
||||
func NewConf() (*koanf.Koanf, error) {
|
||||
@@ -30,7 +30,7 @@ func NewConf() (*koanf.Koanf, error) {
|
||||
func initGlobal(conf *koanf.Koanf) {
|
||||
app.Key = conf.MustString("app.key")
|
||||
if len(app.Key) != 32 {
|
||||
log.Fatalf("app key must be 32 characters")
|
||||
log.Fatalf("panel app key must be 32 characters")
|
||||
}
|
||||
|
||||
app.Root = conf.MustString("app.root")
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/TheTNB/panel/internal/job"
|
||||
pkgcron "github.com/TheTNB/panel/pkg/cron"
|
||||
"github.com/tnborg/panel/internal/job"
|
||||
pkgcron "github.com/tnborg/panel/pkg/cron"
|
||||
)
|
||||
|
||||
func NewCron(conf *koanf.Koanf, log *slog.Logger, jobs *job.Jobs) (*cron.Cron, error) {
|
||||
|
||||
@@ -4,20 +4,21 @@ import (
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"github.com/knadh/koanf/v2"
|
||||
slogGorm "github.com/orandin/slog-gorm"
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
"github.com/ncruces/go-sqlite3/gormlite"
|
||||
sloggorm "github.com/orandin/slog-gorm"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/migration"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/migration"
|
||||
)
|
||||
|
||||
func NewDB(conf *koanf.Koanf, log *slog.Logger) (*gorm.DB, error) {
|
||||
// You can use any other database, like MySQL or PostgreSQL.
|
||||
return gorm.Open(sqlite.Open(filepath.Join(app.Root, "panel/storage/app.db")), &gorm.Config{
|
||||
Logger: slogGorm.New(slogGorm.WithHandler(log.Handler())),
|
||||
return gorm.Open(gormlite.Open("file:"+filepath.Join(app.Root, "panel/storage/app.db?_txlock=immediate")), &gorm.Config{
|
||||
Logger: sloggorm.New(sloggorm.WithHandler(log.Handler())),
|
||||
SkipDefaultTransaction: true,
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
})
|
||||
|
||||
@@ -8,16 +8,17 @@ import (
|
||||
"github.com/bddjr/hlfhr"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/middleware"
|
||||
"github.com/TheTNB/panel/internal/route"
|
||||
"github.com/tnborg/panel/internal/http/middleware"
|
||||
"github.com/tnborg/panel/internal/route"
|
||||
)
|
||||
|
||||
func NewRouter(middlewares *middleware.Middlewares, http *route.Http, ws *route.Ws) (*chi.Mux, error) {
|
||||
func NewRouter(t *gotext.Locale, middlewares *middleware.Middlewares, http *route.Http, ws *route.Ws) (*chi.Mux, error) {
|
||||
r := chi.NewRouter()
|
||||
|
||||
// add middleware
|
||||
r.Use(middlewares.Globals(r)...)
|
||||
r.Use(middlewares.Globals(t, r)...)
|
||||
// add http route
|
||||
http.Register(r)
|
||||
// add ws route
|
||||
@@ -38,7 +39,7 @@ func NewHttp(conf *koanf.Koanf, r *chi.Mux) (*hlfhr.Server, error) {
|
||||
|
||||
if conf.Bool("http.tls") {
|
||||
srv.TLSConfig = &tls.Config{
|
||||
MinVersion: tls.VersionTLS13,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/knadh/koanf/v2"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
)
|
||||
|
||||
func NewLog(conf *koanf.Koanf) *slog.Logger {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/TheTNB/panel/pkg/queue"
|
||||
"github.com/tnborg/panel/pkg/queue"
|
||||
)
|
||||
|
||||
func NewQueue() *queue.Queue {
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/go-rat/gormstore"
|
||||
"github.com/go-rat/sessions"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/libtnb/gormstore"
|
||||
"github.com/libtnb/sessions"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func NewSession(conf *koanf.Koanf, db *gorm.DB) (*sessions.Manager, error) {
|
||||
// initialize session manager
|
||||
lifetime := conf.Int("session.lifetime")
|
||||
// TODO: will remove this fallback in v3
|
||||
if lifetime == 0 {
|
||||
lifetime = 120
|
||||
}
|
||||
manager, err := sessions.NewManager(&sessions.ManagerOptions{
|
||||
Key: conf.MustString("app.key"),
|
||||
Lifetime: 120,
|
||||
GcInterval: 30,
|
||||
Lifetime: lifetime,
|
||||
GcInterval: 5,
|
||||
DisableDefaultDriver: true,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
|
||||
"github.com/tnborg/panel/pkg/embed"
|
||||
)
|
||||
|
||||
func NewT(conf *koanf.Koanf) (*gotext.Locale, error) {
|
||||
locale := conf.String("app.locale")
|
||||
l := gotext.NewLocaleFSWithPath(locale, embed.LocalesFS, "locales")
|
||||
l.AddDomain("backend")
|
||||
|
||||
return l, nil
|
||||
}
|
||||
@@ -2,19 +2,27 @@ package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/gookit/validate"
|
||||
"github.com/gookit/validate/locales/ruru"
|
||||
"github.com/gookit/validate/locales/zhcn"
|
||||
"github.com/gookit/validate/locales/zhtw"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/internal/http/rule"
|
||||
"github.com/tnborg/panel/internal/http/rule"
|
||||
)
|
||||
|
||||
// NewValidator just for register global rules
|
||||
func NewValidator(db *gorm.DB) *validate.Validation {
|
||||
zhcn.RegisterGlobal()
|
||||
func NewValidator(conf *koanf.Koanf, db *gorm.DB) *validate.Validation {
|
||||
if conf.String("app.locale") == "zh_CN" {
|
||||
zhcn.RegisterGlobal()
|
||||
} else if conf.String("app.locale") == "zh_TW" {
|
||||
zhtw.RegisterGlobal()
|
||||
} else if conf.String("app.locale") == "ru_RU" {
|
||||
ruru.RegisterGlobal()
|
||||
}
|
||||
validate.Config(func(opt *validate.GlobalOption) {
|
||||
opt.StopOnError = false
|
||||
opt.SkipOnEmpty = true
|
||||
opt.FieldTag = "form"
|
||||
})
|
||||
|
||||
// register global rules
|
||||
|
||||
+35
-29
@@ -7,25 +7,31 @@ import (
|
||||
"slices"
|
||||
|
||||
"github.com/expr-lang/expr"
|
||||
"github.com/go-rat/utils/collect"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/libtnb/utils/collect"
|
||||
"github.com/spf13/cast"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/pkg/api"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
"github.com/tnborg/panel/pkg/api"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
)
|
||||
|
||||
type appRepo struct {
|
||||
t *gotext.Locale
|
||||
conf *koanf.Koanf
|
||||
db *gorm.DB
|
||||
cache biz.CacheRepo
|
||||
task biz.TaskRepo
|
||||
}
|
||||
|
||||
func NewAppRepo(db *gorm.DB, cache biz.CacheRepo, task biz.TaskRepo) biz.AppRepo {
|
||||
func NewAppRepo(t *gotext.Locale, conf *koanf.Koanf, db *gorm.DB, cache biz.CacheRepo, task biz.TaskRepo) biz.AppRepo {
|
||||
return &appRepo{
|
||||
t: t,
|
||||
conf: conf,
|
||||
db: db,
|
||||
cache: cache,
|
||||
task: task,
|
||||
@@ -50,7 +56,7 @@ func (r *appRepo) Get(slug string) (*api.App, error) {
|
||||
return item, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("应用不存在")
|
||||
return nil, errors.New(r.t.Get("app %s not found", slug))
|
||||
}
|
||||
|
||||
func (r *appRepo) UpdateExist(slug string) bool {
|
||||
@@ -153,7 +159,7 @@ func (r *appRepo) Install(channel, slug string) error {
|
||||
}
|
||||
|
||||
if installed, _ := r.IsInstalled(slug); installed {
|
||||
return errors.New("应用已安装")
|
||||
return errors.New(r.t.Get("app %s already installed", slug))
|
||||
}
|
||||
|
||||
shellUrl, shellChannel, shellVersion := "", "", ""
|
||||
@@ -163,8 +169,8 @@ func (r *appRepo) Install(channel, slug string) error {
|
||||
continue
|
||||
}
|
||||
if ch.Slug == channel {
|
||||
if vs.GreaterThan(panel) {
|
||||
return fmt.Errorf("应用 %s 需要面板版本 %s,当前版本 %s", item.Name, ch.Panel, app.Version)
|
||||
if vs.GreaterThan(panel) && !r.conf.Bool("app.debug") {
|
||||
return errors.New(r.t.Get("app %s requires panel version %s, current version %s", item.Name, ch.Panel, app.Version))
|
||||
}
|
||||
shellUrl = ch.Install
|
||||
shellChannel = ch.Slug
|
||||
@@ -173,7 +179,7 @@ func (r *appRepo) Install(channel, slug string) error {
|
||||
}
|
||||
}
|
||||
if shellUrl == "" {
|
||||
return fmt.Errorf("应用 %s 不支持当前面板版本", item.Name)
|
||||
return errors.New(r.t.Get("app %s not support current panel version", item.Name))
|
||||
}
|
||||
|
||||
if err = r.preCheck(item); err != nil {
|
||||
@@ -181,13 +187,13 @@ func (r *appRepo) Install(channel, slug string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
return shell.ExecfWithOutput(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s"`, shellUrl, shellChannel, shellVersion)
|
||||
return shell.ExecfWithOutput(`curl -sSLm 10 --retry 3 "%s" | bash -s -- "%s" "%s"`, shellUrl, shellChannel, shellVersion)
|
||||
}
|
||||
|
||||
task := new(biz.Task)
|
||||
task.Name = "安装应用 " + item.Name
|
||||
task.Name = r.t.Get("Install app %s", item.Name)
|
||||
task.Status = biz.TaskStatusWaiting
|
||||
task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
|
||||
task.Shell = fmt.Sprintf(`curl -sSLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
|
||||
return r.task.Push(task)
|
||||
@@ -204,7 +210,7 @@ func (r *appRepo) UnInstall(slug string) error {
|
||||
}
|
||||
|
||||
if installed, _ := r.IsInstalled(slug); !installed {
|
||||
return errors.New("应用未安装")
|
||||
return errors.New(r.t.Get("app %s not installed", item.Name))
|
||||
}
|
||||
installed, err := r.GetInstalled(slug)
|
||||
if err != nil {
|
||||
@@ -218,8 +224,8 @@ func (r *appRepo) UnInstall(slug string) error {
|
||||
continue
|
||||
}
|
||||
if ch.Slug == installed.Channel {
|
||||
if vs.GreaterThan(panel) {
|
||||
return fmt.Errorf("应用 %s 需要面板版本 %s,当前版本 %s", item.Name, ch.Panel, app.Version)
|
||||
if vs.GreaterThan(panel) && !r.conf.Bool("app.debug") {
|
||||
return errors.New(r.t.Get("app %s requires panel version %s, current version %s", item.Name, ch.Panel, app.Version))
|
||||
}
|
||||
shellUrl = ch.Uninstall
|
||||
shellChannel = ch.Slug
|
||||
@@ -228,7 +234,7 @@ func (r *appRepo) UnInstall(slug string) error {
|
||||
}
|
||||
}
|
||||
if shellUrl == "" {
|
||||
return fmt.Errorf("无法获取应用 %s 的卸载脚本", item.Name)
|
||||
return errors.New(r.t.Get("failed to get uninstall script for app %s", item.Name))
|
||||
}
|
||||
|
||||
if err = r.preCheck(item); err != nil {
|
||||
@@ -236,13 +242,13 @@ func (r *appRepo) UnInstall(slug string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
return shell.ExecfWithOutput(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s"`, shellUrl, shellChannel, shellVersion)
|
||||
return shell.ExecfWithOutput(`curl -sSLm 10 --retry 3 "%s" | bash -s -- "%s" "%s"`, shellUrl, shellChannel, shellVersion)
|
||||
}
|
||||
|
||||
task := new(biz.Task)
|
||||
task.Name = "卸载应用 " + item.Name
|
||||
task.Name = r.t.Get("Uninstall app %s", item.Name)
|
||||
task.Status = biz.TaskStatusWaiting
|
||||
task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
|
||||
task.Shell = fmt.Sprintf(`curl -sSLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
|
||||
return r.task.Push(task)
|
||||
@@ -259,7 +265,7 @@ func (r *appRepo) Update(slug string) error {
|
||||
}
|
||||
|
||||
if installed, _ := r.IsInstalled(slug); !installed {
|
||||
return errors.New("应用未安装")
|
||||
return errors.New(r.t.Get("app %s not installed", item.Name))
|
||||
}
|
||||
installed, err := r.GetInstalled(slug)
|
||||
if err != nil {
|
||||
@@ -273,8 +279,8 @@ func (r *appRepo) Update(slug string) error {
|
||||
continue
|
||||
}
|
||||
if ch.Slug == installed.Channel {
|
||||
if vs.GreaterThan(panel) {
|
||||
return fmt.Errorf("应用 %s 需要面板版本 %s,当前版本 %s", item.Name, ch.Panel, app.Version)
|
||||
if vs.GreaterThan(panel) && !r.conf.Bool("app.debug") {
|
||||
return errors.New(r.t.Get("app %s requires panel version %s, current version %s", item.Name, ch.Panel, app.Version))
|
||||
}
|
||||
shellUrl = ch.Update
|
||||
shellChannel = ch.Slug
|
||||
@@ -283,7 +289,7 @@ func (r *appRepo) Update(slug string) error {
|
||||
}
|
||||
}
|
||||
if shellUrl == "" {
|
||||
return fmt.Errorf("应用 %s 不支持当前面板版本", item.Name)
|
||||
return errors.New(r.t.Get("app %s not support current panel version", item.Name))
|
||||
}
|
||||
|
||||
if err = r.preCheck(item); err != nil {
|
||||
@@ -291,13 +297,13 @@ func (r *appRepo) Update(slug string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
return shell.ExecfWithOutput(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s"`, shellUrl, shellChannel, shellVersion)
|
||||
return shell.ExecfWithOutput(`curl -sSLm 10 --retry 3 "%s" | bash -s -- "%s" "%s"`, shellUrl, shellChannel, shellVersion)
|
||||
}
|
||||
|
||||
task := new(biz.Task)
|
||||
task.Name = "更新应用 " + item.Name
|
||||
task.Name = r.t.Get("Update app %s", item.Name)
|
||||
task.Status = biz.TaskStatusWaiting
|
||||
task.Shell = fmt.Sprintf(`curl -fsLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
|
||||
task.Shell = fmt.Sprintf(`curl -sSLm 10 --retry 3 "%s" | bash -s -- "%s" "%s" >> /tmp/%s.log 2>&1`, shellUrl, shellChannel, shellVersion, item.Slug)
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
|
||||
return r.task.Push(task)
|
||||
@@ -341,7 +347,7 @@ func (r *appRepo) preCheck(app *api.App) error {
|
||||
|
||||
result := cast.ToString(output)
|
||||
if result != "ok" {
|
||||
return fmt.Errorf("应用 %s %s", app.Name, result)
|
||||
return errors.New(r.t.Get("App %s %s", app.Name, result))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+149
-129
@@ -9,26 +9,29 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/pkg/db"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/tools"
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
"github.com/tnborg/panel/pkg/db"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/tools"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type backupRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
setting biz.SettingRepo
|
||||
website biz.WebsiteRepo
|
||||
}
|
||||
|
||||
func NewBackupRepo(db *gorm.DB, setting biz.SettingRepo, website biz.WebsiteRepo) biz.BackupRepo {
|
||||
func NewBackupRepo(t *gotext.Locale, db *gorm.DB, setting biz.SettingRepo, website biz.WebsiteRepo) biz.BackupRepo {
|
||||
return &backupRepo{
|
||||
t: t,
|
||||
db: db,
|
||||
setting: setting,
|
||||
website: website,
|
||||
@@ -42,7 +45,7 @@ func (r *backupRepo) List(typ biz.BackupType) ([]*types.BackupFile, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
files, err := io.ReadDir(path)
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -89,7 +92,7 @@ func (r *backupRepo) Create(typ biz.BackupType, target string, path ...string) e
|
||||
|
||||
}
|
||||
|
||||
return errors.New("未知备份类型")
|
||||
return errors.New(r.t.Get("unknown backup type"))
|
||||
}
|
||||
|
||||
// Delete 删除备份
|
||||
@@ -125,7 +128,7 @@ func (r *backupRepo) Restore(typ biz.BackupType, backup, target string) error {
|
||||
return r.restorePostgres(backup, target)
|
||||
}
|
||||
|
||||
return errors.New("未知备份类型")
|
||||
return errors.New(r.t.Get("unknown backup type"))
|
||||
}
|
||||
|
||||
// CutoffLog 切割日志
|
||||
@@ -133,7 +136,7 @@ func (r *backupRepo) Restore(typ biz.BackupType, backup, target string) error {
|
||||
// target 待切割日志文件绝对路径
|
||||
func (r *backupRepo) CutoffLog(path, target string) error {
|
||||
if !io.Exists(target) {
|
||||
return errors.New("日志文件不存在")
|
||||
return errors.New(r.t.Get("log file %s not exists", target))
|
||||
}
|
||||
|
||||
to := filepath.Join(path, fmt.Sprintf("%s_%s.zip", time.Now().Format("20060102150405"), filepath.Base(target)))
|
||||
@@ -141,7 +144,12 @@ func (r *backupRepo) CutoffLog(path, target string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return io.Remove(target)
|
||||
// 原文件不能直接删除,直接删的话仍会占用空间直到重启相关的应用
|
||||
if _, err := shell.Execf("cat /dev/null > '%s'", target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearExpired 清理过期备份
|
||||
@@ -149,7 +157,7 @@ func (r *backupRepo) CutoffLog(path, target string) error {
|
||||
// prefix 目标文件前缀
|
||||
// save 保存份数
|
||||
func (r *backupRepo) ClearExpired(path, prefix string, save int) error {
|
||||
files, err := io.ReadDir(path)
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -184,14 +192,10 @@ func (r *backupRepo) ClearExpired(path, prefix string, save int) error {
|
||||
for _, file := range toDelete {
|
||||
filePath := filepath.Join(path, file.Name())
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-清理过期文件:%s\n", filePath)
|
||||
fmt.Println(r.t.Get("|-Cleaning expired file: %s", filePath))
|
||||
}
|
||||
if err = os.Remove(filePath); err != nil {
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-清理失败:%v\n", err)
|
||||
} else {
|
||||
return fmt.Errorf("清理失败:%v", err)
|
||||
}
|
||||
return errors.New(r.t.Get("Cleanup failed: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,12 +209,12 @@ func (r *backupRepo) GetPath(typ biz.BackupType) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
if !slices.Contains([]biz.BackupType{biz.BackupTypePath, biz.BackupTypeWebsite, biz.BackupTypeMySQL, biz.BackupTypePostgres, biz.BackupTypeRedis, biz.BackupTypePanel}, typ) {
|
||||
return "", errors.New("未知备份类型")
|
||||
return "", errors.New(r.t.Get("unknown backup type"))
|
||||
}
|
||||
|
||||
backupPath = filepath.Join(backupPath, string(typ))
|
||||
if !io.Exists(backupPath) {
|
||||
if err = io.Mkdir(backupPath, 0644); err != nil {
|
||||
if err = os.MkdirAll(backupPath, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
@@ -236,8 +240,8 @@ func (r *backupRepo) createWebsite(to string, name string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-备份耗时:%s\n", time.Since(start).String())
|
||||
fmt.Printf("|-已备份至文件:%s\n", filepath.Base(backup))
|
||||
fmt.Println(r.t.Get("|-Backup time: %s", time.Since(start).String()))
|
||||
fmt.Println(r.t.Get("|-Backed up to file: %s", filepath.Base(backup)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -252,9 +256,11 @@ func (r *backupRepo) createMySQL(to string, name string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer mysql.Close()
|
||||
defer func(mysql *db.MySQL) {
|
||||
_ = mysql.Close()
|
||||
}(mysql)
|
||||
if exist, _ := mysql.DatabaseExists(name); !exist {
|
||||
return fmt.Errorf("数据库不存在:%s", name)
|
||||
return errors.New(r.t.Get("database does not exist: %s", name))
|
||||
}
|
||||
size, err := mysql.DatabaseSize(name)
|
||||
if err != nil {
|
||||
@@ -284,8 +290,8 @@ func (r *backupRepo) createMySQL(to string, name string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-备份耗时:%s\n", time.Since(start).String())
|
||||
fmt.Printf("|-已备份至文件:%s\n", filepath.Base(backup+".zip"))
|
||||
fmt.Println(r.t.Get("|-Backup time: %s", time.Since(start).String()))
|
||||
fmt.Println(r.t.Get("|-Backed up to file: %s", filepath.Base(backup+".zip")))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -296,9 +302,11 @@ func (r *backupRepo) createPostgres(to string, name string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer postgres.Close()
|
||||
defer func(postgres *db.Postgres) {
|
||||
_ = postgres.Close()
|
||||
}(postgres)
|
||||
if exist, _ := postgres.DatabaseExist(name); !exist {
|
||||
return fmt.Errorf("数据库不存在:%s", name)
|
||||
return errors.New(r.t.Get("database does not exist: %s", name))
|
||||
}
|
||||
size, err := postgres.DatabaseSize(name)
|
||||
if err != nil {
|
||||
@@ -322,8 +330,8 @@ func (r *backupRepo) createPostgres(to string, name string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-备份耗时:%s\n", time.Since(start).String())
|
||||
fmt.Printf("|-已备份至文件:%s\n", filepath.Base(backup+".zip"))
|
||||
fmt.Println(r.t.Get("|-Backup time: %s", time.Since(start).String()))
|
||||
fmt.Println(r.t.Get("|-Backed up to file: %s", filepath.Base(backup+".zip")))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -338,7 +346,7 @@ func (r *backupRepo) createPanel(to string) error {
|
||||
|
||||
start := time.Now()
|
||||
|
||||
temp, err := io.TempDir("panel-backup")
|
||||
temp, err := os.MkdirTemp("", "panel-backup")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -362,8 +370,8 @@ func (r *backupRepo) createPanel(to string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-备份耗时:%s\n", time.Since(start).String())
|
||||
fmt.Printf("|-已备份至文件:%s\n", filepath.Base(backup))
|
||||
fmt.Println(r.t.Get("|-Backup time: %s", time.Since(start).String()))
|
||||
fmt.Println(r.t.Get("|-Backed up to file: %s", filepath.Base(backup)))
|
||||
}
|
||||
|
||||
return io.Remove(temp)
|
||||
@@ -372,7 +380,7 @@ func (r *backupRepo) createPanel(to string) error {
|
||||
// restoreWebsite 恢复网站备份
|
||||
func (r *backupRepo) restoreWebsite(backup, target string) error {
|
||||
if !io.Exists(backup) {
|
||||
return errors.New("备份文件不存在")
|
||||
return errors.New(r.t.Get("backup file %s not exists", backup))
|
||||
}
|
||||
|
||||
website, err := r.website.GetByName(target)
|
||||
@@ -399,7 +407,7 @@ func (r *backupRepo) restoreWebsite(backup, target string) error {
|
||||
// restoreMySQL 恢复 MySQL 备份
|
||||
func (r *backupRepo) restoreMySQL(backup, target string) error {
|
||||
if !io.Exists(backup) {
|
||||
return errors.New("备份文件不存在")
|
||||
return errors.New(r.t.Get("backup file %s not exists", backup))
|
||||
}
|
||||
|
||||
rootPassword, err := r.setting.Get(biz.SettingKeyMySQLRootPassword)
|
||||
@@ -410,19 +418,23 @@ func (r *backupRepo) restoreMySQL(backup, target string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer mysql.Close()
|
||||
defer func(mysql *db.MySQL) {
|
||||
_ = mysql.Close()
|
||||
}(mysql)
|
||||
if exist, _ := mysql.DatabaseExists(target); !exist {
|
||||
return fmt.Errorf("数据库不存在:%s", target)
|
||||
return errors.New(r.t.Get("database does not exist: %s", target))
|
||||
}
|
||||
if err = os.Setenv("MYSQL_PWD", rootPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clean := false
|
||||
if !strings.HasSuffix(backup, ".sql") {
|
||||
backup, err = r.autoUnCompressSQL(backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clean = true
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`mysql -u root '%s' < '%s'`, target, backup); err != nil {
|
||||
@@ -431,38 +443,46 @@ func (r *backupRepo) restoreMySQL(backup, target string) error {
|
||||
if err = os.Unsetenv("MYSQL_PWD"); err != nil {
|
||||
return err
|
||||
}
|
||||
if clean {
|
||||
_ = io.Remove(filepath.Dir(backup))
|
||||
}
|
||||
|
||||
_ = io.Remove(filepath.Dir(backup))
|
||||
return nil
|
||||
}
|
||||
|
||||
// restorePostgres 恢复 PostgreSQL 备份
|
||||
func (r *backupRepo) restorePostgres(backup, target string) error {
|
||||
if !io.Exists(backup) {
|
||||
return errors.New("备份文件不存在")
|
||||
return errors.New(r.t.Get("backup file %s not exists", backup))
|
||||
}
|
||||
|
||||
postgres, err := db.NewPostgres("postgres", "", "127.0.0.1", 5432)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer postgres.Close()
|
||||
defer func(postgres *db.Postgres) {
|
||||
_ = postgres.Close()
|
||||
}(postgres)
|
||||
if exist, _ := postgres.DatabaseExist(target); !exist {
|
||||
return fmt.Errorf("数据库不存在:%s", target)
|
||||
return errors.New(r.t.Get("database does not exist: %s", target))
|
||||
}
|
||||
|
||||
clean := false
|
||||
if !strings.HasSuffix(backup, ".sql") {
|
||||
backup, err = r.autoUnCompressSQL(backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clean = true
|
||||
}
|
||||
|
||||
if _, err = shell.Execf(`su - postgres -c "psql '%s'" < '%s'`, target, backup); err != nil {
|
||||
return err
|
||||
}
|
||||
if clean {
|
||||
_ = io.Remove(filepath.Dir(backup))
|
||||
}
|
||||
|
||||
_ = io.Remove(filepath.Dir(backup))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -485,18 +505,19 @@ func (r *backupRepo) preCheckPath(to, path string) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-目标大小:%s\n", tools.FormatBytes(float64(size)))
|
||||
fmt.Printf("|-目标文件数:%d\n", files)
|
||||
fmt.Printf("|-备份目录可用空间:%s\n", tools.FormatBytes(float64(usage.Free)))
|
||||
fmt.Printf("|-备份目录可用Inode:%d\n", usage.InodesFree)
|
||||
fmt.Println(r.t.Get("|-Target size: %s", tools.FormatBytes(float64(size))))
|
||||
fmt.Println(r.t.Get("|-Target file count: %d", files))
|
||||
fmt.Println(r.t.Get("|-Backup directory available space: %s", tools.FormatBytes(float64(usage.Free))))
|
||||
fmt.Println(r.t.Get("|-Backup directory available Inode: %d", usage.InodesFree))
|
||||
}
|
||||
|
||||
if uint64(size) > usage.Free {
|
||||
return errors.New("备份目录空间不足")
|
||||
}
|
||||
if uint64(files) > usage.InodesFree {
|
||||
return errors.New("备份目录Inode不足")
|
||||
return errors.New(r.t.Get("Insufficient backup directory space"))
|
||||
}
|
||||
// 对于 fuse 等文件系统,可能没有 inode 的概念
|
||||
/*if uint64(files) > usage.InodesFree {
|
||||
return errors.New(r.t.Get("Insufficient backup directory inode"))
|
||||
}*/
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -511,13 +532,13 @@ func (r *backupRepo) preCheckDB(to string, size int64) error {
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-目标大小:%s\n", tools.FormatBytes(float64(size)))
|
||||
fmt.Printf("|-备份目录可用空间:%s\n", tools.FormatBytes(float64(usage.Free)))
|
||||
fmt.Printf("|-备份目录可用Inode:%d\n", usage.InodesFree)
|
||||
fmt.Println(r.t.Get("|-Target size: %s", tools.FormatBytes(float64(size))))
|
||||
fmt.Println(r.t.Get("|-Backup directory available space: %s", tools.FormatBytes(float64(usage.Free))))
|
||||
fmt.Println(r.t.Get("|-Backup directory available Inode: %d", usage.InodesFree))
|
||||
}
|
||||
|
||||
if uint64(size) > usage.Free {
|
||||
return errors.New("备份目录空间不足")
|
||||
return errors.New(r.t.Get("Insufficient backup directory space"))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -525,7 +546,7 @@ func (r *backupRepo) preCheckDB(to string, size int64) error {
|
||||
|
||||
// autoUnCompressSQL 自动处理压缩文件
|
||||
func (r *backupRepo) autoUnCompressSQL(backup string) (string, error) {
|
||||
temp, err := io.TempDir(backup)
|
||||
temp, err := os.MkdirTemp("", "sql-uncompress")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -537,7 +558,7 @@ func (r *backupRepo) autoUnCompressSQL(backup string) (string, error) {
|
||||
backup = "" // 置空,防止干扰后续判断
|
||||
if files, err := os.ReadDir(temp); err == nil {
|
||||
if len(files) != 1 {
|
||||
return "", fmt.Errorf("压缩文件中包含的文件数量不为1,实际为%d", len(files))
|
||||
return "", errors.New(r.t.Get("The number of files contained in the compressed file is not 1, actual %d", len(files)))
|
||||
}
|
||||
if strings.HasSuffix(files[0].Name(), ".sql") {
|
||||
backup = filepath.Join(temp, files[0].Name())
|
||||
@@ -545,7 +566,7 @@ func (r *backupRepo) autoUnCompressSQL(backup string) (string, error) {
|
||||
}
|
||||
|
||||
if backup == "" {
|
||||
return "", errors.New("无法找到.sql备份文件")
|
||||
return "", errors.New(r.t.Get("could not find .sql backup file"))
|
||||
}
|
||||
|
||||
return backup, nil
|
||||
@@ -553,22 +574,23 @@ func (r *backupRepo) autoUnCompressSQL(backup string) (string, error) {
|
||||
|
||||
func (r *backupRepo) FixPanel() error {
|
||||
if app.IsCli {
|
||||
fmt.Println("|-开始修复面板...")
|
||||
fmt.Println(r.t.Get("|-Start fixing the panel..."))
|
||||
}
|
||||
|
||||
// 检查关键文件是否正常
|
||||
flag := false
|
||||
if !io.Exists(filepath.Join(app.Root, "panel", "web")) {
|
||||
flag := !io.Exists("/usr/local/etc/panel/config.yml") ||
|
||||
!io.Exists(filepath.Join(app.Root, "panel", "web")) ||
|
||||
!io.Exists(filepath.Join(app.Root, "panel", "storage", "app.db")) ||
|
||||
io.Exists("/tmp/panel-storage.zip")
|
||||
// 检查数据库连接
|
||||
if err := r.db.Exec("VACUUM").Error; err != nil {
|
||||
flag = true
|
||||
}
|
||||
if !io.Exists(filepath.Join(app.Root, "panel", "storage", "app.db")) {
|
||||
flag = true
|
||||
}
|
||||
if io.Exists("/tmp/panel-storage.zip") {
|
||||
if err := r.db.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error; err != nil {
|
||||
flag = true
|
||||
}
|
||||
if !flag {
|
||||
return fmt.Errorf("文件正常无需修复,请运行 panel-cli update 更新面板")
|
||||
return errors.New(r.t.Get("Files are normal and do not need to be repaired, please run panel-cli update to update the panel"))
|
||||
}
|
||||
|
||||
// 再次确认是否需要修复
|
||||
@@ -578,10 +600,10 @@ func (r *backupRepo) FixPanel() error {
|
||||
io.Exists(filepath.Join(app.Root, "panel", "storage", "app.db")) &&
|
||||
io.Exists("/usr/local/etc/panel/config.yml") {
|
||||
if err := io.Remove("/tmp/panel-storage.zip"); err != nil {
|
||||
return fmt.Errorf("清理临时文件失败:%w", err)
|
||||
return errors.New(r.t.Get("failed to clean temporary files: %v", err))
|
||||
}
|
||||
if app.IsCli {
|
||||
fmt.Println("|-已清理临时文件,请运行 panel-cli update 更新面板")
|
||||
fmt.Println(r.t.Get("|-Cleaned up temporary files, please run panel-cli update to update the panel"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -596,57 +618,57 @@ func (r *backupRepo) FixPanel() error {
|
||||
return int(b.Time.Unix() - a.Time.Unix())
|
||||
})
|
||||
if len(list) == 0 {
|
||||
return fmt.Errorf("未找到备份文件,无法自动修复")
|
||||
return errors.New(r.t.Get("No backup file found, unable to automatically repair"))
|
||||
}
|
||||
latest := list[0]
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-使用备份文件:%s\n", latest.Name)
|
||||
fmt.Println(r.t.Get("|-Backup file used: %s", latest.Name))
|
||||
}
|
||||
|
||||
// 解压备份文件
|
||||
if app.IsCli {
|
||||
fmt.Println("|-解压备份文件...")
|
||||
fmt.Println(r.t.Get("|-Unzip backup file..."))
|
||||
}
|
||||
if err = io.Remove("/tmp/panel-fix"); err != nil {
|
||||
return fmt.Errorf("清理临时目录失败:%w", err)
|
||||
return errors.New(r.t.Get("Cleaning temporary directory failed: %v", err))
|
||||
}
|
||||
if err = io.UnCompress(latest.Path, "/tmp/panel-fix"); err != nil {
|
||||
return fmt.Errorf("解压备份文件失败:%w", err)
|
||||
return errors.New(r.t.Get("Unzip backup file failed: %v", err))
|
||||
}
|
||||
|
||||
// 移动文件到对应位置
|
||||
if app.IsCli {
|
||||
fmt.Println("|-移动备份文件...")
|
||||
fmt.Println(r.t.Get("|-Move backup file..."))
|
||||
}
|
||||
if io.Exists(filepath.Join("/tmp/panel-fix", "panel")) && io.IsDir(filepath.Join("/tmp/panel-fix", "panel")) {
|
||||
if err = io.Remove(filepath.Join(app.Root, "panel")); err != nil {
|
||||
return fmt.Errorf("删除目录失败:%w", err)
|
||||
return errors.New(r.t.Get("Remove panel file failed: %v", err))
|
||||
}
|
||||
if err = io.Mv(filepath.Join("/tmp/panel-fix", "panel"), filepath.Join(app.Root)); err != nil {
|
||||
return fmt.Errorf("移动目录失败:%w", err)
|
||||
return errors.New(r.t.Get("Move panel file failed: %v", err))
|
||||
}
|
||||
}
|
||||
if io.Exists(filepath.Join("/tmp/panel-fix", "config.yml")) {
|
||||
if err = io.Mv(filepath.Join("/tmp/panel-fix", "config.yml"), "/usr/local/etc/panel/config.yml"); err != nil {
|
||||
return fmt.Errorf("移动文件失败:%w", err)
|
||||
return errors.New(r.t.Get("Move panel config failed: %v", err))
|
||||
}
|
||||
}
|
||||
if io.Exists(filepath.Join("/tmp/panel-fix", "panel-cli")) {
|
||||
if err = io.Mv(filepath.Join("/tmp/panel-fix", "panel-cli"), "/usr/local/sbin/panel-cli"); err != nil {
|
||||
return fmt.Errorf("移动文件失败:%w", err)
|
||||
return errors.New(r.t.Get("Move panel-cli file failed: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// tmp 目录下如果有 storage 备份,则解压回去
|
||||
if app.IsCli {
|
||||
fmt.Println("|-恢复面板数据...")
|
||||
fmt.Println(r.t.Get("|-Restore panel data..."))
|
||||
}
|
||||
if io.Exists("/tmp/panel-storage.zip") {
|
||||
if err = io.UnCompress("/tmp/panel-storage.zip", filepath.Join(app.Root, "panel")); err != nil {
|
||||
return fmt.Errorf("恢复面板数据失败:%w", err)
|
||||
return errors.New(r.t.Get("Unzip panel data failed: %v", err))
|
||||
}
|
||||
if err = io.Remove("/tmp/panel-storage.zip"); err != nil {
|
||||
return fmt.Errorf("清理临时文件失败:%w", err)
|
||||
return errors.New(r.t.Get("Cleaning temporary file failed: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,12 +681,12 @@ func (r *backupRepo) FixPanel() error {
|
||||
|
||||
// 处理权限
|
||||
if app.IsCli {
|
||||
fmt.Println("|-设置关键文件权限...")
|
||||
fmt.Println(r.t.Get("|-Set key file permissions..."))
|
||||
}
|
||||
if err = io.Chmod("/usr/local/etc/panel/config.yml", 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = io.Chmod("/etc/systemd/system/panel.service", 0700); err != nil {
|
||||
if err = io.Chmod("/etc/systemd/system/panel.service", 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = io.Chmod("/usr/local/sbin/panel-cli", 0700); err != nil {
|
||||
@@ -674,8 +696,12 @@ func (r *backupRepo) FixPanel() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = io.Remove("/tmp/panel-fix"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-修复完成")
|
||||
fmt.Println(r.t.Get("|-Fix completed"))
|
||||
}
|
||||
|
||||
tools.RestartPanel()
|
||||
@@ -693,121 +719,115 @@ func (r *backupRepo) UpdatePanel(version, url, checksum string) error {
|
||||
|
||||
name := filepath.Base(url)
|
||||
if app.IsCli {
|
||||
fmt.Printf("|-目标版本:%s\n", version)
|
||||
fmt.Printf("|-下载链接:%s\n", url)
|
||||
fmt.Printf("|-文件名:%s\n", name)
|
||||
fmt.Println(r.t.Get("|-Target version: %s", version))
|
||||
fmt.Println(r.t.Get("|-Download link: %s", url))
|
||||
fmt.Println(r.t.Get("|-File name: %s", name))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-正在下载...")
|
||||
fmt.Println(r.t.Get("|-Downloading..."))
|
||||
}
|
||||
if _, err := shell.Execf("wget -T 120 -t 3 -O /tmp/%s %s", name, url); err != nil {
|
||||
return fmt.Errorf("下载失败:%w", err)
|
||||
return errors.New(r.t.Get("Download failed: %v", err))
|
||||
}
|
||||
if _, err := shell.Execf("wget -T 20 -t 3 -O /tmp/%s %s", name+".sha256", checksum); err != nil {
|
||||
return fmt.Errorf("下载失败:%w", err)
|
||||
return errors.New(r.t.Get("Download failed: %v", err))
|
||||
}
|
||||
if !io.Exists(filepath.Join("/tmp", name)) || !io.Exists(filepath.Join("/tmp", name+".sha256")) {
|
||||
return errors.New("下载文件检查失败")
|
||||
return errors.New(r.t.Get("Download file check failed"))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-校验下载文件...")
|
||||
fmt.Println(r.t.Get("|-Verify download file..."))
|
||||
}
|
||||
if check, err := shell.Execf("cd /tmp && sha256sum -c %s --ignore-missing", name+".sha256"); check != name+": OK" || err != nil {
|
||||
return errors.New("下载文件校验失败")
|
||||
return errors.New(r.t.Get("Verify download file failed: %v", err))
|
||||
}
|
||||
if err := io.Remove(filepath.Join("/tmp", name+".sha256")); err != nil {
|
||||
if app.IsCli {
|
||||
fmt.Println("|-清理校验文件失败:", err)
|
||||
}
|
||||
return fmt.Errorf("清理校验文件失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Clean up verification file failed: %v", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-前置检查...")
|
||||
}
|
||||
if io.Exists("/tmp/panel-storage.zip") {
|
||||
return errors.New("检测到 /tmp 存在临时文件,可能是上次更新失败所致,请运行 panel-cli fix 修复后重试")
|
||||
return errors.New(r.t.Get("Temporary file detected in /tmp, this may be caused by the last update failure, please run panel-cli fix to repair and try again"))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-备份面板数据...")
|
||||
fmt.Println(r.t.Get("|-Backup panel data..."))
|
||||
}
|
||||
// 备份面板
|
||||
if err := r.Create(biz.BackupTypePanel, ""); err != nil {
|
||||
if app.IsCli {
|
||||
fmt.Println("|-备份面板失败:", err)
|
||||
}
|
||||
return fmt.Errorf("备份面板失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Backup panel data failed: %v", err))
|
||||
}
|
||||
if err := io.Compress(filepath.Join(app.Root, "panel/storage"), nil, "/tmp/panel-storage.zip"); err != nil {
|
||||
if app.IsCli {
|
||||
fmt.Println("|-备份面板数据失败:", err)
|
||||
}
|
||||
return fmt.Errorf("备份面板数据失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Backup panel data failed: %v", err))
|
||||
}
|
||||
if !io.Exists("/tmp/panel-storage.zip") {
|
||||
return errors.New("已备份面板数据检查失败")
|
||||
return errors.New(r.t.Get("|-Backup panel data failed, missing file"))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-清理旧版本...")
|
||||
fmt.Println(r.t.Get("|-Cleaning old version..."))
|
||||
}
|
||||
if _, err := shell.Execf("rm -rf %s/panel/*", app.Root); err != nil {
|
||||
return fmt.Errorf("清理旧版本失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Cleaning old version failed: %v", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-解压新版本...")
|
||||
fmt.Println(r.t.Get("|-Unzip new version..."))
|
||||
}
|
||||
if err := io.UnCompress(filepath.Join("/tmp", name), filepath.Join(app.Root, "panel")); err != nil {
|
||||
return fmt.Errorf("解压失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Unzip new version failed: %v", err))
|
||||
}
|
||||
if !io.Exists(filepath.Join(app.Root, "panel", "web")) {
|
||||
return errors.New("解压失败,缺失文件")
|
||||
return errors.New(r.t.Get("|-Unzip new version failed, missing file"))
|
||||
}
|
||||
if err := io.Remove(filepath.Join("/tmp", name)); err != nil {
|
||||
return errors.New(r.t.Get("|-Clean up temporary file failed: %v", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-恢复面板数据...")
|
||||
fmt.Println(r.t.Get("|-Restore panel data..."))
|
||||
}
|
||||
if err := io.UnCompress("/tmp/panel-storage.zip", filepath.Join(app.Root, "panel", "storage")); err != nil {
|
||||
return fmt.Errorf("恢复面板数据失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Restore panel data failed: %v", err))
|
||||
}
|
||||
if !io.Exists(filepath.Join(app.Root, "panel/storage/app.db")) {
|
||||
return errors.New("恢复面板数据失败")
|
||||
return errors.New(r.t.Get("|-Restore panel data failed, missing file"))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-运行更新后脚本...")
|
||||
fmt.Println(r.t.Get("|-Run post-update script..."))
|
||||
}
|
||||
if _, err := shell.Execf("curl -fsLm 10 https://dl.cdn.haozi.net/panel/auto_update.sh | bash"); err != nil {
|
||||
return fmt.Errorf("运行面板更新后脚本失败:%w", err)
|
||||
if _, err := shell.Execf("curl -sSLm 10 https://dl.cdn.haozi.net/panel/auto_update.sh | bash"); err != nil {
|
||||
return errors.New(r.t.Get("|-Run post-update script failed: %v", err))
|
||||
}
|
||||
if _, err := shell.Execf(`wget -O /etc/systemd/system/panel.service https://dl.cdn.haozi.net/panel/panel.service && sed -i "s|/www|%s|g" /etc/systemd/system/panel.service`, app.Root); err != nil {
|
||||
return fmt.Errorf("下载面板服务文件失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Download panel service file failed: %v", err))
|
||||
}
|
||||
if _, err := shell.Execf("panel-cli setting write version %s", version); err != nil {
|
||||
return fmt.Errorf("写入面板版本号失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Write new panel version failed: %v", err))
|
||||
}
|
||||
if err := io.Mv(filepath.Join(app.Root, "panel/cli"), "/usr/local/sbin/panel-cli"); err != nil {
|
||||
return fmt.Errorf("移动面板命令行工具失败:%w", err)
|
||||
return errors.New(r.t.Get("|-Move panel-cli tool failed: %v", err))
|
||||
}
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-设置关键文件权限...")
|
||||
fmt.Println(r.t.Get("|-Set key file permissions..."))
|
||||
}
|
||||
_ = io.Chmod("/usr/local/sbin/panel-cli", 0700)
|
||||
_ = io.Chmod("/etc/systemd/system/panel.service", 0700)
|
||||
_ = io.Chmod("/etc/systemd/system/panel.service", 0644)
|
||||
_ = io.Chmod(filepath.Join(app.Root, "panel"), 0700)
|
||||
|
||||
if app.IsCli {
|
||||
fmt.Println("|-更新完成")
|
||||
fmt.Println(r.t.Get("|-Update completed"))
|
||||
}
|
||||
|
||||
_, _ = shell.Execf("systemctl daemon-reload")
|
||||
_ = io.Remove("/tmp/panel-storage.zip")
|
||||
_ = io.Remove(filepath.Join(app.Root, "panel/config.example.yml"))
|
||||
if sqlDB, err := r.db.DB(); err == nil {
|
||||
_ = sqlDB.Close()
|
||||
}
|
||||
tools.RestartPanel()
|
||||
|
||||
return nil
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/apps"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/pkg/api"
|
||||
"github.com/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
"github.com/tnborg/panel/pkg/api"
|
||||
"github.com/tnborg/panel/pkg/apploader"
|
||||
)
|
||||
|
||||
type cacheRepo struct {
|
||||
@@ -20,7 +20,7 @@ type cacheRepo struct {
|
||||
|
||||
func NewCacheRepo(db *gorm.DB) biz.CacheRepo {
|
||||
return &cacheRepo{
|
||||
api: api.NewAPI(app.Version),
|
||||
api: api.NewAPI(app.Version, app.Locale),
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
@@ -57,8 +57,8 @@ func (r *cacheRepo) UpdateApps() error {
|
||||
}
|
||||
|
||||
// 去除本地不存在的应用
|
||||
*remote = slices.Clip(slices.DeleteFunc(*remote, func(app *api.App) bool {
|
||||
return !slices.Contains(apps.Slugs(), app.Slug)
|
||||
*remote = slices.Clip(slices.DeleteFunc(*remote, func(item *api.App) bool {
|
||||
return !slices.Contains(apploader.Slugs(), item.Slug)
|
||||
}))
|
||||
|
||||
encoded, err := json.Marshal(remote)
|
||||
|
||||
+86
-26
@@ -4,31 +4,38 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/TheTNB/panel/internal/app"
|
||||
"github.com/TheTNB/panel/internal/biz"
|
||||
"github.com/TheTNB/panel/internal/http/request"
|
||||
"github.com/TheTNB/panel/pkg/acme"
|
||||
pkgcert "github.com/TheTNB/panel/pkg/cert"
|
||||
"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/tnborg/panel/internal/app"
|
||||
"github.com/tnborg/panel/internal/biz"
|
||||
"github.com/tnborg/panel/internal/http/request"
|
||||
"github.com/tnborg/panel/pkg/acme"
|
||||
pkgcert "github.com/tnborg/panel/pkg/cert"
|
||||
"github.com/tnborg/panel/pkg/io"
|
||||
"github.com/tnborg/panel/pkg/shell"
|
||||
"github.com/tnborg/panel/pkg/systemctl"
|
||||
"github.com/tnborg/panel/pkg/types"
|
||||
)
|
||||
|
||||
type certRepo struct {
|
||||
t *gotext.Locale
|
||||
db *gorm.DB
|
||||
log *slog.Logger
|
||||
client *acme.Client
|
||||
}
|
||||
|
||||
func NewCertRepo(db *gorm.DB) biz.CertRepo {
|
||||
func NewCertRepo(t *gotext.Locale, db *gorm.DB, log *slog.Logger) biz.CertRepo {
|
||||
return &certRepo{
|
||||
db: db,
|
||||
t: t,
|
||||
db: db,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +56,8 @@ func (r *certRepo) List(page, limit uint) ([]*types.CertList, int64, error) {
|
||||
AutoRenew: cert.AutoRenew,
|
||||
Cert: cert.Cert,
|
||||
Key: cert.Key,
|
||||
CertURL: cert.CertURL,
|
||||
Script: cert.Script,
|
||||
CreatedAt: cert.CreatedAt,
|
||||
UpdatedAt: cert.UpdatedAt,
|
||||
}
|
||||
@@ -80,10 +89,10 @@ func (r *certRepo) GetByWebsite(WebsiteID uint) (*biz.Cert, error) {
|
||||
func (r *certRepo) Upload(req *request.CertUpload) (*biz.Cert, error) {
|
||||
info, err := pkgcert.ParseCert(req.Cert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: %v", err)
|
||||
return nil, errors.New(r.t.Get("failed to parse certificate: %v", err))
|
||||
}
|
||||
if _, err = pkgcert.ParseKey(req.Key); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse private key: %v", err)
|
||||
return nil, errors.New(r.t.Get("failed to parse private key: %v", err))
|
||||
}
|
||||
|
||||
cert := &biz.Cert{
|
||||
@@ -119,6 +128,9 @@ func (r *certRepo) Update(req *request.CertUpdate) error {
|
||||
if err == nil && req.Type == "upload" {
|
||||
req.Domains = info.DNSNames
|
||||
}
|
||||
if req.Type == "upload" && req.AutoRenew {
|
||||
return errors.New(r.t.Get("upload certificate cannot be set to auto renew"))
|
||||
}
|
||||
|
||||
return r.db.Model(&biz.Cert{}).Where("id = ?", req.ID).Select("*").Updates(&biz.Cert{
|
||||
ID: req.ID,
|
||||
@@ -128,6 +140,7 @@ func (r *certRepo) Update(req *request.CertUpdate) error {
|
||||
Type: req.Type,
|
||||
Cert: req.Cert,
|
||||
Key: req.Key,
|
||||
Script: req.Script,
|
||||
Domains: req.Domains,
|
||||
AutoRenew: req.AutoRenew,
|
||||
}).Error
|
||||
@@ -149,14 +162,14 @@ func (r *certRepo) ObtainAuto(id uint) (*acme.Certificate, error) {
|
||||
}
|
||||
|
||||
if cert.DNS != nil {
|
||||
client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
|
||||
client.UseDns(cert.DNS.Type, cert.DNS.Data)
|
||||
} else {
|
||||
if cert.Website == nil {
|
||||
return nil, errors.New("this certificate is not associated with a website and cannot be signed. You can try to sign it manually")
|
||||
return nil, errors.New(r.t.Get("this certificate is not associated with a website and cannot be obtained. You can try to obtain it manually"))
|
||||
} else {
|
||||
for _, domain := range cert.Domains {
|
||||
if strings.Contains(domain, "*") {
|
||||
return nil, errors.New("wildcard domains cannot use HTTP verification")
|
||||
return nil, errors.New(r.t.Get("wildcard domains cannot use HTTP verification"))
|
||||
}
|
||||
}
|
||||
conf := fmt.Sprintf("%s/server/vhost/acme/%s.conf", app.Root, cert.Website.Name)
|
||||
@@ -180,6 +193,10 @@ func (r *certRepo) ObtainAuto(id uint) (*acme.Certificate, error) {
|
||||
return &ssl, r.Deploy(cert.ID, cert.WebsiteID)
|
||||
}
|
||||
|
||||
if err = r.runScript(cert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ssl, nil
|
||||
}
|
||||
|
||||
@@ -190,7 +207,7 @@ func (r *certRepo) ObtainManual(id uint) (*acme.Certificate, error) {
|
||||
}
|
||||
|
||||
if r.client == nil {
|
||||
return nil, errors.New("please retry the manual obtain operation")
|
||||
return nil, errors.New(r.t.Get("please retry the manual obtain operation"))
|
||||
}
|
||||
|
||||
ssl, err := r.client.ObtainCertificateManual()
|
||||
@@ -209,6 +226,10 @@ func (r *certRepo) ObtainManual(id uint) (*acme.Certificate, error) {
|
||||
return &ssl, r.Deploy(cert.ID, cert.WebsiteID)
|
||||
}
|
||||
|
||||
if err = r.runScript(cert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ssl, nil
|
||||
}
|
||||
|
||||
@@ -233,6 +254,10 @@ func (r *certRepo) ObtainSelfSigned(id uint) error {
|
||||
return r.Deploy(cert.ID, cert.WebsiteID)
|
||||
}
|
||||
|
||||
if err = r.runScript(cert); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -248,18 +273,18 @@ func (r *certRepo) Renew(id uint) (*acme.Certificate, error) {
|
||||
}
|
||||
|
||||
if cert.CertURL == "" {
|
||||
return nil, errors.New("this certificate has not been signed successfully and cannot be renewed")
|
||||
return nil, errors.New(r.t.Get("this certificate has not been obtained successfully and cannot be renewed"))
|
||||
}
|
||||
|
||||
if cert.DNS != nil {
|
||||
client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data)
|
||||
client.UseDns(cert.DNS.Type, cert.DNS.Data)
|
||||
} else {
|
||||
if cert.Website == nil {
|
||||
return nil, errors.New("this certificate is not associated with a website and cannot be signed. You can try to sign it manually")
|
||||
return nil, errors.New(r.t.Get("this certificate is not associated with a website and cannot be obtained. You can try to obtain it manually"))
|
||||
} else {
|
||||
for _, domain := range cert.Domains {
|
||||
if strings.Contains(domain, "*") {
|
||||
return nil, errors.New("wildcard domains cannot use HTTP verification")
|
||||
return nil, errors.New(r.t.Get("wildcard domains cannot use HTTP verification"))
|
||||
}
|
||||
}
|
||||
conf := fmt.Sprintf("%s/server/vhost/acme/%s.conf", app.Root, cert.Website.Name)
|
||||
@@ -269,7 +294,11 @@ func (r *certRepo) Renew(id uint) (*acme.Certificate, error) {
|
||||
|
||||
ssl, err := client.RenewCertificate(context.Background(), cert.CertURL, cert.Domains, acme.KeyType(cert.Type))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// 续签失败,尝试重签
|
||||
ssl, err = client.ObtainCertificate(context.Background(), cert.Domains, acme.KeyType(cert.Type))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
cert.CertURL = ssl.URL
|
||||
@@ -297,7 +326,7 @@ func (r *certRepo) ManualDNS(id uint) ([]acme.DNSRecord, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.UseManualDns(len(cert.Domains))
|
||||
client.UseManualDns()
|
||||
records, err := client.GetDNSRecords(context.Background(), cert.Domains, acme.KeyType(cert.Type))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -319,7 +348,7 @@ func (r *certRepo) Deploy(ID, WebsiteID uint) error {
|
||||
}
|
||||
|
||||
if cert.Cert == "" || cert.Key == "" {
|
||||
return errors.New("this certificate has not been signed successfully and cannot be deployed")
|
||||
return errors.New(r.t.Get("this certificate has not been obtained successfully and cannot be deployed"))
|
||||
}
|
||||
|
||||
website := new(biz.Website)
|
||||
@@ -341,9 +370,40 @@ func (r *certRepo) Deploy(ID, WebsiteID uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *certRepo) runScript(cert *biz.Cert) error {
|
||||
if cert.Script == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.CreateTemp("", "cert-deploy-*.sh")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 替换变量
|
||||
cert.Script = strings.ReplaceAll(cert.Script, "{cert}", cert.Cert)
|
||||
cert.Script = strings.ReplaceAll(cert.Script, "{key}", cert.Key)
|
||||
|
||||
if _, err = f.WriteString(cert.Script); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.Chmod(0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(name string) {
|
||||
_ = os.Remove(name)
|
||||
}(f.Name())
|
||||
|
||||
_, err = shell.Execf("bash " + f.Name())
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *certRepo) getClient(cert *biz.Cert) (*acme.Client, error) {
|
||||
if cert.Account == nil {
|
||||
return nil, errors.New("this certificate is not associated with an ACME account and cannot be signed")
|
||||
return nil, errors.New(r.t.Get("this certificate is not associated with an ACME account and cannot be obtained"))
|
||||
}
|
||||
|
||||
var ca string
|
||||
@@ -367,5 +427,5 @@ func (r *certRepo) getClient(cert *biz.Cert) (*acme.Client, error) {
|
||||
eab = &acme.EAB{KeyID: cert.Account.Kid, MACKey: cert.Account.HmacEncoded}
|
||||
}
|
||||
|
||||
return acme.NewPrivateKeyAccount(cert.Account.Email, cert.Account.PrivateKey, ca, eab)
|
||||
return acme.NewPrivateKeyAccount(cert.Account.Email, cert.Account.PrivateKey, ca, eab, r.log)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user