Compare commits

..
228 Commits
Author SHA1 Message Date
CityFun c5d403f10a feat: Add pagination cache (#10229) 2025-09-02 15:23:27 +08:00
蘭 598098c2ed feat: Login page username automatically obtains focus (#10228) 2025-09-02 15:12:08 +08:00
ssongliu bff1a8ad60 fix: Optimize warning messages for database deletion (#10226) 2025-09-02 05:43:44 +00:00
蘭 592e315f21 refactor: Adjust the default page size for the file management list (#10225) 2025-09-02 05:41:45 +00:00
CityFun 76933bc6ef feat: Modify the configuration file to support tabs (#10224) 2025-09-02 05:39:45 +00:00
KOMATA 0dc312db64 fix: Fix typo of "Redis" in language files (#10222) 2025-09-02 10:21:51 +08:00
ssongliu 4deb92e715 fix: Optimize container port detection mechanism (#10220) 2025-09-01 10:14:49 +00:00
CityFun bc95e47b92 fix: Fixed the issue with The child node cannot update the certificate (#10219) 2025-09-01 17:39:56 +08:00
CityFun f2335d313c feat: Optimize file log reading (#10218) 2025-09-01 17:39:40 +08:00
蘭 4c287f4a2d feat: Add date to smtp service header (#10215) 2025-09-01 06:42:47 +00:00
CityFun 0a6e1c275b feat: Restrict sqlbot version upgrades (#10214) 2025-09-01 06:26:48 +00:00
ssongliu ff2dc05e7f fix: Optimize container port detection mechanism (#10209)
Refs #5827
2025-09-01 06:24:48 +00:00
蘭 f62ce085bf feat: MFA verification code automatically obtains focus (#10213)
refs #8032
2025-09-01 06:20:48 +00:00
CityFun 29306827ba feat: App Store - Installed: Pagination Cache Added (#10212) 2025-09-01 06:08:48 +00:00
ssongliu 222d07175a fix: Fix version upgrade information display issues (#10208)
Refs #10196
2025-09-01 06:00:48 +00:00
CityFun 663196c3d9 fix: Resolve the issue of failed push certificate to all nodes. (#10195) 2025-08-29 07:46:15 +00:00
蘭 df839714a3 fix: Fix issues with pasting files not loading and file retrieval path cursor (#10194) 2025-08-29 07:44:15 +00:00
CityFun dcb004e983 fix: Fixed issue with build frontend failed (#10193) 2025-08-29 06:20:15 +00:00
CityFun cee0604efc perf: Optimize front-end component imports (#10192) 2025-08-29 06:04:14 +00:00
蘭 dfbd364f60 fix: Fix the initialization issue of alert tasks (#10191) 2025-08-29 05:56:15 +00:00
CityFun 339d6732d7 fix: fixed issue with push ssl to node failed (#10188) 2025-08-29 05:54:16 +00:00
蘭 b0dbfb4c7c fix: Fix page loading flicker issue (#10187) 2025-08-29 05:52:17 +00:00
ssongliu 021603d25d fix: Optimize container compose deletion logic (#10186)
Refs #10184
2025-08-29 05:50:15 +00:00
CityFun a875e1ec5c fix: Resolving the Issue of Website Directory Not Migrated During OpenResty Reinstallation (#10185) 2025-08-29 13:48:20 +08:00
CityFun 5c5904216b feat: remove console (#10181) 2025-08-28 18:57:06 +08:00
蘭 1dae294d76 fix: Fix folder redirection issue (#10180) 2025-08-28 10:56:09 +00:00
ssongliu acbb151b15 fix: Update authorization IP regular expression (#10179)
Refs #10173
2025-08-28 10:52:10 +00:00
CityFun a1311a2a0d perf: optimization file component (#10178) 2025-08-28 18:50:53 +08:00
蘭 bc8d564ce8 fix: Fix some bugs in the alert (#10176) 2025-08-28 07:58:10 +00:00
CityFun 97cdf7bc78 feat: Optimized the runtime environment installation, but the application store still does not display it as installed. (#10175)
Refs https://github.com/1Panel-dev/1Panel/issues/10174
2025-08-28 07:56:08 +00:00
蘭 ce69b4de01 fix: Fix bug in file management with multiple tabs (#10170) 2025-08-28 04:10:09 +00:00
ssongliu e880df8142 fix: Fix snapshot cronjob execution issues (#10169) 2025-08-28 04:08:09 +00:00
ssongliu a35ca87778 fix: Remove deprecated google drive connection information (#10168) 2025-08-28 04:06:08 +00:00
ssongliu 5a34e2d380 fix: Optimize backup logic for cronjob (#10163) 2025-08-27 14:35:28 +08:00
蘭 abf3247c50 fix: Resolve file renaming and other operational conflicts (#10162) 2025-08-27 05:44:03 +00:00
ssongliu 8663ed129d fix: Fix image sorting anomalies (#10159) 2025-08-27 13:41:37 +08:00
cui c0a0f4c801 refactor: use maps (#10154) 2025-08-27 10:08:51 +08:00
KOMATA e84aa49003 feat: Enhance anti-leech configuration with new options and improved UI (#10149)
* feat: enhance anti-leech configuration with new options and improved UI

* fix: remove redundant code
2025-08-27 10:08:38 +08:00
ssongliu 6f45b90520 fix: Fix container resource cleanup result tracking (#10153) 2025-08-26 14:06:01 +00:00
蘭 0685984a80 ref: Adjust the content of alert information (#10152) 2025-08-26 14:04:01 +00:00
ssongliu 5f33c083bf style: Adjust compose log display height (#10151) 2025-08-26 09:41:59 +00:00
CityFun ab0e0f3f43 feat: Add restart runtime for config php (#10150) 2025-08-26 09:13:59 +00:00
蘭 6b62b84157 feat: File management supports multi tab management (#10147) 2025-08-26 09:10:00 +00:00
CityFun fd9627576b feat: Add table page size cache (#10146) 2025-08-26 17:08:13 +08:00
ssongliu 39f334f3a2 fix: Optimize backup logic for cronjob (#10145) 2025-08-26 17:07:41 +08:00
CityFun ca84909716 feat: confirm before leaving during file upload (#10143) 2025-08-26 06:19:58 +00:00
CityFun 283609cf2d style: change appstore tag style (#10142) 2025-08-26 06:13:59 +00:00
CityFun 9591409fe2 optimization: optimization the log file read (#10141) 2025-08-26 06:11:59 +00:00
CityFun 9cf52ccc97 fix: incorrect restart strategy display in app editor (#10136) 2025-08-25 18:56:58 +08:00
CityFun 261cb2061b feat: add NoLog prompt for log component (#10135) 2025-08-25 18:56:43 +08:00
CityFun f45af6e0cc style: change appstore tag style (#10134) 2025-08-25 10:55:57 +00:00
ssongliu c752ea4959 fix: Add gpu monitoring support for some machines (#10133)
Refs #9504 #8397
2025-08-25 10:53:57 +00:00
CityFun b2b578a5f0 feat: add timeout for app upgrade (#10132) 2025-08-25 10:51:56 +00:00
CityFun 47e60bfdcb refactor: reverse proxy rules (#10131) 2025-08-25 08:15:57 +00:00
ssongliu 780b6cdec9 fix: Optimize decryption implementation (#10130) 2025-08-25 08:13:56 +00:00
ssongliu 0a5b13a418 feat: Add restart support for container compose (#10129) 2025-08-25 08:11:56 +00:00
ssongliu ebdc3f05de fix: Optimize application recommendation logic (#10128) 2025-08-25 08:09:56 +00:00
CityFun aad79077bf feat: add restart button for php supervisor (#10127)
Refs https://github.com/1Panel-dev/1Panel/issues/10083
2025-08-25 08:07:57 +00:00
ssongliu 00b25b39c5 fix: Fix backup record loading timeout issue (#10126) 2025-08-25 08:05:57 +00:00
KOMATA dd6a709053 fix: resolve null destination when enabling AntiLeech for deployment mode websites (#10125)
* fix: Add proxy_pass directive for deployment model in UpdateAntiLeech

* fix: Update UpdateAntiLeech to use constant for deployment type
2025-08-25 16:00:35 +08:00
ssongliu 99b04e3635 fix: Fix SSH log export exception when logs are empty (#10114) 2025-08-22 14:37:52 +00:00
ssongliu 2e06b78409 style: Optimize cache cleaning interface styling (#10113) 2025-08-22 18:29:47 +08:00
CityFun c7d603e142 fix: resolve log loss in certain scenarios (#10111) 2025-08-22 09:15:52 +00:00
ssongliu 4fcf845934 feat: Add system information copy button for dashboard (#10109)
Refs #9725
2025-08-22 07:59:52 +00:00
CityFun 83cefb2a72 feat: improve empty state for app store (#10108) 2025-08-22 07:57:52 +00:00
ssongliu 7ab40996e0 style: Optimize version update log styling (#10107) 2025-08-22 07:55:52 +00:00
ssongliu bbe4d49947 feat: Add sorting support to image list (#10106)
Refs #9482
2025-08-22 07:53:52 +00:00
KOMATA b6fbacc992 fix: Optimized proxy configure drawer content (#10097)
* fix: Update proxy settings interface, add tabs and optimize style

* fix: update i18n text

* fix: reset modified data

* fix: Optimize proxy settings interface, remove unnecessary options and adjust SNI settings display logic

* fix: Optimize proxy settings interface, replace custom style tag to tailwind.css class

* fix: Update i18n text
2025-08-22 15:46:49 +08:00
ssongliu 89491eac56 fix: Optimize container image operation task logging (#10103)
Refs #9999
2025-08-22 03:23:50 +00:00
CityFun acf27693b2 feat: Add description field matching to application search (#10101)
Refs https://github.com/1Panel-dev/1Panel/issues/10071
2025-08-22 03:07:51 +00:00
ssongliu 5fd9d20f99 fix: Exclude system ip from snapshot backups (#10100)
Refs  #9991
2025-08-22 03:05:50 +00:00
ssongliu fd9c421969 fix: Optimize image push logic (#10096)
Refs #10030
2025-08-21 10:33:49 +00:00
ssongliu f5bae6d75c fix: Add service health check to node editing (#10095)
Refs #9935
2025-08-21 09:29:49 +00:00
CityFun 4ac8bc7bd2 feat: Optimize log file highlighting (#10093) 2025-08-21 17:26:52 +08:00
蘭 ec81d0d2c3 feat: Supports inline editing of file names (#10080) 2025-08-21 02:30:59 +00:00
CityFun 815b633fb8 fix: Fixed issue with website proxy proxy_ssl_name display error (#10079) 2025-08-21 10:18:48 +08:00
ssongliu c69c76f97d fix: Fix container image deletion warning anomalies (#10078) 2025-08-20 16:44:57 +08:00
ssongliu 75197db3e6 fix: Fix partial WebDAV upload failures (#10077) 2025-08-20 16:44:43 +08:00
CityFun d09d686378 feat: Support pushing certificates to other nodes (#10074)
Refs https://github.com/1Panel-dev/1Panel/issues/9103
2025-08-20 08:41:19 +00:00
蘭 d6f16cf700 feat: Support table row selection (#10072) 2025-08-20 08:39:17 +00:00
蘭 536a5fc1a0 feat: Optimize code (#10063) 2025-08-19 14:49:17 +00:00
蘭 a07df9d7fe feat: Optimize log file highlighting (#10062) 2025-08-19 14:47:15 +00:00
ssongliu e9c195c778 fix: Fix report display issues for cronjob (#10058) 2025-08-19 09:33:15 +00:00
蘭 0637ca308b feat: File list supports right menu operation (#10056) 2025-08-19 09:31:15 +00:00
ssongliu fd1e0e72a0 feat: Add default value support for file selection component (#10055) 2025-08-19 09:29:15 +00:00
ssongliu 34b80373c7 fix: Optimize cache cleanup logging (#10053)
Refs #10045
2025-08-19 06:59:15 +00:00
ssongliu 1ade1b8d8b fix: Fix temporary file leak during snapshot deletion (#10052) 2025-08-19 14:58:18 +08:00
ssongliu 19fa30fb96 fix: fix slow log state toggle exception (#10050)
Refs #10007
2025-08-19 06:13:14 +00:00
蘭 efe7815a7e feat: File list supports right-click operation (#10049)
#5620
2025-08-19 05:45:14 +00:00
CityFun e9b4ff1932 feat: Optimize App Store Tag Selection (#10047) 2025-08-19 05:43:15 +00:00
蘭 0dca3fc997 feat(alert): Support panel login/ssh login/license exception and node exception alerts (#10034)
#9635
2025-08-19 05:41:15 +00:00
CityFun 4e6ebb53ac feat: Change Website List Order Rule (#10036)
Refs https://github.com/1Panel-dev/1Panel/issues/9979
2025-08-18 10:35:13 +00:00
CityFun 83ed835c9e fix: Resolve the "No Port Specified" Error When Creating a Website Runtime Environment (#10041) 2025-08-18 18:34:24 +08:00
CityFun 7bcc897379 fix: Fixed issue with child nodes cannot enable "HTTPS Anti-Site Hijacking" (#10032)
Refs https://github.com/1Panel-dev/1Panel/issues/10031
2025-08-18 02:57:13 +00:00
CityFun cd2109fefe feat:Upgrade OpenResty and rebuild image (#10016) 2025-08-18 10:56:22 +08:00
CityFun d337c4726d feat: Add empty state prompt for app list (#10014) 2025-08-15 09:41:03 +00:00
CityFun 356e6a4ef6 feat: Add import/export for URL/UA allow/deny lists (#10012)
Refs https://github.com/1Panel-dev/1Panel/issues/9408
2025-08-15 09:39:03 +00:00
蘭 037da3a7fd feat: Support file management page drag and drop upload (#10011)
#9307
2025-08-15 09:37:04 +00:00
ssongliu 18462296ae fix: Fix 1pctl user-info retrieval exception (#10010)
Refs #10006
2025-08-15 09:35:04 +00:00
苦苦的码人 1d76e54b0d fix:Asynchronous log writing has not taken effect (#10000)
Make it effective by modifying the queue
2025-08-15 17:34:17 +08:00
CityFun 22c903f555 feat: Add import/export for WAF IP groups (#10008) 2025-08-15 14:26:49 +08:00
ssongliu 6d4d53e2c7 feat: Add historical release notes display (#10002)
Refs #9662
2025-08-14 14:34:54 +00:00
CityFun 6b81ae1b7b feat: Website Add Composer support (#9997)
Refs https://github.com/1Panel-dev/1Panel/issues/6030
2025-08-14 09:46:54 +00:00
ssongliu 5b62a0f2b3 feat: Add file verification for SSH file transfers (#9998) 2025-08-14 09:26:55 +00:00
ssongliu 137465683a fix: Optimize license error messaging (#9996) 2025-08-14 09:24:53 +00:00
ssongliu 6be7b4a75a fix: Fix terminal connection initialization exception (#9987) 2025-08-13 13:46:52 +00:00
ssongliu 58ca377418 feat: Optimize file selection component (#9983) 2025-08-13 09:56:51 +00:00
蘭 b173e808c7 fix: Fix file hiding display issue (#9981)
#9871
2025-08-13 08:40:50 +00:00
CityFun ec44ba9335 fix: Fix the issue of system certificates not auto-renewing. (#9978) 2025-08-13 16:14:07 +08:00
ssongliu f54ac59ff7 fix: Modify domain binding and authorization IP priority logic (#9977) 2025-08-13 15:40:42 +08:00
蘭 db44204c1a fix: Fix the display issue in the mobile task center (#9976) 2025-08-13 15:40:27 +08:00
蘭 ec01aef71a fix: Fix .tgz file decompression exception issue (#9974)
#9927
2025-08-13 07:38:51 +00:00
CityFun b706774b5b feat: Add quick jump to website directory button (#9973)
Refs https://github.com/1Panel-dev/1Panel/issues/3945
2025-08-13 07:36:50 +00:00
Snrat 9eee2d6b6f 添加启用HSTS子域名选项 (#9971)
* Add HSTS Subdomains

* Fix Prettier Error
2025-08-13 09:55:48 +08:00
KOMATA c9d4e2a4b5 fix: Optimize Nginx configure settings (#9962)
* fix: remove unnecessary proxy header settings to simplify default Nginx config

* fix: refactor directive appending to use AppendDirectives method in Block

* fix: migrate proxy header setting to UpdateRootProxy
2025-08-13 09:53:44 +08:00
CityFun 4c4df5cbb0 feat: Modify the regular expression for website alias (#9969) 2025-08-12 10:42:48 +00:00
ssongliu cb69128838 feat: Optimize compose navigation logic (#9968)
Refs #9959
2025-08-12 10:40:48 +00:00
ssongliu 81af207dc5 feat: Add right-click context menu support for table component (#9967) 2025-08-12 10:38:49 +00:00
CityFun 5e9c43fc71 style: Download progress adapted for dark mode (#9965)
Refs https://github.com/1Panel-dev/1Panel/issues/9926
2025-08-12 09:30:49 +00:00
CityFun fb44e3c1e2 fix: Fix the issue where SSH sessions keep loading indefinitely (#9964)
Refs https://github.com/1Panel-dev/1Panel/issues/9943
2025-08-12 09:28:48 +00:00
CityFun 7e23ae126a feat: Exit file upload with added prompt (#9963)
Refs https://github.com/1Panel-dev/1Panel/issues/9736
2025-08-12 07:40:47 +00:00
ssongliu 7e229c05e7 fix: Fix clamdscan timeout issues (#9961)
Refs #9626
2025-08-12 03:20:45 +00:00
CityFun 764fc8fec4 fix: Fixed the issue where IPv6 would turn off after favoriting a website. (#9946)
Refs https://github.com/1Panel-dev/1Panel/issues/9945
2025-08-12 02:20:48 +00:00
ssongliu ad17918c84 fix: Add custom execution cycle hint for cronjob (#9952) 2025-08-12 10:20:03 +08:00
ssongliu d7013727b9 fix: Add ctrl+c support in container terminal (#9951)
Refs #9125
2025-08-11 10:30:44 +00:00
ssongliu 6a8bd11fc9 feat: Add container orchestration navigation in app store (#9950)
Refs #8431
2025-08-11 10:28:44 +00:00
ssongliu 4df0c3e461 feat: Optimize node synchronization logic (#9947)
Refs #9691
2025-08-11 10:26:45 +00:00
CityFun f8f47b51e9 feat: Configure restart policy for application installation support (#9953)
Refs https://github.com/1Panel-dev/1Panel/issues/9895
2025-08-11 10:16:44 +00:00
CityFun b735178fcd feat: Restrict special characters in website aliases (#9944)
Refs https://github.com/1Panel-dev/1Panel/issues/9909
2025-08-11 10:14:44 +00:00
chiichen 1759c2c81e feat: support riscv64 (#9925)
* feat: add riscv64

* feat: correct arch related codes
2025-08-09 18:00:56 +08:00
wangdage12andfanbook-wangdage 2be7054b40 fix: Add margin to "View comparison" button and fix "Use custom version" button color issue (#9905)
* Tweak button spacing and fix color issue on App upgrade comparison pages

* update button styles and replace inline margin with utility class

---------

Co-authored-by: fanbook-wangdage <124357765+fanbook-wangdage@users.noreply.github.com>
2025-08-09 17:57:32 +08:00
CityFun c77d2f7dd6 fix: Fixed issue with create supervisorConfig failed in php runtime (#9919) 2025-08-08 20:29:34 +08:00
CityFun 3256ac9f44 feat: Remove database and add website association prompt (#9918) 2025-08-08 20:29:13 +08:00
ssongliu b156ef9476 feat: Support batch upgrades for container images (#9915) 2025-08-08 20:29:00 +08:00
蘭 c49c16cbb5 fix: Fix errors in creating cron job and clam alerts (#9910) 2025-08-08 20:28:35 +08:00
CityFun 2b7ec9ffe1 feat: Reverse proxy supports configuring server caching (#9914) 2025-08-08 16:59:49 +08:00
CityFun c2ad006a19 fix: Resolve the issue of missing tags in the synchronized app store (#9913) 2025-08-08 16:59:35 +08:00
ssongliu c12bab477c fix: Optimize toolbox cache cleaning functionality (#9902)
Refs #6550 #6582
2025-08-07 13:58:39 +00:00
CityFun fe39545cf6 feat: add fpm status for PHP runtime (#9891) 2025-08-07 13:56:40 +00:00
KOMATA 39d3b819fb fix: improve location retrieval in OperateProxy to handle modified nginx configs (#9901) 2025-08-07 21:49:57 +08:00
CityFun 04a0e15899 feat: Add a copy button to application parameters (#9892)
Refs https://github.com/1Panel-dev/1Panel/issues/2998
2025-08-07 13:44:38 +00:00
ssongliu 3dc81d85e9 feat: Optimize backup filename format for cronjob (#9890)
Refs #4673
2025-08-07 13:42:38 +00:00
ssongliu 5a8ddde495 feat: Support export for ssh logs (#9889)
Refs #3496
2025-08-07 13:40:39 +00:00
CityFun 4e6f872c02 feat: add root config for proxy website (#9881) 2025-08-07 21:39:46 +08:00
CityFun e76836f394 feat: Add concurrency configuration for PHP performance optimization (#9877) 2025-08-07 21:39:33 +08:00
CityFun 9fed5f4bb8 fix: Fixed issue with database locked (#9898)
Refs https://github.com/1Panel-dev/1Panel/issues/9896
2025-08-07 09:30:37 +00:00
ssongliu b3a55ea44d fix: Fix menu display exception issue (#9883) 2025-08-06 22:00:31 +08:00
ssongliu 270411f7fd fix: Fix menu display exception issue (#9882) 2025-08-06 16:44:54 +08:00
KOMATA 3c01198e61 fix: Improve config handling for proxy configuration in GetProxies (#9879) 2025-08-06 16:02:27 +08:00
ssongliu 483b47ad63 fix: Fix node ip switch exception issue (#9874) 2025-08-06 12:12:19 +08:00
ssongliu b10de66ed3 style: Update list styling for cronjob (#9872) 2025-08-06 10:30:37 +08:00
ssongliu 305eff048c fix: Fix the problem of initializing planned task groups (#9870) 2025-08-05 23:15:03 +08:00
ssongliu da7968d364 fix: Fix firewall routing jump exception issue (#9869) 2025-08-05 22:10:12 +08:00
ssongliu d3e2c8e9d3 fix: Add remote database connection timeout record (#9868) 2025-08-05 11:15:00 +00:00
ssongliu e3a9f2dfb7 fix: Fix PostgreSQL database recovery failures (#9867) 2025-08-05 18:21:18 +08:00
CityFun 3c967f8906 fix: Fix the issue where the list keeps loading when autostart = false is set in the Supervisor configuration. (#9866) 2025-08-05 10:20:59 +00:00
ssongliu 39bb7acdba fix: Add non-standard log filtering for SSH (#9865) 2025-08-05 17:54:41 +08:00
蘭 a30066989b fix: Reset data before copying files (#9864) 2025-08-05 08:44:59 +00:00
CityFun 0c30dfa092 style: Adjust the style of task logs (#9863) 2025-08-05 16:37:04 +08:00
ssongliu 7a44493d13 fix: Fix menu name display issue (#9862) 2025-08-05 08:35:00 +00:00
ssongliu 9b726d5d22 fix: Fix menu name display issue (#9861) 2025-08-05 16:24:06 +08:00
ssongliu 78e3d9e5fb fix: Enhance archive extraction command compatibility (#9860)
Refs #9856
2025-08-05 08:14:59 +00:00
CityFun d74b41a5f2 style: Adjust the column width of the PHP runtime status (#9858) 2025-08-05 07:56:59 +00:00
CityFun f122afdbec feat: Add master node IP validation when installing a cluster slave node (#9857)
#9840
2025-08-05 07:55:00 +00:00
ssongliu 37308b5c42 fix: Add custom timeout support for remote database (#9859) 2025-08-05 15:54:48 +08:00
ssongliu 9c5a6e3925 style: Adjust menu tab styling (#9855) 2025-08-05 06:52:59 +00:00
ssongliu 58b95ef2ba fix: Fix PostgreSQL database backup/restore failures (#9854) 2025-08-05 14:30:34 +08:00
CityFun f617c9d5dd fix: Fix the issue where viewing logs during certificate application results in an error. (#9853) 2025-08-05 14:30:18 +08:00
CityFun c661cc37b3 feat: Delete app while ignoring it during synchronization. (#9852) 2025-08-05 14:29:50 +08:00
ssongliu 7c49f6c79b fix: Fix redis configuration modification issues (#9848)
Refs #9832
2025-08-05 03:26:59 +00:00
蘭 12b9356c17 ref: Optimize code (#9845) 2025-08-05 02:46:59 +00:00
g0l4 d3a34293f7 fix: Move inline editorconfig comments (#9839) 2025-08-05 10:38:50 +08:00
ssongliu c131061f81 fix: Fix grouping exception issue for cronjob (#9841) 2025-08-04 22:20:06 +08:00
CityFun 6a5e1ebaa5 feat: Add OpenResty upgrade notification. (#9837) 2025-08-04 22:19:28 +08:00
ssongliu a07d016951 fix: Fix menu tab display issues (#9838) 2025-08-04 21:32:21 +08:00
CityFun 9d343a90db feat: add DenySpider Config for waf (#9835) 2025-08-04 10:06:31 +00:00
蘭 4c813be09d style: file code editor style (#9834)
refs #9781
2025-08-04 10:04:29 +00:00
CityFun d48166f546 fix: Add autostart configuration for supervisor (#9831) 2025-08-04 14:20:53 +08:00
ssongliu 0f138c1b7c fix: Optimize SSH key synchronization (#9833) 2025-08-04 06:18:29 +00:00
ssongliu 8542323c0b fix: Add custom timeout support for remote database (#9830) 2025-08-04 13:40:35 +08:00
CityFun 00f103c66a fix: Fixed issue with where runtime website fails to start after being stopped (#9827) 2025-08-04 13:40:21 +08:00
ssongliu 430c75e7ed fix: Fix redis cluster connection info modification issue (#9799)
Refs #9798
2025-08-01 10:47:23 +00:00
hang 67ffb5e938 feat: support autocomplete for login form fields to enhance password manager compatibility (#9794) 2025-08-01 16:28:20 +08:00
ssongliu 9c292bebf0 fix: Optimize container cleanup mechanism (#9795)
Refs #7444

涉及到容器、镜像、网络、存储卷、构建缓存清理
2025-08-01 08:07:24 +00:00
ssongliu ba4307c1dc fix: Optimize backup file upload display (#9797) 2025-08-01 08:03:23 +00:00
蘭 b8b2041a0c fix: Fix Docker proxy error issue (#9790)
refs #9780
2025-08-01 08:01:24 +00:00
蘭 c45bb60b50 fix: Fix file copy overwrite error (#9796) 2025-08-01 16:00:11 +08:00
ssongliu fba46be47a fix: Fix overview page refresh issue (#9785)
Refs #9756 #9769
2025-08-01 02:47:22 +00:00
ssongliu be0a460935 feat: Optimize SSH key management (#9782) 2025-08-01 02:43:20 +00:00
CityFun c0a041d024 feat: upgrade github.com/docker/docker version to v28.3.3 (#9779) 2025-07-31 19:13:41 +08:00
ssongliu 2ce5ad82ce fix: Fix SSH SDK timeout and disconnection issues (#9776) 2025-07-31 17:22:19 +08:00
CityFun 50dcb6c7e4 fix: Fix the issue where certificate deletion fails due to network issues (#9775) 2025-07-31 17:21:33 +08:00
CityFun 017424bf7b fix: Fixed issue where syncing remote apps causes local app tags to be lost (#9774) 2025-07-31 17:21:08 +08:00
CityFun c7e7407f7c fix: Fix the issue where updating the image in the Compose file during upgrade has no effect (#9772) 2025-07-31 17:20:55 +08:00
CityFun cab42b3c7d feat: Mcp Server support streamableHttp (#9761)
Refs https://github.com/1Panel-dev/1Panel/issues/8482
2025-07-30 10:54:51 +00:00
CityFun 78f280c88f feat: Support Enter key to confirm in the delete application dialog (#9758)
Refs https://github.com/1Panel-dev/1Panel/issues/9754
2025-07-30 10:52:52 +00:00
CityFun 22f5a975c3 perf: Optimize website log reading (#9755)
Refs https://github.com/1Panel-dev/1Panel/issues/9383
2025-07-30 10:50:52 +00:00
CityFun 90ba036e39 feat: Restrict deletion of certificates that are being applied for. (#9753)
Refs https://github.com/1Panel-dev/1Panel/issues/9737#issuecomment-3134445730
2025-07-30 10:48:52 +00:00
CityFun d1e18e43ee prompt: Optimize the timeout prompt when pulling application during installation (#9757) 2025-07-30 18:48:43 +08:00
ssongliu a801140a82 feat: support group management for cronjob (#9735)
Refs #9258
2025-07-30 04:08:50 +00:00
CityFun 951ab2502e fix: Resolve the issue of ignoring MySQL failures (#9734)
Refs https://github.com/1Panel-dev/1Panel/issues/9718
2025-07-30 04:06:51 +00:00
CityFun 0316cb0662 feat: Add HTTPS fallback prevention configuration in OpenResty (#9703) 2025-07-30 04:04:51 +00:00
CityFun 04b9cbd87a fix: Fixed issue with recover postgres-cluster database failed (#9732) 2025-07-29 16:19:39 +08:00
ssongliu 4565c5b300 fix: Fix fatabase backup cronjobimport failures (#9731) 2025-07-29 16:03:55 +08:00
ssongliu bf9a9bf17a fix: Fix execution refresh issues for cronjob (#9728) 2025-07-29 14:51:34 +08:00
ssongliu 70df4dc6c4 fix: Fix import failures for cronjob (#9727) 2025-07-29 14:32:25 +08:00
ssongliu ed3e738fff fix: Fix import issues of shell cronjob (#9726) 2025-07-29 04:09:49 +00:00
ssongliu bc376ec495 fix: script-type scheduled task import/export issues (#9713) 2025-07-28 19:04:35 +08:00
Snrat 40314e9079 Fix Issue with Podman Unable to Change PHP Runtime in Website Page (#9711)
* Update container.go

* Update website.go
2025-07-28 18:34:05 +08:00
ssongliu 82f978c683 fix: Add file upload restrictions for cronjob (#9710) 2025-07-28 18:33:15 +08:00
CityFun ed288368ea fix: Fixed issue with recover mysql-cluster with backup files (#9707) 2025-07-28 18:01:05 +08:00
sheng.wang db8fa144eb fix: Fix crash when adding custom spec if specs is null (#9708) 2025-07-28 18:00:35 +08:00
ssongliu 57b76d97ec fix: Optimize task log tracking mechanism (#9704) 2025-07-28 17:12:42 +08:00
蘭 28d8210ce6 fix: add nil checks for transport.DialContext (#9702) 2025-07-28 17:02:46 +08:00
ssongliu 80ce60c56c fix: Fix port forwarding error issues (#9700)
Refs #9658
2025-07-28 08:29:40 +00:00
Snrat 2758ab07dc Fix Issue with Podman Unable to Create PHP Runtime Website (#9697)
* Update website.go

* Update container.go
2025-07-28 15:38:27 +08:00
CityFun 0828804de2 fix: Fix the issue where the host IP is shown as 127.0.0.1 after enabling 'allowPort' in the application parameter settings. (#9699) 2025-07-28 15:36:25 +08:00
ssongliu 134b17d2f0 fix: Resolve certificate validate failure Issues (#9698) 2025-07-28 15:36:10 +08:00
ssongliu 3af254cdda fix: Add lightweight object storage compatibility for COS backup acco… (#9673)
Refs #9494
2025-07-25 11:07:36 +00:00
蘭 f3d3d54722 feat: Customize login page buttons and link colors (#9671)
refs  #9061
2025-07-25 11:05:37 +00:00
CityFun fa419809f6 fix: Fixed issue with create website with openresty defaultHttpsPort (#9672) 2025-07-25 19:04:19 +08:00
CityFun da606826c6 fix: Fixed Changing the runtime container name may cause it to fail to start (#9670)
Refs https://github.com/1Panel-dev/1Panel/issues/9666
2025-07-25 11:03:35 +00:00
ssongliu b3d9cc156a fix: Optimize filename generation for directory backup (#9669)
Refs #9433
2025-07-25 09:21:35 +00:00
ssongliu ba03d26b2c fix: Optimize cronjob report loading (#9665)
Refs #9412
2025-07-25 08:35:36 +00:00
ssongliu 40f02889fe feat: Add import/export support for cronjob (#9663)
Refs #5157
2025-07-25 07:15:35 +00:00
CityFun 4f8fb26d89 feat: Adapt default website domain to non-standard OpenResty port during creation (#9664) 2025-07-25 15:15:33 +08:00
ssongliu 0ca11e2b88 fix: Fix node switching style issues (#9641) 2025-07-24 11:30:08 +08:00
ssongliu 0daf0d690c fix: Resolve deleted node status display issue (#9633) 2025-07-24 10:23:45 +08:00
440 changed files with 14450 additions and 5750 deletions
+2
View File
@@ -32,6 +32,7 @@ builds:
- arm
- ppc64le
- s390x
- riscv64
hooks:
post:
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
@@ -57,6 +58,7 @@ builds:
- arm
- ppc64le
- s390x
- riscv64
hooks:
post:
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
+2 -2
View File
@@ -466,12 +466,12 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
}
switch req.Type {
case "mysql", "mariadb":
case "mysql", "mariadb", constant.AppMysqlCluster:
if err := backupService.MysqlRecoverByUpload(req); err != nil {
helper.InternalServer(c, err)
return
}
case constant.AppPostgresql:
case constant.AppPostgresql, constant.AppPostgresqlCluster:
if err := backupService.PostgresqlRecoverByUpload(req); err != nil {
helper.InternalServer(c, err)
return
+21 -5
View File
@@ -57,7 +57,7 @@ func (b *BaseApi) LoadContainerUsers(c *gin.Context) {
// @Summary List containers
// @Accept json
// @Produce json
// @Success 200 {array} string
// @Success 200 {array} dto.ContainerOptions
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/list [post]
@@ -65,6 +65,23 @@ func (b *BaseApi) ListContainer(c *gin.Context) {
helper.SuccessWithData(c, containerService.List())
}
// @Tags Container
// @Summary List containers by image
// @Accept json
// @Produce json
// @Success 200 {array} dto.ContainerOptions
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/list/byimage [post]
func (b *BaseApi) ListContainerByImage(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
helper.SuccessWithData(c, containerService.ListByImage(req.Name))
}
// @Tags Container
// @Summary Load containers status
// @Accept json
@@ -315,7 +332,7 @@ func (b *BaseApi) ContainerUpgrade(c *gin.Context) {
// @Summary Clean container
// @Accept json
// @Param request body dto.ContainerPrune true "request"
// @Success 200 {object} dto.ContainerPruneReport
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/prune [post]
@@ -326,12 +343,11 @@ func (b *BaseApi) ContainerPrune(c *gin.Context) {
return
}
report, err := containerService.Prune(req)
if err != nil {
if err := containerService.Prune(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, report)
helper.Success(c)
}
// @Tags Container
+67 -24
View File
@@ -1,6 +1,8 @@
package v2
import (
"net/http"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
@@ -53,6 +55,49 @@ func (b *BaseApi) LoadCronjobInfo(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Tags Cronjob
// @Summary Export cronjob list
// @Accept json
// @Param request body dto.OperateByIDs true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /cronjobs/export [post]
func (b *BaseApi) ExportCronjob(c *gin.Context) {
var req dto.OperateByIDs
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
content, err := cronjobService.Export(req)
if err != nil {
helper.InternalServer(c, err)
return
}
http.ServeContent(c.Writer, c.Request, "", time.Now(), strings.NewReader(content))
}
// @Tags Cronjob
// @Summary Import cronjob list
// @Accept json
// @Param request body dto.CronjobImport true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /cronjobs/import [post]
func (b *BaseApi) ImportCronjob(c *gin.Context) {
var req dto.CronjobImport
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := cronjobService.Import(req.Cronjobs); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Cronjob
// @Summary Load script options
// @Success 200 {array} dto.ScriptOptions
@@ -225,6 +270,28 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
helper.Success(c)
}
// @Tags Cronjob
// @Summary Update cronjob group
// @Accept json
// @Param request body dto.ChangeGroup true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /cronjobs/group/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"更新计划任务分组 [name]","formatEN":"update cronjob group [name]"}
func (b *BaseApi) UpdateCronjobGroup(c *gin.Context) {
var req dto.ChangeGroup
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := cronjobService.UpdateGroup(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Cronjob
// @Summary Update cronjob status
// @Accept json
@@ -247,30 +314,6 @@ func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
helper.Success(c)
}
// @Tags Cronjob
// @Summary Download cronjob records
// @Accept json
// @Param request body dto.CronjobDownload true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /cronjobs/download [post]
// @x-panel-log {"bodyKeys":["recordID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"recordID","isList":false,"db":"job_records","output_column":"file","output_value":"file"}],"formatZH":"下载计划任务记录 [file]","formatEN":"download the cronjob record [file]"}
func (b *BaseApi) TargetDownload(c *gin.Context) {
var req dto.CronjobDownload
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
filePath, err := cronjobService.Download(req)
if err != nil {
helper.InternalServer(c, err)
return
}
c.File(filePath)
}
// @Tags Cronjob
// @Summary Handle cronjob once
// @Accept json
+5 -6
View File
@@ -9,14 +9,14 @@ import (
// @Tags Container Image
// @Summary Page images
// @Accept json
// @Param request body dto.SearchWithPage true "request"
// @Param request body dto.PageImage true "request"
// @Produce json
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/image/search [post]
func (b *BaseApi) SearchImage(c *gin.Context) {
var req dto.SearchWithPage
var req dto.PageImage
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
@@ -138,7 +138,7 @@ func (b *BaseApi) ImagePush(c *gin.Context) {
// @Summary Delete image
// @Accept json
// @Param request body dto.BatchDelete true "request"
// @Success 200 {object} dto.ContainerPruneReport
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/image/remove [post]
@@ -149,13 +149,12 @@ func (b *BaseApi) ImageRemove(c *gin.Context) {
return
}
data, err := imageService.ImageRemove(req)
if err != nil {
if err := imageService.ImageRemove(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
helper.Success(c)
}
// @Tags Container Image
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/net"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/net"
)
// @Tags Monitor
+36
View File
@@ -157,3 +157,39 @@ func (b *BaseApi) GetNginxModules(c *gin.Context) {
}
helper.SuccessWithData(c, modules)
}
// @Tags OpenResty
// @Summary Operate default HTTPs
// @Accept json
// @Param request body request.NginxOperateReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /openresty/https [post]
func (b *BaseApi) OperateDefaultHTTPs(c *gin.Context) {
var req request.NginxOperateReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := nginxService.OperateDefaultHTTPs(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags OpenResty
// @Summary Get default HTTPs status
// @Success 200 {object} response.NginxConfigRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /openresty/https [get]
func (b *BaseApi) GetDefaultHTTPsStatus(c *gin.Context) {
res, err := nginxService.GetDefaultHttpsStatus()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
+22
View File
@@ -555,3 +555,25 @@ func (b *BaseApi) UpdateRuntimeRemark(c *gin.Context) {
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Get PHP runtime status
// @Accept json
// @Param id path integer true "request"
// @Success 200 {object} map[string]interface{}
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/fpm/status/:id [get]
func (b *BaseApi) GetFPMStatus(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.BadRequest(c, err)
return
}
data, err := runtimeService.GetFPMStatus(id)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
+1 -1
View File
@@ -113,7 +113,7 @@ func loadLocalConn() (*ssh.SSHClient, error) {
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
}
if _, err := os.Stat(itemPath); err != nil {
_ = sshService.GenerateSSH(dto.GenerateSSH{EncryptionMode: "ed25519", Name: "_1panel"})
_ = sshService.CreateRootCert(dto.CreateRootCert{EncryptionMode: "ed25519", Name: "id_ed25519_1panel", Description: "1Panel Terminal"})
}
privateKey, _ := os.ReadFile(itemPath)
+1 -1
View File
@@ -36,7 +36,7 @@ func (b *BaseApi) CreateSnapshot(c *gin.Context) {
return
}
if err := snapshotService.SnapshotCreate(nil, req, 0, 0, 0); err != nil {
if err := snapshotService.SnapshotCreate(nil, req, 0, 3); err != nil {
helper.InternalServer(c, err)
return
}
+106 -15
View File
@@ -1,6 +1,8 @@
package v2
import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/gin-gonic/gin"
@@ -90,19 +92,58 @@ func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
// @Tags SSH
// @Summary Generate host SSH secret
// @Accept json
// @Param request body dto.GenerateSSH true "request"
// @Param request body dto.CreateRootCert true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/generate [post]
// @Router /hosts/ssh/cert [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
func (b *BaseApi) GenerateSSH(c *gin.Context) {
var req dto.GenerateSSH
func (b *BaseApi) CreateRootCert(c *gin.Context) {
var req dto.CreateRootCert
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.PassPhrase) != 0 {
passPhrase, err := base64.StdEncoding.DecodeString(req.PassPhrase)
if err != nil {
helper.BadRequest(c, err)
return
}
req.PassPhrase = string(passPhrase)
}
if len(req.PrivateKey) != 0 {
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
if err != nil {
helper.BadRequest(c, err)
return
}
req.PrivateKey = string(privateKey)
}
if len(req.PublicKey) != 0 {
publicKey, err := base64.StdEncoding.DecodeString(req.PublicKey)
if err != nil {
helper.BadRequest(c, err)
return
}
req.PublicKey = string(publicKey)
}
if err := sshService.GenerateSSH(req); err != nil {
if err := sshService.CreateRootCert(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags SSH
// @Summary Sycn host SSH secret
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/cert/sync [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"同步 SSH 密钥 ","formatEN":"sync SSH secret"}
func (b *BaseApi) SyncRootCert(c *gin.Context) {
if err := sshService.SyncRootCert(); err != nil {
helper.InternalServer(c, err)
return
}
@@ -112,30 +153,55 @@ func (b *BaseApi) GenerateSSH(c *gin.Context) {
// @Tags SSH
// @Summary Load host SSH secret
// @Accept json
// @Param request body dto.GenerateLoad true "request"
// @Success 200 {string} secret
// @Param request body dto.SearchWithPage true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/secret [post]
func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
var req dto.GenerateLoad
// @Router /hosts/ssh/cert/search [post]
func (b *BaseApi) SearchRootCert(c *gin.Context) {
var req dto.SearchWithPage
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := sshService.LoadSSHSecret(req.EncryptionMode)
total, data, err := sshService.SearchRootCerts(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: data,
})
}
// @Tags SSH
// @Summary Delete host SSH secret
// @Accept json
// @Param request body dto.ForceDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/cert/delete [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除 SSH 密钥 ","formatEN":"delete SSH secret"}
func (b *BaseApi) DeleteRootCert(c *gin.Context) {
var req dto.ForceDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := sshService.DeleteRootCerts(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags SSH
// @Summary Load host SSH logs
// @Accept json
// @Param request body dto.SearchSSHLog true "request"
// @Success 200 {object} dto.SSHLog
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/log [post]
@@ -145,12 +211,37 @@ func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
return
}
data, err := sshService.LoadLog(c, req)
total, data, err := sshService.LoadLog(c, req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: data,
})
}
// @Tags SSH
// @Summary Export host SSH logs
// @Accept json
// @Param request body dto.SearchSSHLog true "request"
// @Success 200 {string} path
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/log/export [post]
func (b *BaseApi) ExportSSHLogs(c *gin.Context) {
var req dto.SearchSSHLog
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
tmpFile, err := sshService.ExportLog(c, req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, tmpFile)
}
// @Tags SSH
+20
View File
@@ -1121,3 +1121,23 @@ func (b *BaseApi) OperateCrossSiteAccess(c *gin.Context) {
}
helper.Success(c)
}
// @Tags Website
// @Summary Exec Composer
// @Accept json
// @Param request body request.ExecComposerReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/exec/composer [post]
func (b *BaseApi) ExecComposer(c *gin.Context) {
var req request.ExecComposerReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.ExecComposer(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
+13
View File
@@ -1,6 +1,7 @@
package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"net/http"
"net/url"
"reflect"
@@ -245,3 +246,15 @@ func (b *BaseApi) DownloadWebsiteSSL(c *gin.Context) {
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
http.ServeContent(c.Writer, c.Request, info.Name(), info.ModTime(), file)
}
func (b *BaseApi) ImportMasterSSL(c *gin.Context) {
var req model.WebsiteSSL
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteSSLService.ImportMasterSSL(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
+31 -28
View File
@@ -36,40 +36,43 @@ type AlertSearch struct {
}
type AlertDTO struct {
ID uint `json:"id"`
Type string `json:"type"`
Cycle uint `json:"cycle"`
Count uint `json:"count"`
Method string `json:"method"`
Title string `json:"title"`
Project string `json:"project"`
Status string `json:"status"`
SendCount uint `json:"sendCount"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID uint `json:"id"`
Type string `json:"type"`
Cycle uint `json:"cycle"`
Count uint `json:"count"`
Method string `json:"method"`
Title string `json:"title"`
Project string `json:"project"`
Status string `json:"status"`
SendCount uint `json:"sendCount"`
AdvancedParams string `json:"advancedParams"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type AlertCreate struct {
Type string `json:"type" validate:"required"`
Cycle uint `json:"cycle"`
Count uint `json:"count"`
Method string `json:"method" validate:"required"`
Title string `json:"title"`
Project string `json:"project"`
Status string `json:"status"`
SendCount uint `json:"sendCount"`
Type string `json:"type" validate:"required"`
Cycle uint `json:"cycle"`
Count uint `json:"count"`
Method string `json:"method" validate:"required"`
Title string `json:"title"`
Project string `json:"project"`
Status string `json:"status"`
SendCount uint `json:"sendCount"`
AdvancedParams string `json:"advancedParams"`
}
type AlertUpdate struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type"`
Cycle uint `json:"cycle"`
Count uint `json:"count"`
Method string `json:"method"`
Title string `json:"title"`
Project string `json:"project"`
Status string `json:"status"`
SendCount uint `json:"sendCount"`
ID uint `json:"id" validate:"required"`
Type string `json:"type"`
Cycle uint `json:"cycle"`
Count uint `json:"count"`
Method string `json:"method"`
Title string `json:"title"`
Project string `json:"project"`
Status string `json:"status"`
SendCount uint `json:"sendCount"`
AdvancedParams string `json:"advancedParams"`
}
type DeleteRequest struct {
+8
View File
@@ -28,6 +28,9 @@ type OperationWithName struct {
type OperateByID struct {
ID uint `json:"id" validate:"required"`
}
type OperateByIDs struct {
IDs []uint `json:"ids"`
}
type Operate struct {
Operation string `json:"operation" validate:"required"`
@@ -76,3 +79,8 @@ type ForceDelete struct {
IDs []uint `json:"ids"`
ForceDelete bool `json:"forceDelete"`
}
type ChangeGroup struct {
ID uint `json:"id" validate:"required"`
GroupID uint `json:"groupID" validate:"required"`
}
+20 -21
View File
@@ -107,10 +107,10 @@ type ContainerCreateByCommand struct {
}
type ContainerUpgrade struct {
TaskID string `json:"taskID"`
Name string `json:"name" validate:"required"`
Image string `json:"image" validate:"required"`
ForcePull bool `json:"forcePull"`
TaskID string `json:"taskID"`
Names []string `json:"names" validate:"required"`
Image string `json:"image" validate:"required"`
ForcePull bool `json:"forcePull"`
}
type ContainerListStats struct {
@@ -174,15 +174,11 @@ type ContainerCommit struct {
}
type ContainerPrune struct {
TaskID string `json:"taskID"`
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network buildcache"`
WithTagAll bool `json:"withTagAll"`
}
type ContainerPruneReport struct {
DeletedNumber int `json:"deletedNumber"`
SpaceReclaimed int `json:"spaceReclaimed"`
}
type Network struct {
ID string `json:"id"`
Name string `json:"name"`
@@ -227,20 +223,22 @@ type VolumeCreate struct {
}
type BatchDelete struct {
Force bool `json:"force"`
Names []string `json:"names" validate:"required"`
TaskID string `json:"taskID"`
Force bool `json:"force"`
Names []string `json:"names" validate:"required"`
}
type ComposeInfo struct {
Name string `json:"name"`
CreatedAt string `json:"createdAt"`
CreatedBy string `json:"createdBy"`
ContainerNumber int `json:"containerNumber"`
ConfigFile string `json:"configFile"`
Workdir string `json:"workdir"`
Path string `json:"path"`
Containers []ComposeContainer `json:"containers"`
Env []string `json:"env"`
Name string `json:"name"`
CreatedAt string `json:"createdAt"`
CreatedBy string `json:"createdBy"`
ContainerCount int `json:"containerCount"`
RunningCount int `json:"runningCount"`
ConfigFile string `json:"configFile"`
Workdir string `json:"workdir"`
Path string `json:"path"`
Containers []ComposeContainer `json:"containers"`
Env []string `json:"env"`
}
type ComposeContainer struct {
ContainerID string `json:"containerID"`
@@ -260,8 +258,9 @@ type ComposeCreate struct {
type ComposeOperation struct {
Name string `json:"name" validate:"required"`
Path string `json:"path"`
Operation string `json:"operation" validate:"required,oneof=up start stop down delete"`
Operation string `json:"operation" validate:"required,oneof=up start restart stop down delete"`
WithFile bool `json:"withFile"`
Force bool `josn:"force"`
}
type ComposeUpdate struct {
Name string `json:"name" validate:"required"`
+58 -8
View File
@@ -6,9 +6,10 @@ import (
type PageCronjob struct {
PageInfo
Info string `json:"info"`
OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
Info string `json:"info"`
GroupIDs []uint `json:"groupIDs"`
OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
}
type CronjobSpec struct {
@@ -19,6 +20,7 @@ type CronjobOperate struct {
ID uint `json:"id"`
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"`
Spec string `json:"spec" validate:"required"`
@@ -63,11 +65,6 @@ type CronjobUpdateStatus struct {
Status string `json:"status" validate:"required"`
}
type CronjobDownload struct {
RecordID uint `json:"recordID" validate:"required"`
BackupAccountID uint `json:"backupAccountID" validate:"required"`
}
type CronjobClean struct {
IsDelete bool `json:"isDelete"`
CleanData bool `json:"cleanData"`
@@ -85,6 +82,7 @@ type CronjobInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"`
Spec string `json:"spec"`
@@ -123,6 +121,58 @@ type CronjobInfo struct {
AlertCount uint `json:"alertCount"`
}
type CronjobImport struct {
Cronjobs []CronjobTrans `json:"cronjobs"`
}
type CronjobTrans struct {
Name string `json:"name"`
Type string `json:"type"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"`
Spec string `json:"spec"`
Executor string `json:"executor"`
ScriptMode string `json:"scriptMode"`
Script string `json:"script"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
User string `json:"user"`
URL string `json:"url"`
ScriptName string `json:"scriptName"`
Apps []TransHelper `json:"apps"`
Websites []string `json:"websites"`
DBType string `json:"dbType"`
DBNames []TransHelper `json:"dbName"`
ExclusionRules string `json:"exclusionRules"`
IsDir bool `json:"isDir"`
SourceDir string `json:"sourceDir"`
RetainCopies uint64 `json:"retainCopies"`
RetryTimes uint `json:"retryTimes"`
Timeout uint `json:"timeout"`
IgnoreErr bool `json:"ignoreErr"`
SnapshotRule SnapshotTransHelper `json:"snapshotRule"`
Secret string `json:"secret"`
SourceAccounts []string `json:"sourceAccounts"`
DownloadAccount string `json:"downloadAccount"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
AlertMethod string `json:"alertMethod"`
}
type TransHelper struct {
Name string `json:"name"`
DetailName string `json:"detailName"`
}
type SnapshotTransHelper struct {
WithImage bool `json:"withImage"`
IgnoreApps []TransHelper `json:"ignoreApps"`
}
type ScriptOptions struct {
ID uint `json:"id"`
Name string `json:"name"`
+10 -2
View File
@@ -4,14 +4,14 @@ import "time"
// common
type DBConfUpdateByFile struct {
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster"`
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster redis-cluster"`
Database string `json:"database" validate:"required"`
File string `json:"file"`
}
type ChangeDBInfo struct {
ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql mysql-cluster postgresql-cluster"`
Type string `json:"type" validate:"required,oneof=mysql mariadb postgresql redis mysql-cluster postgresql-cluster redis-cluster"`
Database string `json:"database" validate:"required"`
Value string `json:"value" validate:"required"`
}
@@ -260,6 +260,7 @@ type DatabaseInfo struct {
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Timeout uint `json:"timeout"`
Description string `json:"description"`
}
@@ -295,6 +296,7 @@ type DatabaseCreate struct {
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Timeout uint `json:"timeout"`
Description string `json:"description"`
}
@@ -313,6 +315,7 @@ type DatabaseUpdate struct {
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Timeout uint `json:"timeout"`
Description string `json:"description"`
}
@@ -326,3 +329,8 @@ type LoadRedisStatus struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
}
type DBResource struct {
Type string `json:"type"`
Name string `json:"name"`
}
+3 -9
View File
@@ -60,26 +60,20 @@ type PostgresqlPrivileges struct {
type PostgresqlLoadDB struct {
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=postgresql"`
Type string `json:"type" validate:"required,oneof=postgresql postgresql-cluster"`
Database string `json:"database" validate:"required"`
}
type PostgresqlDBDeleteCheck struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required,oneof=postgresql"`
Type string `json:"type" validate:"required,oneof=postgresql postgresql-cluster"`
Database string `json:"database" validate:"required"`
}
type PostgresqlDBDelete struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required,oneof=postgresql"`
Type string `json:"type" validate:"required,oneof=postgresql postgresql-cluster"`
Database string `json:"database" validate:"required"`
ForceDelete bool `json:"forceDelete"`
DeleteBackup bool `json:"deleteBackup"`
}
type PostgresqlConfUpdateByFile struct {
Type string `json:"type" validate:"required,oneof=postgresql mariadb"`
Database string `json:"database" validate:"required"`
File string `json:"file"`
}
+8
View File
@@ -0,0 +1,8 @@
package dto
type LogFileRes struct {
Lines []string `json:"lines"`
IsEndOfFile bool `json:"isEndOfFile"`
TotalPages int `json:"totalPages"`
TotalLines int `json:"totalLines"`
}
+8 -1
View File
@@ -2,12 +2,19 @@ package dto
import "time"
type PageImage struct {
PageInfo
Name string `json:"name"`
OrderBy string `json:"orderBy" validate:"required,oneof=size tags createdAt isUsed"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
}
type ImageInfo struct {
ID string `json:"id"`
CreatedAt time.Time `json:"createdAt"`
IsUsed bool `json:"isUsed"`
Tags []string `json:"tags"`
Size string `json:"size"`
Size int64 `json:"size"`
}
type ImageLoad struct {
+1
View File
@@ -40,6 +40,7 @@ type AppContainerConfig struct {
WebUI string `json:"webUI"`
Type string `json:"type"`
SpecifyIP string `json:"specifyIP"`
RestartPolicy string `json:"restartPolicy" validate:"omitempty,oneof=always unless-stopped no on-failure"`
}
type AppInstalledSearch struct {
+1
View File
@@ -33,6 +33,7 @@ type SupervisorProcessConfig struct {
Dir string `json:"dir"`
Numprocs string `json:"numprocs"`
AutoRestart string `json:"autoRestart"`
AutoStart string `json:"autoStart"`
}
type SupervisorProcessFileReq struct {
+11 -9
View File
@@ -9,15 +9,17 @@ type McpServerSearch struct {
}
type McpServerCreate struct {
Name string `json:"name" validate:"required"`
Command string `json:"command" validate:"required"`
Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"`
Port int `json:"port" validate:"required"`
ContainerName string `json:"containerName"`
BaseURL string `json:"baseUrl"`
SsePath string `json:"ssePath"`
HostIP string `json:"hostIP"`
Name string `json:"name" validate:"required"`
Command string `json:"command" validate:"required"`
Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"`
Port int `json:"port" validate:"required"`
ContainerName string `json:"containerName"`
BaseURL string `json:"baseUrl"`
SsePath string `json:"ssePath"`
HostIP string `json:"hostIP"`
StreamableHttpPath string `json:"streamableHttpPath"`
OutputTransport string `json:"outputTransport" validate:"required"`
}
type McpServerUpdate struct {
+4
View File
@@ -126,3 +126,7 @@ type NginxModuleUpdate struct {
Enable bool `json:"enable"`
Params string `json:"params"`
}
type NginxOperateReq struct {
Operate string `json:"operate" validate:"required,oneof=enable disable"`
}
+46 -33
View File
@@ -152,21 +152,22 @@ type WebsiteDomainDelete struct {
}
type WebsiteHTTPSOp struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Enable bool `json:"enable"`
WebsiteSSLID uint `json:"websiteSSLId"`
Type string `json:"type" validate:"oneof=existed auto manual"`
PrivateKey string `json:"privateKey"`
Certificate string `json:"certificate"`
PrivateKeyPath string `json:"privateKeyPath"`
CertificatePath string `json:"certificatePath"`
ImportType string `json:"importType"`
HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
SSLProtocol []string `json:"SSLProtocol"`
Algorithm string `json:"algorithm"`
Hsts bool `json:"hsts"`
HttpsPorts []int `json:"httpsPorts"`
Http3 bool `json:"http3"`
WebsiteID uint `json:"websiteId" validate:"required"`
Enable bool `json:"enable"`
WebsiteSSLID uint `json:"websiteSSLId"`
Type string `json:"type" validate:"oneof=existed auto manual"`
PrivateKey string `json:"privateKey"`
Certificate string `json:"certificate"`
PrivateKeyPath string `json:"privateKeyPath"`
CertificatePath string `json:"certificatePath"`
ImportType string `json:"importType"`
HttpConfig string `json:"httpConfig" validate:"oneof=HTTPSOnly HTTPAlso HTTPToHTTPS"`
SSLProtocol []string `json:"SSLProtocol"`
Algorithm string `json:"algorithm"`
Hsts bool `json:"hsts"`
HstsIncludeSubDomains bool `json:"hstsIncludeSubDomains"`
HttpsPorts []int `json:"httpsPorts"`
Http3 bool `json:"http3"`
}
type WebsiteNginxUpdate struct {
@@ -203,22 +204,24 @@ type WebsiteUpdateDirPermission struct {
}
type WebsiteProxyConfig struct {
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
Enable bool `json:"enable" `
Cache bool `json:"cache" `
CacheTime int `json:"cacheTime" `
CacheUnit string `json:"cacheUnit"`
Name string `json:"name" validate:"required"`
Modifier string `json:"modifier"`
Match string `json:"match" validate:"required"`
ProxyPass string `json:"proxyPass" validate:"required"`
ProxyHost string `json:"proxyHost" validate:"required"`
Content string `json:"content"`
FilePath string `json:"filePath"`
Replaces map[string]string `json:"replaces"`
SNI bool `json:"sni"`
ProxySSLName string `json:"proxySSLName"`
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
Enable bool `json:"enable" `
Cache bool `json:"cache" `
CacheTime int `json:"cacheTime"`
CacheUnit string `json:"cacheUnit"`
ServerCacheTime int `json:"serverCacheTime"`
ServerCacheUnit string `json:"serverCacheUnit"`
Name string `json:"name" validate:"required"`
Modifier string `json:"modifier"`
Match string `json:"match" validate:"required"`
ProxyPass string `json:"proxyPass" validate:"required"`
ProxyHost string `json:"proxyHost" validate:"required"`
Content string `json:"content"`
FilePath string `json:"filePath"`
Replaces map[string]string `json:"replaces"`
SNI bool `json:"sni"`
ProxySSLName string `json:"proxySSLName"`
}
type WebsiteProxyReq struct {
@@ -283,8 +286,8 @@ type WebsiteRealIP struct {
type ChangeDatabase struct {
WebsiteID uint `json:"websiteID" validate:"required"`
DatabaseID uint `json:"databaseID" validate:"required"`
DatabaseType string `json:"databaseType" validate:"required"`
DatabaseID uint `json:"databaseID" `
DatabaseType string `json:"databaseType" `
}
type WebsiteProxyDel struct {
@@ -296,3 +299,13 @@ type CrossSiteAccessOp struct {
WebsiteID uint `json:"websiteID" validate:"required"`
Operation string `json:"operation" validate:"required,oneof=Enable Disable"`
}
type ExecComposerReq struct {
Command string `json:"command" validate:"required"`
ExtCommand string `json:"extCommand"`
Mirror string `json:"mirror" validate:"required"`
Dir string `json:"dir" validate:"required"`
User string `json:"user" validate:"required"`
WebsiteID uint `json:"websiteID" validate:"required"`
TaskID string `json:"taskID" validate:"required"`
}
+4
View File
@@ -27,6 +27,8 @@ type WebsiteSSLCreate struct {
Nameserver2 string `json:"nameserver2"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
PushNode bool `json:"pushNode"`
Nodes string `json:"nodes"`
}
type WebsiteDNSReq struct {
@@ -105,6 +107,8 @@ type WebsiteSSLUpdate struct {
Nameserver2 string `json:"nameserver2"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
PushNode bool `json:"pushNode"`
Nodes string `json:"nodes"`
}
type WebsiteSSLUpload struct {
+1
View File
@@ -66,6 +66,7 @@ type AppInstalledCheck struct {
InstallPath string `json:"installPath"`
HttpPort int `json:"httpPort"`
HttpsPort int `json:"httpsPort"`
WebsiteDir string `json:"websiteDir"`
}
type AppDetailDTO struct {
+1 -1
View File
@@ -6,5 +6,5 @@ type AppIgnoreUpgradeDTO struct {
AppDetailID uint `json:"appDetailID"`
Scope string `json:"scope"`
Version string `json:"version"`
Icon string `json:"icon"`
Name string `json:"name"`
}
+9 -6
View File
@@ -1,8 +1,9 @@
package response
import (
"github.com/1Panel-dev/1Panel/agent/utils/files"
"time"
"github.com/1Panel-dev/1Panel/agent/utils/files"
)
type FileInfo struct {
@@ -37,11 +38,13 @@ type FileWgetRes struct {
}
type FileLineContent struct {
Content string `json:"content"`
End bool `json:"end"`
Path string `json:"path"`
Total int `json:"total"`
Lines []string `json:"lines"`
End bool `json:"end"`
Path string `json:"path"`
Total int `json:"total"`
TaskStatus string `json:"taskStatus"`
Lines []string `json:"lines"`
Scope string `json:"scope"`
TotalLines int `json:"totalLines"`
}
type FileExist struct {
+1
View File
@@ -31,6 +31,7 @@ type SupervisorProcessConfig struct {
Msg string `json:"msg"`
Status []ProcessStatus `json:"status"`
AutoRestart string `json:"autoRestart"`
AutoStart string `json:"autoStart"`
}
type ProcessStatus struct {
+4
View File
@@ -80,3 +80,7 @@ type NginxBuildConfig struct {
Mirror string `json:"mirror"`
Modules []NginxModule `json:"modules"`
}
type NginxConfigRes struct {
Https bool `json:"https"`
}
+5
View File
@@ -78,3 +78,8 @@ type PHPExtensionRes struct {
Extensions []string `json:"extensions"`
SupportExtensions []SupportExtension `json:"supportExtensions"`
}
type FpmStatusItem struct {
Key string `json:"key"`
Value interface{} `json:"value"`
}
+2
View File
@@ -37,6 +37,7 @@ type WebsiteRes struct {
ChildSites []string `json:"childSites"`
RuntimeType string `json:"runtimeType"`
Favorite bool `json:"favorite"`
IPV6 bool `json:"IPV6"`
}
type WebsiteOption struct {
@@ -64,6 +65,7 @@ type WebsiteHTTPS struct {
SSLProtocol []string `json:"SSLProtocol"`
Algorithm string `json:"algorithm"`
Hsts bool `json:"hsts"`
HstsIncludeSubDomains bool `json:"hstsIncludeSubDomains"`
HttpsPorts []int `json:"httpsPorts"`
HttpsPort string `json:"httpsPort"`
Http3 bool `json:"http3"`
+18 -9
View File
@@ -19,12 +19,27 @@ type SSHInfo struct {
PubkeyAuthentication string `json:"pubkeyAuthentication"`
PermitRootLogin string `json:"permitRootLogin"`
UseDNS string `json:"useDNS"`
CurrentUser string `json:"currentUser"`
}
type GenerateSSH struct {
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
Password string `json:"password"`
type CreateRootCert struct {
Name string `json:"name"`
Mode string `json:"mode"`
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
PassPhrase string `json:"passPhrase"`
PublicKey string `json:"publicKey"`
PrivateKey string `json:"privateKey"`
Description string `json:"description"`
}
type RootCert struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name"`
EncryptionMode string `json:"encryptionMode"`
PassPhrase string `json:"passPhrase"`
PublicKey string `json:"publicKey"`
PrivateKey string `json:"privateKey"`
Description string `json:"description"`
}
type GenerateLoad struct {
@@ -39,12 +54,6 @@ type SearchSSHLog struct {
Info string `json:"info"`
Status string `json:"Status" validate:"required,oneof=Success Failed All"`
}
type SSHLog struct {
Logs []SSHHistory `json:"logs"`
TotalCount int `json:"totalCount"`
SuccessfulCount int `json:"successfulCount"`
FailedCount int `json:"failedCount"`
}
type SSHHistory struct {
Date time.Time `json:"date"`
+20 -10
View File
@@ -3,14 +3,15 @@ package model
type Alert struct {
BaseModel
Title string `gorm:"type:varchar(256);not null" json:"title"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Cycle uint `gorm:"type:integer;not null" json:"cycle"`
Count uint `gorm:"type:integer;not null" json:"count"`
Project string `gorm:"type:varchar(64)" json:"project"`
Status string `gorm:"type:varchar(64);not null" json:"status"`
Method string `gorm:"type:varchar(64);not null" json:"method"`
SendCount uint `gorm:"type:integer" json:"sendCount"`
Title string `gorm:"type:varchar(256);not null" json:"title"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Cycle uint `gorm:"type:integer;not null" json:"cycle"`
Count uint `gorm:"type:integer;not null" json:"count"`
Project string `gorm:"type:varchar(64)" json:"project"`
Status string `gorm:"type:varchar(64);not null" json:"status"`
Method string `gorm:"type:varchar(64);not null" json:"method"`
SendCount uint `gorm:"type:integer" json:"sendCount"`
AdvancedParams string `gorm:"type:longText" json:"advancedParam"`
}
type AlertTask struct {
@@ -18,7 +19,7 @@ type AlertTask struct {
Type string `gorm:"type:varchar(64);not null" json:"type"`
Quota string `gorm:"type:varchar(64)" json:"quota"`
QuotaType string `gorm:"type:varchar(64)" json:"quotaType"`
Method string `gorm:"type:varchar(64);not null;default:'sms" json:"method"`
Method string `gorm:"type:varchar(64);not null;default:'sms'" json:"method"`
}
type AlertLog struct {
@@ -33,7 +34,7 @@ type AlertLog struct {
Message string `gorm:"type:varchar(256);" json:"message"`
RecordId uint `gorm:"type:integer;" json:"recordId"`
LicenseId string `gorm:"type:varchar(256);not null;" json:"licenseId" `
Method string `gorm:"type:varchar(64);not null;default:'sms" json:"method"`
Method string `gorm:"type:varchar(64);not null;default:'sms'" json:"method"`
}
type AlertConfig struct {
@@ -43,3 +44,12 @@ type AlertConfig struct {
Status string `gorm:"type:varchar(64);not null" json:"status"`
Config string `gorm:"type:varchar(256);not null" json:"config"`
}
type LoginLog struct {
BaseModel
IP string `json:"ip"`
Address string `json:"address"`
Agent string `json:"agent"`
Status string `json:"status"`
Message string `json:"message"`
}
+1
View File
@@ -11,6 +11,7 @@ type Cronjob struct {
Name string `gorm:"not null" json:"name"`
Type string `gorm:"not null" json:"type"`
GroupID uint `json:"groupID"`
SpecCustom bool `json:"specCustom"`
Spec string `gorm:"not null" json:"spec"`
+1
View File
@@ -18,5 +18,6 @@ type Database struct {
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Timeout uint `json:"timeout"`
Description string `json:"description"`
}
+15 -13
View File
@@ -2,17 +2,19 @@ package model
type McpServer struct {
BaseModel
Name string `json:"name"`
DockerCompose string `json:"dockerCompose"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
Message string `json:"message"`
Port int `json:"port"`
Status string `json:"status"`
Env string `json:"env"`
BaseURL string `json:"baseUrl"`
SsePath string `json:"ssePath"`
WebsiteID int `json:"websiteID"`
Dir string `json:"dir"`
HostIP string `json:"hostIP"`
Name string `json:"name"`
DockerCompose string `json:"dockerCompose"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
Message string `json:"message"`
Port int `json:"port"`
Status string `json:"status"`
Env string `json:"env"`
BaseURL string `json:"baseUrl"`
SsePath string `json:"ssePath"`
WebsiteID int `json:"websiteID"`
Dir string `json:"dir"`
HostIP string `json:"hostIP"`
StreamableHttpPath string `json:"streamableHttpPath"`
OutputTransport string `json:"outputTransport"`
}
+11
View File
@@ -0,0 +1,11 @@
package model
type RootCert struct {
BaseModel
Name string `json:"name" gorm:"not null;"`
EncryptionMode string `json:"encryptionMode"`
PassPhrase string `json:"passPhrase"`
PublicKeyPath string `json:"publicKeyPath"`
PrivateKeyPath string `json:"privateKeyPath"`
Description string `json:"description"`
}
+3
View File
@@ -36,6 +36,9 @@ type WebsiteSSL struct {
DisableCNAME bool `json:"disableCNAME"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
MasterSSLID uint `json:"masterSslId"`
Nodes string `json:"nodes"`
PushNode bool `json:"pushNode"`
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
+18 -1
View File
@@ -33,6 +33,8 @@ type IAppRepo interface {
BatchDelete(ctx context.Context, apps []model.App) error
DeleteByIDs(ctx context.Context, ids []uint) error
DeleteBy(opts ...DBOption) error
GetTopRecomment() ([]string, error)
}
func NewIAppRepo() IAppRepo {
@@ -44,7 +46,7 @@ func (a AppRepo) WithByLikeName(name string) DBOption {
if len(name) == 0 {
return g
}
return g.Where("name like ? or short_desc_zh like ? or short_desc_en like ?", "%"+name+"%", "%"+name+"%", "%"+name+"%")
return g.Where("name like ? or description like ? or short_desc_zh like ? or short_desc_en like ?", "%"+name+"%", "%"+name+"%", "%"+name+"%", "%"+name+"%")
}
}
@@ -123,6 +125,21 @@ func (a AppRepo) GetBy(opts ...DBOption) ([]model.App, error) {
return apps, nil
}
func (a AppRepo) GetTopRecomment() ([]string, error) {
var (
apps []model.App
names []string
)
db := getDb().Model(&model.App{})
if err := db.Order("recommend asc").Limit(6).Find(&apps).Error; err != nil {
return names, err
}
for _, item := range apps {
names = append(names, item.Key)
}
return names, nil
}
func (a AppRepo) BatchCreate(ctx context.Context, apps []model.App) error {
return getTx(ctx).Omit(clause.Associations).Create(&apps).Error
}
+7
View File
@@ -11,6 +11,7 @@ type AppIgnoreUpgradeRepo struct {
type IAppIgnoreUpgradeRepo interface {
WithScope(scope string) DBOption
WithAppID(appID uint) DBOption
List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error)
Create(appIgnoreUpgrade *model.AppIgnoreUpgrade) error
Delete(opts ...DBOption) error
@@ -26,6 +27,12 @@ func (a AppIgnoreUpgradeRepo) WithScope(scope string) DBOption {
}
}
func (a AppIgnoreUpgradeRepo) WithAppID(appID uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("app_id = ?", appID)
}
}
func (a AppIgnoreUpgradeRepo) List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error) {
var appIgnoreUpgradeList []model.AppIgnoreUpgrade
err := getDb(opts...).Find(&appIgnoreUpgradeList).Error
+21
View File
@@ -36,6 +36,7 @@ type IAppInstallRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.AppInstall, error)
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
LoadBaseInfo(key string, name string) (*RootInfo, error)
LoadInstallAppByKeyAndName(key string, name string) (*model.AppInstall, error)
GetFirstByCtx(ctx context.Context, opts ...DBOption) (model.AppInstall, error)
}
@@ -246,3 +247,23 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
info.Status = appInstall.Status
return &info, nil
}
func (a *AppInstallRepo) LoadInstallAppByKeyAndName(key string, name string) (*model.AppInstall, error) {
var (
app model.App
appInstall model.AppInstall
)
if err := global.DB.Where("key = ?", key).First(&app).Error; err != nil {
return nil, err
}
if len(name) == 0 {
if err := global.DB.Where("app_id = ?", app.ID).First(&appInstall).Error; err != nil {
return nil, err
}
} else {
if err := global.DB.Where("app_id = ? AND name = ?", app.ID, name).First(&appInstall).Error; err != nil {
return nil, err
}
}
return &appInstall, nil
}
+17
View File
@@ -17,8 +17,10 @@ type IAppTagRepo interface {
GetByAppId(appId uint) ([]model.AppTag, error)
GetByTagIds(tagIds []uint) ([]model.AppTag, error)
DeleteBy(ctx context.Context, opts ...DBOption) error
GetFirst(ctx context.Context, opts ...DBOption) (*model.AppTag, error)
WithByTagID(tagID uint) DBOption
WithByAppID(appId uint) DBOption
}
func NewIAppTagRepo() IAppTagRepo {
@@ -31,6 +33,12 @@ func (a AppTagRepo) WithByTagID(tagID uint) DBOption {
}
}
func (a AppTagRepo) WithByAppID(appId uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("app_id = ?", appId)
}
}
func (a AppTagRepo) BatchCreate(ctx context.Context, tags []*model.AppTag) error {
return getTx(ctx).Create(&tags).Error
}
@@ -62,3 +70,12 @@ func (a AppTagRepo) GetByTagIds(tagIds []uint) ([]model.AppTag, error) {
func (a AppTagRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
return getTx(ctx, opts...).Delete(&model.AppTag{}).Error
}
func (a AppTagRepo) GetFirst(ctx context.Context, opts ...DBOption) (*model.AppTag, error) {
var appTag model.AppTag
if err := getTx(ctx, opts...).First(&appTag).Error; err != nil {
return nil, err
}
return &appTag, nil
}
+9
View File
@@ -104,6 +104,15 @@ func WithByDate(startTime, endTime time.Time) DBOption {
}
}
func WithByGroups(groupIDs []uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(groupIDs) == 0 {
return g
}
return g.Where("group_id in (?)", groupIDs)
}
}
func WithByCreatedAt(startTime, endTime time.Time) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("created_at > ? AND created_at < ?", startTime, endTime)
+88
View File
@@ -3,6 +3,7 @@ package repo
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
)
type HostRepo struct{}
@@ -13,6 +14,14 @@ type IHostRepo interface {
SaveFirewallRecord(firewall *model.Firewall) error
DeleteFirewallRecordByID(id uint) error
DeleteFirewallRecord(fType, port, protocol, address, strategy string) error
SyncCert(data []model.RootCert) error
GetCert(opts ...DBOption) (model.RootCert, error)
PageCert(limit, offset int, opts ...DBOption) (int64, []model.RootCert, error)
ListCert(opts ...DBOption) ([]model.RootCert, error)
CreateCert(cert *model.RootCert) error
UpdateCert(id uint, vars map[string]interface{}) error
DeleteCert(opts ...DBOption) error
}
func NewIHostRepo() IHostRepo {
@@ -63,3 +72,82 @@ func (h *HostRepo) DeleteFirewallRecordByID(id uint) error {
func (h *HostRepo) DeleteFirewallRecord(fType, port, protocol, address, strategy string) error {
return global.DB.Where("type = ? AND port = ? AND protocol = ? AND address = ? AND strategy = ?", fType, port, protocol, address, strategy).Delete(&model.Firewall{}).Error
}
func (u *HostRepo) GetCert(opts ...DBOption) (model.RootCert, error) {
var cert model.RootCert
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&cert).Error
return cert, err
}
func (u *HostRepo) PageCert(page, size int, opts ...DBOption) (int64, []model.RootCert, error) {
var ops []model.RootCert
db := global.DB.Model(&model.RootCert{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error
return count, ops, err
}
func (u *HostRepo) ListCert(opts ...DBOption) ([]model.RootCert, error) {
var ops []model.RootCert
db := global.DB.Model(&model.RootCert{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Find(&ops).Error
return ops, err
}
func (u *HostRepo) CreateCert(cert *model.RootCert) error {
return global.DB.Create(cert).Error
}
func (u *HostRepo) UpdateCert(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.RootCert{}).Where("id = ?", id).Updates(vars).Error
}
func (u *HostRepo) DeleteCert(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.RootCert{}).Error
}
func (u *HostRepo) SyncCert(data []model.RootCert) error {
tx := global.DB.Begin()
var oldCerts []model.RootCert
_ = tx.Where("1 = ?", 1).Find(&oldCerts).Error
oldCertsMap := make(map[string]uint)
for _, item := range oldCerts {
oldCertsMap[item.Name] = item.ID
}
for _, item := range data {
if _, ok := oldCertsMap[item.Name]; ok {
delete(oldCertsMap, item.Name)
continue
}
item.PassPhrase, _ = encrypt.StringEncrypt("<UN-SET>")
if err := tx.Model(model.RootCert{}).Create(&item).Error; err != nil {
tx.Rollback()
return err
}
}
for _, val := range oldCertsMap {
if err := tx.Where("id = ?", val).Delete(&model.RootCert{}).Error; err != nil {
tx.Rollback()
return err
}
}
tx.Commit()
return nil
}
+7
View File
@@ -17,6 +17,7 @@ type IRuntimeRepo interface {
WithNotId(id uint) DBOption
WithStatus(status string) DBOption
WithDetailId(id uint) DBOption
WithDetailIdsIn(ids []uint) DBOption
WithPort(port int) DBOption
WithNormalStatus(status string) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.Runtime, error)
@@ -55,6 +56,12 @@ func (r *RuntimeRepo) WithDetailId(id uint) DBOption {
}
}
func (r *RuntimeRepo) WithDetailIdsIn(ids []uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("app_detail_id in(?) ", ids)
}
}
func (r *RuntimeRepo) WithNotId(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id != ?", id)
+9
View File
@@ -19,6 +19,7 @@ type ITagRepo interface {
DeleteByID(ctx context.Context, id uint) error
Create(ctx context.Context, tag *model.Tag) error
Save(ctx context.Context, tag *model.Tag) error
GetByKey(key string) (*model.Tag, error)
}
func NewITagRepo() ITagRepo {
@@ -41,6 +42,14 @@ func (t TagRepo) All() ([]model.Tag, error) {
return tags, nil
}
func (t TagRepo) GetByKey(key string) (*model.Tag, error) {
var tag model.Tag
if err := getDb().Where("key = ?", key).First(&tag).Error; err != nil {
return nil, err
}
return &tag, nil
}
func (t TagRepo) GetByIds(ids []uint) ([]model.Tag, error) {
var tags []model.Tag
if err := getDb().Where("id in (?)", ids).Find(&tags).Error; err != nil {
+14
View File
@@ -19,6 +19,8 @@ type IWebsiteRepo interface {
WithRuntimeID(runtimeID uint) DBOption
WithParentID(websiteID uint) DBOption
WithType(websiteType string) DBOption
WithDBType(dbType string) DBOption
WithDBID(dbID uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
List(opts ...DBOption) ([]model.Website, error)
@@ -100,6 +102,18 @@ func (w *WebsiteRepo) WithType(websiteType string) DBOption {
}
}
func (w *WebsiteRepo) WithDBType(dbType string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("db_type = ?", dbType)
}
}
func (w *WebsiteRepo) WithDBID(dbID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("db_id = ?", dbID)
}
}
func (w *WebsiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Website, error) {
var websites []model.Website
db := getDb(opts...).Model(&model.Website{})
+8
View File
@@ -17,6 +17,7 @@ type ISSLRepo interface {
WithByDnsAccountId(dnsAccountId uint) DBOption
WithByCAID(caID uint) DBOption
WithByDomain(domain string) DBOption
WithByMasterSSLID(sslID uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error)
GetFirst(opts ...DBOption) (*model.WebsiteSSL, error)
List(opts ...DBOption) ([]model.WebsiteSSL, error)
@@ -52,12 +53,19 @@ func (w WebsiteSSLRepo) WithByCAID(caID uint) DBOption {
return db.Where("ca_id = ?", caID)
}
}
func (w WebsiteSSLRepo) WithByDomain(domain string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("primary_domain Like ? or domains Like ?", "%"+domain+"%", "%"+domain+"%")
}
}
func (w WebsiteSSLRepo) WithByMasterSSLID(sslID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("master_ssl_id = ?", sslID)
}
}
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) {
var sslList []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
+64 -33
View File
@@ -14,7 +14,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/copier"
"github.com/1Panel-dev/1Panel/agent/utils/email"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v4/disk"
"sort"
"strings"
"sync"
@@ -70,17 +70,18 @@ func (a AlertService) PageAlert(search dto.AlertSearch) (int64, []dto.AlertDTO,
for _, item := range alerts {
result = append(result, dto.AlertDTO{
ID: item.ID,
Type: item.Type,
Cycle: item.Cycle,
Count: item.Count,
Method: item.Method,
Title: item.Title,
Project: item.Project,
Status: item.Status,
SendCount: item.SendCount,
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
ID: item.ID,
Type: item.Type,
Cycle: item.Cycle,
Count: item.Count,
Method: item.Method,
Title: item.Title,
Project: item.Project,
Status: item.Status,
SendCount: item.SendCount,
AdvancedParams: item.AdvancedParams,
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
})
}
@@ -100,17 +101,18 @@ func (a AlertService) GetAlerts() ([]dto.AlertDTO, error) {
for _, item := range alerts {
result = append(result, dto.AlertDTO{
ID: item.ID,
Type: item.Type,
Cycle: item.Cycle,
Count: item.Count,
Method: item.Method,
Title: item.Title,
Project: item.Project,
Status: item.Status,
SendCount: item.SendCount,
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
ID: item.ID,
Type: item.Type,
Cycle: item.Cycle,
Count: item.Count,
Method: item.Method,
Title: item.Title,
Project: item.Project,
Status: item.Status,
SendCount: item.SendCount,
AdvancedParams: item.AdvancedParams,
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
})
}
@@ -165,6 +167,7 @@ func (a AlertService) UpdateAlert(req dto.AlertUpdate) error {
upMap["project"] = req.Project
upMap["status"] = req.Status
upMap["send_count"] = req.SendCount
upMap["advanced_params"] = req.AdvancedParams
if err := alertRepo.Update(upMap, repo.WithByID(req.ID)); err != nil {
return err
@@ -483,23 +486,51 @@ func (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {
func (a AlertService) ExternalUpdateAlert(updateAlert dto.AlertCreate) error {
upMap := make(map[string]interface{})
var newStatus string
if updateAlert.SendCount == 0 {
upMap["status"] = constant.AlertDisable
newStatus = constant.AlertDisable
} else {
upMap["status"] = constant.AlertEnable
newStatus = constant.AlertEnable
upMap["send_count"] = updateAlert.SendCount
if updateAlert.Method != "" {
upMap["method"] = updateAlert.Method
}
}
upMap["method"] = updateAlert.Method
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(updateAlert.Type), alertRepo.WithByProject(updateAlert.Project))
upMap["status"] = newStatus
alertInfo, _ := alertRepo.Get(
alertRepo.WithByType(updateAlert.Type),
alertRepo.WithByProject(updateAlert.Project),
)
if alertInfo.ID > 0 {
if err := alertRepo.Update(upMap, alertRepo.WithByProject(updateAlert.Project), alertRepo.WithByType(updateAlert.Type)); err != nil {
return err
shouldUpdate := false
if alertInfo.Status != newStatus {
shouldUpdate = true
}
if val, ok := upMap["send_count"]; ok && val != alertInfo.SendCount {
shouldUpdate = true
}
if val, ok := upMap["method"]; ok && val != "" && val != alertInfo.Method {
shouldUpdate = true
}
if shouldUpdate {
if err := alertRepo.Update(
upMap,
alertRepo.WithByProject(updateAlert.Project),
alertRepo.WithByType(updateAlert.Type),
); err != nil {
return err
}
}
} else {
updateAlert.Status = constant.AlertEnable
err := a.CreateAlert(updateAlert)
if err != nil {
return err
if updateAlert.Method != "" && updateAlert.Title != "" {
updateAlert.Status = newStatus
if err := a.CreateAlert(updateAlert); err != nil {
return err
}
}
}
File diff suppressed because it is too large Load Diff
+165
View File
@@ -0,0 +1,165 @@
package service
import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"strings"
)
type AlertSender struct {
alert dto.AlertDTO
quotaType string
}
func NewAlertSender(alert dto.AlertDTO, quotaType string) *AlertSender {
return &AlertSender{
alert: alert,
quotaType: quotaType,
}
}
func (s *AlertSender) Send(quota string, params []dto.Param) {
methods := strings.Split(s.alert.Method, ",")
for _, method := range methods {
method = strings.TrimSpace(method)
switch method {
case constant.SMS:
s.sendSMS(quota, params)
case constant.Email:
s.sendEmail(quota, params)
}
}
}
func (s *AlertSender) ResourceSend(quota string, params []dto.Param) {
methods := strings.Split(s.alert.Method, ",")
for _, method := range methods {
method = strings.TrimSpace(method)
switch method {
case constant.SMS:
s.sendResourceSMS(quota, params)
case constant.Email:
s.sendResourceEmail(quota, params)
}
}
}
func (s *AlertSender) sendSMS(quota string, params []dto.Param) {
if !alertUtil.CheckSMSSendLimit(constant.SMS) {
return
}
totalCount, isValid := s.canSendAlert(constant.SMS)
if !isValid {
return
}
create := dto.AlertLogCreate{
Status: constant.AlertSuccess,
Count: totalCount + 1,
AlertId: s.alert.ID,
Type: s.alert.Type,
}
_ = xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS)
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.SMS)
global.LOG.Infof("%s alert sms push successful", s.alert.Type)
}
func (s *AlertSender) sendEmail(quota string, params []dto.Param) {
totalCount, isValid := s.canSendAlert(constant.Email)
if !isValid {
return
}
create := dto.AlertLogCreate{
Status: constant.AlertSuccess,
Count: totalCount + 1,
AlertId: s.alert.ID,
Type: s.alert.Type,
AlertRule: alertUtil.ProcessAlertRule(s.alert),
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
}
transport := xpack.LoadRequestTransport()
_ = alertUtil.CreateEmailAlertLog(create, s.alert, params, transport)
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
global.LOG.Infof("%s alert email push successful", s.alert.Type)
}
func (s *AlertSender) sendResourceSMS(quota string, params []dto.Param) {
if !alertUtil.CheckSMSSendLimit(constant.SMS) {
return
}
todayCount, isValid := s.canResourceSendAlert(constant.SMS)
if !isValid {
return
}
create := dto.AlertLogCreate{
Status: constant.AlertSuccess,
Count: todayCount + 1,
AlertId: s.alert.ID,
Type: s.alert.Type,
}
if err := xpack.CreateSMSAlertLog(s.alert.Type, s.alert, create, quota, params, constant.SMS); err != nil {
global.LOG.Errorf("failed to send SMS alert: %v", err)
return
}
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.SMS)
global.LOG.Infof("%s alert sms push successful", s.alert.Type)
}
func (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {
todayCount, isValid := s.canResourceSendAlert(constant.Email)
if !isValid {
return
}
create := dto.AlertLogCreate{
Status: constant.AlertSuccess,
Count: todayCount + 1,
AlertId: s.alert.ID,
Type: s.alert.Type,
AlertRule: alertUtil.ProcessAlertRule(s.alert),
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Email),
}
transport := xpack.LoadRequestTransport()
if err := alertUtil.CreateEmailAlertLog(create, s.alert, params, transport); err != nil {
global.LOG.Errorf("failed to send Email alert: %v", err)
return
}
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
global.LOG.Infof("%s alert email push successful", s.alert.Type)
}
func (s *AlertSender) canSendAlert(method string) (uint, bool) {
todayCount, totalCount, err := alertRepo.LoadTaskCount(s.alert.Type, s.quotaType, method)
if err != nil {
global.LOG.Errorf("error getting task count: %v", err)
return totalCount, false
}
if todayCount >= 1 || s.alert.SendCount <= totalCount {
return totalCount, false
}
return totalCount, true
}
func (s *AlertSender) canResourceSendAlert(method string) (uint, bool) {
todayCount, _, err := alertRepo.LoadTaskCount(s.alert.Type, s.quotaType, method)
if err != nil {
global.LOG.Errorf("error getting task count: %v", err)
return todayCount, false
}
if s.alert.SendCount <= todayCount {
return todayCount, false
}
return todayCount, true
}
+38 -52
View File
@@ -7,7 +7,6 @@ import (
"fmt"
"net/http"
"os"
"path"
"path/filepath"
"reflect"
"strconv"
@@ -56,7 +55,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
var opts []repo.DBOption
opts = append(opts, appRepo.OrderByRecommend())
if req.Name != "" {
opts = append(opts, appRepo.WithByLikeName(req.Name))
opts = append(opts, appRepo.WithByLikeName(strings.TrimSpace(req.Name)))
}
if req.Type != "" {
opts = append(opts, appRepo.WithType(req.Type))
@@ -100,7 +99,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
if err != nil {
return nil, err
}
var appDTOs []*response.AppItem
appDTOs := make([]*response.AppItem, 0)
info := &dto.SettingInfo{}
if req.Type == "php" {
info, _ = NewISettingService().GetSettingInfo()
@@ -132,8 +131,21 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
continue
}
appDTO.Tags = tags
installs, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(ap.ID))
appDTO.Installed = len(installs) > 0
if ap.Type == constant.RuntimePHP || ap.Type == constant.RuntimeGo || ap.Type == constant.RuntimeNode || ap.Type == constant.RuntimePython || ap.Type == constant.RuntimeJava || ap.Type == constant.RuntimeDotNet {
details, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(ap.ID))
var ids []uint
if len(details) == 0 {
continue
}
for _, d := range details {
ids = append(ids, d.ID)
}
runtimes, _ := runtimeRepo.List(runtimeRepo.WithDetailIdsIn(ids))
appDTO.Installed = len(runtimes) > 0
} else {
installs, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(ap.ID))
appDTO.Installed = len(installs) > 0
}
}
res.Items = appDTOs
res.Total = total
@@ -355,29 +367,6 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
}
}
}
if app.Key == "openresty" && (app.Resource == "remote" || app.Resource == "custom") && common.CompareVersion(appDetail.Version, "1.27") {
if dir, ok := req.Params["WEBSITE_DIR"]; ok {
siteDir := dir.(string)
if siteDir == "" || !strings.HasPrefix(siteDir, "/") {
siteDir = path.Join(global.Dir.DataDir, dir.(string))
}
req.Params["WEBSITE_DIR"] = siteDir
oldWebStePath, _ := settingRepo.GetValueByKey("WEBSITE_DIR")
fileOp := files.NewFileOp()
if oldWebStePath != "" && oldWebStePath != siteDir && fileOp.Stat(oldWebStePath) {
_ = fileOp.Rename(oldWebStePath, siteDir)
}
if !fileOp.Stat(siteDir) {
_ = fileOp.CreateDir(siteDir, constant.DirPerm)
_ = fileOp.CreateDir(path.Join(siteDir, "conf.d"), constant.DirPerm)
}
err = settingRepo.UpdateOrCreate("WEBSITE_DIR", siteDir)
if err != nil {
return
}
go RestartPHPRuntime()
}
}
for key := range req.Params {
if !strings.Contains(key, "PANEL_APP_PORT") {
continue
@@ -486,6 +475,11 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
req.Params["DATABASE_NAME"] = database.Name
}
}
if app.Key == "openresty" {
req.Params["CONTAINER_PACKAGE_URL"] = "http://archive.ubuntu.com/ubuntu/"
req.Params["RESTY_ADD_PACKAGE_BUILDDEPS"] = ""
req.Params["RESTY_CONFIG_OPTIONS_MORE"] = ""
}
paramByte, err = json.Marshal(req.Params)
if err != nil {
return
@@ -513,6 +507,9 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
return err
}
if app.Key == "openresty" {
if err = handleSiteDir(app, appDetail, req, t); err != nil {
return err
}
if err = handleOpenrestyFile(appInstall); err != nil {
return err
}
@@ -928,18 +925,11 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
return err
}
var (
tags []*model.Tag
appTags []*model.AppTag
oldAppIds []uint
)
for _, tag := range list.Extra.Tags {
translations, _ := json.Marshal(tag.Locales)
tags = append(tags, &model.Tag{
Name: tag.Name,
Translations: string(translations),
Sort: tag.Sort,
Key: tag.Key,
})
if err = SyncTags(list.Extra); err != nil {
return err
}
deleteCustomApp()
oldApps, err := appRepo.GetBy(appRepo.WithNotLocal())
@@ -1015,6 +1005,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
}
t.LogSuccess(i18n.GetMsgByKey("SyncAppDetail"))
tags, _ := tagRepo.All()
var (
addAppArray []model.App
updateAppArray []model.App
@@ -1061,16 +1052,8 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
return
}
}
if err = tagRepo.DeleteAll(ctx); err != nil {
return
}
if len(tags) > 0 {
if err = tagRepo.BatchCreate(ctx, tags); err != nil {
return
}
for _, tag := range tags {
tagMap[tag.Key] = tag.ID
}
for _, tag := range tags {
tagMap[tag.Key] = tag.ID
}
for _, update := range updateAppArray {
if err = appRepo.Save(ctx, &update); err != nil {
@@ -1088,10 +1071,13 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
for _, tag := range app.TagsKey {
tagId, ok := tagMap[tag]
if ok {
appTags = append(appTags, &model.AppTag{
AppId: app.ID,
TagId: tagId,
})
exist, _ := appTagRepo.GetFirst(ctx, appTagRepo.WithByTagID(tagId), appTagRepo.WithByAppID(app.ID))
if exist == nil {
appTags = append(appTags, &model.AppTag{
AppId: app.ID,
TagId: tagId,
})
}
}
}
for _, d := range app.Details {
@@ -1134,7 +1120,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
}
if len(oldAppIds) > 0 {
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
if err = appTagRepo.DeleteByAppIds(ctx, deleteIds); err != nil {
return
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ func (a AppIgnoreUpgradeService) List() ([]response.AppIgnoreUpgradeDTO, error)
_ = appIgnoreUpgradeRepo.Delete(repo.WithByID(ignore.ID))
continue
}
dto.Icon = app.Icon
dto.Name = app.Name
if ignore.Scope == "version" {
appDetail, err := appDetailRepo.GetFirst(repo.WithByID(ignore.AppDetailID))
if errors.Is(err, gorm.ErrRecordNotFound) {
+7 -1
View File
@@ -164,6 +164,11 @@ func (a *AppInstallService) CheckExist(req request.AppInstalledInfo) (*response.
res.HttpPort = appInstall.HttpPort
res.HttpsPort = appInstall.HttpsPort
if appInstall.App.Key == "openresty" {
websiteDir, _ := settingRepo.GetValueByKey("WEBSITE_DIR")
res.WebsiteDir = websiteDir
}
return res, nil
}
@@ -672,7 +677,7 @@ func (a *AppInstallService) GetDefaultConfigByKey(key, name string) (string, err
if key == constant.AppMysql || key == constant.AppMariaDB || key == constant.AppMysqlCluster {
filePath = path.Join(filePath, "my.cnf")
}
if key == constant.AppRedis {
if key == constant.AppRedis || key == constant.AppRedisCluster {
filePath = path.Join(filePath, "redis.conf")
}
if key == constant.AppOpenresty {
@@ -788,6 +793,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
}
res.AppContainerConfig = config
res.HostMode = isHostModel(install.DockerCompose)
res.RestartPolicy = getRestartPolicy(install.DockerCompose)
res.WebUI = install.WebUI
res.Type = install.App.Type
return &res, nil
+243 -15
View File
@@ -1,11 +1,13 @@
package service
import (
"bufio"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"maps"
"math"
"net/http"
"os"
@@ -357,7 +359,7 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
if err != nil {
return err
}
images, err := composeV2.GetDockerComposeImagesV2(content, []byte(install.DockerCompose))
images, err := composeV2.GetImagesFromDockerCompose(content, []byte(install.DockerCompose))
if err != nil {
return err
}
@@ -440,6 +442,11 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
_ = op.DeleteDir(parentDir)
}
tx.Commit()
existApps, _ := appInstallRepo.ListBy(context.Background(), appInstallRepo.WithAppId(install.AppId))
if len(existApps) == 0 {
_ = appIgnoreUpgradeRepo.Delete(appIgnoreUpgradeRepo.WithAppID(install.AppId))
}
return nil
}
uninstallTask.AddSubTask(task.GetTaskName(install.Name, task.TaskUninstall, task.TaskScopeApp), uninstall, nil)
@@ -535,6 +542,9 @@ func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map
if oldServiceValue["deploy"] != nil {
serviceValue["deploy"] = oldServiceValue["deploy"]
}
if oldServiceValue["restart"] != nil {
serviceValue["restart"] = oldServiceValue["restart"]
}
servicesMap[install.ServiceName] = serviceValue
composeMap["services"] = servicesMap
return composeMap, nil
@@ -573,6 +583,76 @@ func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string
return string(composeByte), nil
}
func buildNginx(parentTask *task.Task) error {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
fileOp := files.NewFileOp()
buildPath := path.Join(nginxInstall.GetPath(), "build")
if !fileOp.Stat(buildPath) {
return buserr.New("ErrBuildDirNotFound")
}
moduleConfigPath := path.Join(buildPath, "module.json")
moduleContent, err := fileOp.GetContent(moduleConfigPath)
if err != nil {
return err
}
var (
modules []dto.NginxModule
addModuleParams []string
addPackages []string
)
if len(moduleContent) > 0 {
_ = json.Unmarshal(moduleContent, &modules)
bashFile, err := os.OpenFile(path.Join(buildPath, "tmp", "pre.sh"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.DirPerm)
if err != nil {
return err
}
defer bashFile.Close()
bashFileWriter := bufio.NewWriter(bashFile)
for _, module := range modules {
if !module.Enable {
continue
}
_, err = bashFileWriter.WriteString(module.Script + "\n")
if err != nil {
return err
}
addModuleParams = append(addModuleParams, module.Params)
addPackages = append(addPackages, module.Packages...)
}
err = bashFileWriter.Flush()
if err != nil {
return err
}
}
envs, err := gotenv.Read(nginxInstall.GetEnvPath())
if err != nil {
return err
}
envs["RESTY_CONFIG_OPTIONS_MORE"] = ""
envs["RESTY_ADD_PACKAGE_BUILDDEPS"] = ""
if len(addModuleParams) > 0 {
envs["RESTY_CONFIG_OPTIONS_MORE"] = strings.Join(addModuleParams, " ")
}
if len(addPackages) > 0 {
envs["RESTY_ADD_PACKAGE_BUILDDEPS"] = strings.Join(addPackages, " ")
}
_ = gotenv.Write(envs, nginxInstall.GetEnvPath())
if len(addModuleParams) == 0 && len(addPackages) == 0 {
return nil
}
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("TaskBuild"), i18n.GetMsgByKey("Image"))
parentTask.LogStart(logStr)
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*parentTask), cmd.WithTimeout(60*time.Minute))
if err = cmdMgr.RunBashCf("docker compose -f %s build", nginxInstall.GetComposePath()); err != nil {
return err
}
parentTask.LogSuccess(logStr)
return nil
}
func upgradeInstall(req request.AppInstallUpgrade) error {
install, err := appInstallRepo.GetFirst(repo.WithByID(req.InstallID))
if err != nil {
@@ -650,7 +730,11 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
}
dockerCLi, _ := docker.NewClient()
if req.PullImage {
images, err := composeV2.GetDockerComposeImagesV2(content, []byte(detail.DockerCompose))
composeContent := []byte(detail.DockerCompose)
if req.DockerCompose != "" {
composeContent = []byte(req.DockerCompose)
}
images, err := composeV2.GetImagesFromDockerCompose(content, composeContent)
if err != nil {
return err
}
@@ -701,6 +785,14 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
return err
}
envParams := make(map[string]string, len(envs))
if install.App.Key == constant.AppOpenresty {
packageUrl, _ := env.GetEnvValueByKey(install.GetEnvPath(), "CONTAINER_PACKAGE_URL")
addPackage, _ := env.GetEnvValueByKey(install.GetEnvPath(), "RESTY_ADD_PACKAGE_BUILDDEPS")
options, _ := env.GetEnvValueByKey(install.GetEnvPath(), "RESTY_CONFIG_OPTIONS_MORE")
envParams["CONTAINER_PACKAGE_URL"] = packageUrl
envParams["RESTY_ADD_PACKAGE_BUILDDEPS"] = addPackage
envParams["RESTY_CONFIG_OPTIONS_MORE"] = options
}
handleMap(envs, envParams)
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
return err
@@ -714,6 +806,13 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
return err
}
if install.App.Key == constant.AppOpenresty {
if err = buildNginx(t); err != nil {
t.Log(err.Error())
return err
}
}
logStr := fmt.Sprintf("%s %s", i18n.GetMsgByKey("Run"), i18n.GetMsgByKey("App"))
t.Log(logStr)
if out, err := compose.Up(install.GetComposePath()); err != nil {
@@ -739,7 +838,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
}
}
upgradeTask.AddSubTask(task.GetTaskName(install.Name, task.TaskScopeApp, task.TaskUpgrade), upgradeApp, rollBackApp)
upgradeTask.AddSubTaskWithOps(task.GetTaskName(install.Name, task.TaskScopeApp, task.TaskUpgrade), upgradeApp, rollBackApp, 0, 1*time.Hour)
go func() {
err = upgradeTask.Execute()
@@ -864,9 +963,9 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
_ = fileOp.CreateDir(appVersionDir, constant.DirPerm)
}
if logger == nil {
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
global.LOG.Infof("download app [%s] from %s", app.Name, appDetail.DownloadUrl)
} else {
logger.Printf("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
logger.Printf("download app [%s] from %s", app.Name, appDetail.DownloadUrl)
}
filePath := path.Join(appVersionDir, app.Key+"-"+appDetail.Version+".tar.gz")
@@ -882,9 +981,9 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
if err = files.DownloadFileWithProxy(appDetail.DownloadUrl, filePath); err != nil {
if logger == nil {
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
global.LOG.Errorf("download app [%s] error %v", app.Name, err)
} else {
logger.Printf("download app[%s] error %v", app.Name, err)
logger.Printf("download app [%s] error %v", app.Name, err)
}
return
}
@@ -946,11 +1045,11 @@ func copyData(task *task.Task, app model.App, appDetail model.AppDetail, appInst
return
}
envPath := path.Join(appDir, ".env")
envParams := make(map[string]string, len(req.Params))
envParams := make(map[string]string)
if fileOp.Stat(envPath) {
envs, _ := gotenv.Read(envPath)
for k, v := range envs {
envParams[k] = v
if envParams = maps.Clone(envs); envParams == nil {
envParams = make(map[string]string)
}
}
handleMap(req.Params, envParams)
@@ -983,7 +1082,7 @@ func runScript(task *task.Task, appInstall *model.AppInstall, operate string) er
task.LogStart(logStr)
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute), cmd.WithWorkDir(workDir))
out, err := cmdMgr.RunWithStdoutBashCf(scriptPath)
out, err := cmdMgr.RunWithStdoutBashC(scriptPath)
if err != nil {
if out != "" {
err = errors.New(out)
@@ -1028,7 +1127,6 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
errMsg string
)
if pullImages && appInstall.App.Type != "php" {
//projectName := strings.ToLower(appInstall.Name)
envByte, err := files.NewFileOp().GetContent(appInstall.GetEnvPath())
if err != nil {
return err
@@ -1058,8 +1156,8 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
if strings.Contains(errOur, "no such host") {
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
}
if strings.Contains(errOur, "timeout") {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
if strings.Contains(errOur, "Error response from daemon") {
errMsg = i18n.GetMsgByKey("PullImageTimeout") + ":"
}
}
appInstall.Message = errMsg + errOur
@@ -1650,6 +1748,14 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
deploy["resources"] = resource
serviceValue["deploy"] = deploy
if req.RestartPolicy != "" {
if req.RestartPolicy == "on-failure" {
serviceValue["restart"] = "on-failure:5"
} else {
serviceValue["restart"] = req.RestartPolicy
}
}
ports, ok := serviceValue["ports"].([]interface{})
if ok {
for i, port := range ports {
@@ -1752,6 +1858,17 @@ func isHostModel(dockerCompose string) bool {
return false
}
func getRestartPolicy(yml string) string {
var project docker.ComposeProject
if err := yaml.Unmarshal([]byte(yml), &project); err != nil {
return ""
}
for _, service := range project.Services {
return service.Restart
}
return ""
}
func getMajorVersion(version string) string {
parts := strings.Split(version, ".")
if len(parts) >= 2 {
@@ -1774,8 +1891,16 @@ func ignoreUpdate(installed model.AppInstall) bool {
}
return true
}
if installed.App.Key == "sqlbot" {
if common.CompareVersion("1.1.0", installed.Version) {
return true
}
}
ignores, _ := appIgnoreUpgradeRepo.List(appDetailRepo.WithAppId(installed.AppId), appIgnoreUpgradeRepo.WithScope("all"))
return len(ignores) > 0
if len(ignores) > 0 {
return true
}
return false
}
func RequestDownloadCallBack(downloadCallBackUrl string) {
@@ -1814,6 +1939,53 @@ func getAppTags(appID uint, lang string) ([]response.TagDTO, error) {
return res, nil
}
func handleSiteDir(app model.App, appDetail model.AppDetail, req request.AppInstallCreate, t *task.Task) error {
if app.Key == "openresty" && (app.Resource == "remote" || app.Resource == "custom") && common.CompareVersion(appDetail.Version, "1.27") {
if dir, ok := req.Params["WEBSITE_DIR"]; ok {
siteDir := dir.(string)
if siteDir == "" || !strings.HasPrefix(siteDir, "/") {
siteDir = path.Join(global.Dir.DataDir, dir.(string))
}
req.Params["WEBSITE_DIR"] = siteDir
oldWebStePath, _ := settingRepo.GetValueByKey("WEBSITE_DIR")
fileOp := files.NewFileOp()
if oldWebStePath != "" && oldWebStePath != siteDir && fileOp.Stat(oldWebStePath) {
t.Log(i18n.GetWithName("MoveSiteDir", siteDir))
if fileOp.Stat(siteDir) {
if fileOp.Stat(path.Join(siteDir, "conf.d")) {
_ = fileOp.Rename(path.Join(siteDir, "conf.d"), path.Join(siteDir, "conf.d.bak"))
}
if fileOp.Stat(path.Join(siteDir, "sites")) {
_ = fileOp.Rename(path.Join(siteDir, "sites"), path.Join(siteDir, "sites.bak"))
}
if err := fileOp.Rename(path.Join(oldWebStePath, "sites"), path.Join(siteDir, "sites")); err != nil {
return err
}
if err := fileOp.Rename(path.Join(oldWebStePath, "conf.d"), path.Join(siteDir, "conf.d")); err != nil {
return err
}
} else {
err := fileOp.Rename(oldWebStePath, siteDir)
if err != nil {
return err
}
}
t.Log(i18n.GetMsgByKey("MoveSiteDirSuccess"))
}
if !fileOp.Stat(siteDir) {
_ = fileOp.CreateDir(siteDir, constant.DirPerm)
_ = fileOp.CreateDir(path.Join(siteDir, "conf.d"), constant.DirPerm)
}
err := settingRepo.UpdateOrCreate("WEBSITE_DIR", siteDir)
if err != nil {
return err
}
go RestartPHPRuntime()
}
}
return nil
}
func handleOpenrestyFile(appInstall *model.AppInstall) error {
websites, _ := websiteRepo.List()
hasDefaultWebsite := false
@@ -1899,3 +2071,59 @@ func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool) error
}
return nil
}
func SyncTags(remoteProperties dto.ExtraProperties) error {
tx, ctx := getTxAndContext()
defer tx.Rollback()
localTags, _ := tagRepo.All()
localTagsMap := make(map[string]*model.Tag)
for i := range localTags {
localTagsMap[localTags[i].Key] = &localTags[i]
}
var err error
remoteTagsMap := make(map[string]*dto.Tag)
for i := range remoteProperties.Tags {
remoteTagsMap[remoteProperties.Tags[i].Key] = &remoteProperties.Tags[i]
}
for key, localTag := range localTagsMap {
if _, exists := remoteTagsMap[key]; !exists {
_ = tagRepo.DeleteByID(ctx, localTag.ID)
}
}
for _, remoteTag := range remoteProperties.Tags {
translations, _ := json.Marshal(remoteTag.Locales)
if existTag, exists := localTagsMap[remoteTag.Key]; exists {
if needsUpdate(existTag, remoteTag, string(translations)) {
existTag.Name = remoteTag.Name
existTag.Sort = remoteTag.Sort
existTag.Translations = string(translations)
if err = tagRepo.Save(ctx, existTag); err != nil {
return err
}
}
} else {
newTag := &model.Tag{
Key: remoteTag.Key,
Name: remoteTag.Name,
Sort: remoteTag.Sort,
Translations: string(translations),
}
if err = tagRepo.Create(ctx, newTag); err != nil {
return err
}
}
}
tx.Commit()
return nil
}
func needsUpdate(localTag *model.Tag, remoteTag dto.Tag, translations string) bool {
return localTag.Name != remoteTag.Name ||
localTag.Sort != remoteTag.Sort ||
localTag.Translations != translations
}
+53 -6
View File
@@ -14,9 +14,11 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage"
"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage/client"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
@@ -393,9 +395,13 @@ type backupClientHelper struct {
name string
backupPath string
client cloud_storage.CloudStorageClient
isOk bool
hasBackup bool
message string
}
func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
func NewBackupClientMap(ids []string) map[string]backupClientHelper {
var accounts []model.BackupAccount
var idItems []uint
for i := 0; i < len(ids); i++ {
@@ -406,18 +412,59 @@ func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
clientMap := make(map[string]backupClientHelper)
for _, item := range accounts {
backClient, err := newClient(&item, true)
if err != nil {
return nil, err
}
clientMap[fmt.Sprintf("%v", item.ID)] = backupClientHelper{
itemHelper := backupClientHelper{
client: backClient,
name: item.Name,
backupPath: item.BackupPath,
accountType: item.Type,
id: item.ID,
isOk: err == nil,
}
if err != nil {
itemHelper.message = err.Error()
}
clientMap[fmt.Sprintf("%v", item.ID)] = itemHelper
}
return clientMap, nil
return clientMap
}
func uploadWithMap(taskItem task.Task, accountMap map[string]backupClientHelper, src, dst, accountIDs string, downloadAccountID, retry uint) error {
accounts := strings.Split(accountIDs, ",")
for _, account := range accounts {
if len(account) == 0 {
continue
}
itemBackup, ok := accountMap[account]
if !ok {
continue
}
if itemBackup.hasBackup {
continue
}
if !itemBackup.isOk {
taskItem.LogFailed(i18n.GetMsgWithDetail("LoadBackupFailed", itemBackup.message))
continue
}
taskItem.LogStart(i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
"file": path.Join(itemBackup.backupPath, dst),
"backup": itemBackup.name,
}))
for i := 0; i < int(retry); i++ {
_, err := itemBackup.client.Upload(src, path.Join(itemBackup.backupPath, dst))
taskItem.LogWithStatus(i18n.GetMsgByKey("Upload"), err)
if err != nil {
if account == fmt.Sprintf("%d", downloadAccountID) {
return err
}
} else {
break
}
}
itemBackup.hasBackup = true
accountMap[account] = itemBackup
}
os.RemoveAll(src)
return nil
}
func newClient(account *model.BackupAccount, isEncrypt bool) (cloud_storage.CloudStorageClient, error) {
+2 -2
View File
@@ -119,7 +119,7 @@ func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir,
}
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName) }
backupTask.AddSubTask(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeWebsite), func(task *task.Task) error { return itemHandler() }, nil)
backupTask.AddSubTask(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeDatabase), func(task *task.Task) error { return itemHandler() }, nil)
if parentTask != nil {
return itemHandler()
}
@@ -137,7 +137,7 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
}
itemTask = parentTask
if parentTask == nil {
itemTask, err = task.NewTaskWithOps("Redis", task.TaskRecover, task.TaskScopeDatabase, req.TaskID, dbInfo.ID)
itemTask, err = task.NewTaskWithOps(req.Name, task.TaskRecover, task.TaskScopeDatabase, req.TaskID, dbInfo.ID)
if err != nil {
return err
}
+19 -6
View File
@@ -6,6 +6,7 @@ import (
"os"
"path"
"sync"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
@@ -249,20 +250,32 @@ func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.Recor
recordIds = append(recordIds, fmt.Sprintf("%v", record.DownloadID))
}
}
clientMap, err := NewBackupClientMap(recordIds)
if err != nil {
return nil, err
}
clientMap := NewBackupClientMap(recordIds)
var datas []dto.RecordFileSize
var wg sync.WaitGroup
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
for i := 0; i < len(list); i++ {
datas = append(datas, dto.RecordFileSize{ID: list[i].ID})
if val, ok := clientMap[fmt.Sprintf("%v", list[i].DownloadID)]; ok {
if !val.isOk {
continue
}
wg.Add(1)
go func(index int) {
datas[index].Size, _ = val.client.Size(path.Join(val.backupPath, list[i].FilePath))
wg.Done()
defer wg.Done()
done := make(chan struct{}, 1)
go func() {
datas[index].Size, _ = val.client.Size(path.Join(val.backupPath, list[i].FilePath))
defer close(done)
}()
select {
case <-ctx.Done():
return
case <-done:
return
}
}(i)
}
}
+4 -5
View File
@@ -3,7 +3,6 @@ package service
import (
"bufio"
"fmt"
"github.com/1Panel-dev/1Panel/agent/utils/alert_push"
"os"
"os/exec"
"path"
@@ -12,13 +11,13 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/alert_push"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/systemctl"
@@ -192,7 +191,7 @@ func (c *ClamService) Create(req dto.ClamCreate) error {
if err := clamRepo.Create(&clam); err != nil {
return err
}
if req.AlertCount != 0 {
if req.AlertCount != 0 && req.AlertTitle != "" && req.AlertMethod != "" {
createAlert := dto.AlertCreate{
Title: req.AlertTitle,
SendCount: req.AlertCount,
@@ -345,7 +344,7 @@ func (c *ClamService) HandleOnce(req dto.OperateByID) error {
}
}
global.LOG.Debugf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
stdout, err := cmd.RunDefaultWithStdoutBashCf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
stdout, err := cmd.NewCommandMgr(cmd.WithIgnoreExist1(), cmd.WithTimeout(30*time.Minute)).RunWithStdoutBashCf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
if err != nil {
global.LOG.Errorf("clamdscan failed, stdout: %v, err: %v", stdout, err)
}
+213 -89
View File
@@ -45,8 +45,8 @@ import (
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/mem"
)
type ContainerService struct{}
@@ -54,6 +54,7 @@ type ContainerService struct{}
type IContainerService interface {
Page(req dto.PageContainer) (int64, interface{}, error)
List() []dto.ContainerOptions
ListByImage(imageName string) []dto.ContainerOptions
LoadStatus() (dto.ContainerStatus, error)
PageNetwork(req dto.SearchWithPage) (int64, interface{}, error)
ListNetwork() ([]dto.Options, error)
@@ -82,7 +83,7 @@ type IContainerService interface {
CreateVolume(req dto.VolumeCreate) error
TestCompose(req dto.ComposeCreate) (bool, error)
ComposeUpdate(req dto.ComposeUpdate) error
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
Prune(req dto.ContainerPrune) error
LoadUsers(req dto.OperationWithName) []string
@@ -246,6 +247,33 @@ func (u *ContainerService) List() []dto.ContainerOptions {
return options
}
func (u *ContainerService) ListByImage(imageName string) []dto.ContainerOptions {
var options []dto.ContainerOptions
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("load docker client for contianer list failed, err: %v", err)
return nil
}
defer client.Close()
containers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
if err != nil {
global.LOG.Errorf("load container list failed, err: %v", err)
return nil
}
for _, container := range containers {
if container.Image != imageName {
continue
}
for _, name := range container.Names {
if len(name) != 0 {
options = append(options, dto.ContainerOptions{Name: strings.TrimPrefix(name, "/"), State: container.State})
}
}
}
return options
}
func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
var data dto.ContainerStatus
client, err := docker.NewDockerClient()
@@ -417,66 +445,93 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
return string(bytes), nil
}
func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) {
report := dto.ContainerPruneReport{}
func (u *ContainerService) Prune(req dto.ContainerPrune) error {
client, err := docker.NewDockerClient()
if err != nil {
return report, err
return err
}
defer client.Close()
pruneFilters := filters.NewArgs()
if req.WithTagAll {
pruneFilters.Add("dangling", "false")
if req.PruneType != "image" {
pruneFilters.Add("until", "24h")
}
}
name := ""
switch req.PruneType {
case "container":
rep, err := client.ContainersPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.ContainersDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
name = "Container"
case "image":
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.ImagesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
case "network":
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.NetworksDeleted)
name = "Image"
case "volume":
versions, err := client.ServerVersion(context.Background())
if err != nil {
return report, err
}
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
pruneFilters.Add("all", "true")
}
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.VolumesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
name = "Volume"
case "buildcache":
opts := build.CachePruneOptions{}
opts.All = true
rep, err := client.BuildCachePrune(context.Background(), opts)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.CachesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
name = "BuildCache"
case "network":
name = "Network"
}
return report, nil
taskItem, err := task.NewTaskWithOps(i18n.GetMsgByKey(name), task.TaskClean, task.TaskScopeContainer, req.TaskID, 1)
if err != nil {
global.LOG.Errorf("new task for create container failed, err: %v", err)
return err
}
taskItem.AddSubTask(i18n.GetMsgByKey("TaskClean"), func(t *task.Task) error {
pruneFilters := filters.NewArgs()
if req.WithTagAll {
pruneFilters.Add("dangling", "false")
if req.PruneType != "image" {
pruneFilters.Add("until", "24h")
}
}
DeletedNumber := 0
SpaceReclaimed := 0
switch req.PruneType {
case "container":
rep, err := client.ContainersPrune(context.Background(), pruneFilters)
if err != nil {
return err
}
DeletedNumber = len(rep.ContainersDeleted)
SpaceReclaimed = int(rep.SpaceReclaimed)
case "image":
rep, err := client.ImagesPrune(context.Background(), pruneFilters)
if err != nil {
return err
}
DeletedNumber = len(rep.ImagesDeleted)
SpaceReclaimed = int(rep.SpaceReclaimed)
case "network":
rep, err := client.NetworksPrune(context.Background(), pruneFilters)
if err != nil {
return err
}
DeletedNumber = len(rep.NetworksDeleted)
case "volume":
versions, err := client.ServerVersion(context.Background())
if err != nil {
return err
}
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
pruneFilters.Add("all", "true")
}
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
if err != nil {
return err
}
DeletedNumber = len(rep.VolumesDeleted)
SpaceReclaimed = int(rep.SpaceReclaimed)
case "buildcache":
opts := build.CachePruneOptions{}
opts.All = true
rep, err := client.BuildCachePrune(context.Background(), opts)
if err != nil {
return err
}
DeletedNumber = len(rep.CachesDeleted)
SpaceReclaimed = int(rep.SpaceReclaimed)
}
taskItem.Log(i18n.GetMsgWithMap("PruneHelper", map[string]interface{}{"name": i18n.GetMsgByKey(name), "count": DeletedNumber, "size": common.LoadSizeUnit2F(float64(SpaceReclaimed))}))
return nil
}, nil)
go func() {
_ = taskItem.Execute()
}()
return nil
}
func (u *ContainerService) LoadResourceLimit() (*dto.ResourceLimit, error) {
@@ -656,6 +711,12 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
return err
}
if len(oldContainer.Config.Entrypoint) != 0 {
if oldContainer.Config.Entrypoint[0] == "/docker-entrypoint.sh" {
oldContainer.Config.Entrypoint = []string{}
}
}
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskUpdate, task.TaskScopeContainer, req.TaskID, 1)
if err != nil {
global.LOG.Errorf("new task for create container failed, err: %v", err)
@@ -718,17 +779,14 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
}
defer client.Close()
ctx := context.Background()
oldContainer, err := client.ContainerInspect(ctx, req.Name)
if err != nil {
return err
}
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskUpgrade, task.TaskScopeContainer, req.TaskID, 1)
taskItem, err := task.NewTaskWithOps(req.Image, task.TaskUpgrade, task.TaskScopeImage, req.TaskID, 1)
if err != nil {
global.LOG.Errorf("new task for create container failed, err: %v", err)
return err
}
go func() {
taskItem.AddSubTask(i18n.GetWithName("ContainerImagePull", req.Image), func(t *task.Task) error {
taskItem.LogStart(i18n.GetWithName("ContainerImagePull", req.Image))
if !checkImageExist(client, req.Image) || req.ForcePull {
if err := pullImages(taskItem, client, req.Image); err != nil {
if !req.ForcePull {
@@ -739,38 +797,58 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
}
return nil
}, nil)
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", req.Name), func(t *task.Task) error {
config := oldContainer.Config
config.Image = req.Image
hostConf := oldContainer.HostConfig
var networkConf network.NetworkingConfig
if oldContainer.NetworkSettings != nil {
for networkKey := range oldContainer.NetworkSettings.Networks {
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}
break
for _, item := range req.Names {
var oldContainer container.InspectResponse
taskItem.AddSubTask(i18n.GetWithName("ContainerLoadInfo", item), func(t *task.Task) error {
taskItem.Logf("----------------- %s -----------------", item)
oldContainer, err = client.ContainerInspect(ctx, item)
if err != nil {
return err
}
}
err := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true})
taskItem.LogWithStatus(i18n.GetWithName("ContainerRemoveOld", req.Name), err)
if err != nil {
return err
}
inspected, err := client.ImageInspect(ctx, req.Image)
if err != nil {
return fmt.Errorf("inspect image failed, err: %v", err)
}
if isDynamicImage(inspected) {
oldContainer.Config.Entrypoint = nil
oldContainer.Config.Cmd = nil
}
return nil
}, nil)
con, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
if err != nil {
taskItem.Log(i18n.GetMsgByKey("ContainerRecreate"))
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
return fmt.Errorf("upgrade container failed, err: %v", err)
}
err = client.ContainerStart(ctx, con.ID, container.StartOptions{})
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerStartCheck"), err)
if err != nil {
return fmt.Errorf("upgrade successful but start failed, err: %v", err)
}
return nil
}, nil)
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", item), func(t *task.Task) error {
oldContainer.Config.Cmd = nil
config := oldContainer.Config
config.Image = req.Image
hostConf := oldContainer.HostConfig
var networkConf network.NetworkingConfig
if oldContainer.NetworkSettings != nil {
for networkKey := range oldContainer.NetworkSettings.Networks {
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{networkKey: {}}
break
}
}
err := client.ContainerRemove(ctx, item, container.RemoveOptions{Force: true})
taskItem.LogWithStatus(i18n.GetWithName("ContainerRemoveOld", item), err)
if err != nil {
return err
}
con, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, item)
if err != nil {
taskItem.Log(i18n.GetMsgByKey("ContainerRecreate"))
reCreateAfterUpdate(item, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
return fmt.Errorf("upgrade container failed, err: %v", err)
}
err = client.ContainerStart(ctx, con.ID, container.StartOptions{})
taskItem.LogWithStatus(i18n.GetMsgByKey("ContainerStartCheck"), err)
if err != nil {
return fmt.Errorf("upgrade successful but start failed, err: %v", err)
}
return nil
}, nil)
}
if err := taskItem.Execute(); err != nil {
global.LOG.Error(err.Error())
}
@@ -1240,6 +1318,32 @@ func checkImageExist(client *client.Client, imageItem string) bool {
return false
}
func checkImageLike(client *client.Client, imageName string) bool {
if client == nil {
var err error
client, err = docker.NewDockerClient()
if err != nil {
return false
}
}
images, err := client.ImageList(context.Background(), image.ListOptions{})
if err != nil {
return false
}
for _, img := range images {
for _, tag := range img.RepoTags {
parts := strings.Split(tag, "/")
imageNameWithTag := parts[len(parts)-1]
if imageNameWithTag == imageName {
return true
}
}
}
return false
}
func pullImages(task *task.Task, client *client.Client, imageName string) error {
dockerCli := docker.NewClientWithExist(client)
options := image.PullOptions{}
@@ -1325,7 +1429,7 @@ func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
portMap[nat.Port(fmt.Sprintf("%d/%s", containerStart+i, port.Protocol))] = []nat.PortBinding{bindItem}
}
for i := hostStart; i <= hostEnd; i++ {
if common.ScanPort(i) {
if common.ScanPortWithIP(port.HostIP, i) {
return portMap, buserr.WithDetail("ErrPortInUsed", i, nil)
}
}
@@ -1336,7 +1440,7 @@ func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
} else {
portItem, _ = strconv.Atoi(port.HostPort)
}
if common.ScanPort(portItem) {
if common.ScanPortWithIP(port.HostIP, portItem) {
return portMap, buserr.WithDetail("ErrPortInUsed", portItem, nil)
}
bindItem := nat.PortBinding{HostPort: strconv.Itoa(portItem), HostIP: port.HostIP}
@@ -1639,3 +1743,23 @@ func loadContainerPortForInfo(itemPorts []container.Port) []dto.PortHelper {
}
return exposedPorts
}
func isDynamicImage(inspected image.InspectResponse) bool {
if len(inspected.Config.Entrypoint) > 0 {
entrypointStr := strings.Join(inspected.Config.Entrypoint, " ")
if strings.Contains(entrypointStr, "entrypoint") {
return true
}
}
dirs := []string{"/docker-entrypoint.d", "/docker-entrypoint-initdb.d"}
for _, dir := range dirs {
for _, layer := range inspected.RootFS.Layers {
if strings.Contains(layer, dir) {
return true
}
}
}
return false
}
+52 -22
View File
@@ -10,12 +10,9 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/docker/docker/api/types/container"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
@@ -24,6 +21,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/compose"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"golang.org/x/net/context"
)
@@ -54,14 +52,13 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
}
composeCreatedByLocal, _ := composeRepo.ListRecord()
composeLocalMap := make(map[string]dto.ComposeInfo)
for _, localItem := range composeCreatedByLocal {
composeItemLocal := dto.ComposeInfo{
ContainerNumber: 0,
CreatedAt: localItem.CreatedAt.Format(constant.DateTimeLayout),
ConfigFile: localItem.Path,
Workdir: strings.TrimSuffix(localItem.Path, "/docker-compose.yml"),
ContainerCount: 0,
CreatedAt: localItem.CreatedAt.Format(constant.DateTimeLayout),
ConfigFile: localItem.Path,
Workdir: strings.TrimSuffix(localItem.Path, "/docker-compose.yml"),
}
composeItemLocal.CreatedBy = "1Panel"
composeItemLocal.Path = localItem.Path
@@ -78,18 +75,24 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
CreateTime: time.Unix(container.Created, 0).Format(constant.DateTimeLayout),
}
if compose, has := composeMap[name]; has {
compose.ContainerNumber++
compose.ContainerCount++
if strings.ToLower(containerItem.State) == "running" {
compose.RunningCount++
}
compose.Containers = append(compose.Containers, containerItem)
composeMap[name] = compose
} else {
config := container.Labels[composeConfigLabel]
workdir := container.Labels[composeWorkdirLabel]
composeItem := dto.ComposeInfo{
ContainerNumber: 1,
CreatedAt: time.Unix(container.Created, 0).Format(constant.DateTimeLayout),
ConfigFile: config,
Workdir: workdir,
Containers: []dto.ComposeContainer{containerItem},
ContainerCount: 1,
CreatedAt: time.Unix(container.Created, 0).Format(constant.DateTimeLayout),
ConfigFile: config,
Workdir: workdir,
Containers: []dto.ComposeContainer{containerItem},
}
if strings.ToLower(containerItem.State) == "running" {
composeItem.RunningCount = 1
}
createdBy, ok := container.Labels[composeCreatedBy]
if ok {
@@ -118,8 +121,8 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
}
for key, item := range composeMap {
if existingItem, exists := mergedMap[key]; exists {
if item.ContainerNumber > 0 {
if existingItem.ContainerNumber <= 0 {
if item.ContainerCount > 0 {
if existingItem.ContainerCount <= 0 {
mergedMap[key] = item
}
}
@@ -224,12 +227,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
if cmd.CheckIllegal(req.Path, req.Operation) {
return buserr.New("ErrCmdIllegal")
}
if _, err := os.Stat(req.Path); err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
}
if req.Operation == "delete" {
if stdout, err := compose.Operate(req.Path, "down"); err != nil {
return errors.New(string(stdout))
if err := removeContainerForCompose(req.Name, req.Path); err != nil && !req.Force {
return err
}
if req.WithFile {
_ = os.RemoveAll(path.Dir(req.Path))
@@ -237,6 +237,9 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
_ = composeRepo.DeleteRecord(repo.WithByName(req.Name))
return nil
}
if _, err := os.Stat(req.Path); err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
}
if req.Operation == "up" {
if stdout, err := compose.Up(req.Path); err != nil {
return errors.New(string(stdout))
@@ -305,6 +308,33 @@ func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
return nil
}
func removeContainerForCompose(composeName, composePath string) error {
if _, err := os.Stat(composePath); err == nil {
if stdout, err := compose.Operate(composePath, "down"); err != nil {
return errors.New(stdout)
}
return nil
}
var options container.ListOptions
options.All = true
options.Filters = filters.NewArgs()
options.Filters.Add("label", "com.docker.compose.project="+composeName)
client, err := docker.NewDockerClient()
if err != nil {
return err
}
defer client.Close()
ctx := context.Background()
containers, err := client.ContainerList(ctx, options)
if err != nil {
return err
}
for _, c := range containers {
_ = client.ContainerRemove(ctx, c.ID, container.RemoveOptions{RemoveVolumes: true, Force: true})
}
return nil
}
func recreateCompose(content, path string) error {
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
+311 -33
View File
@@ -1,6 +1,7 @@
package service
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -15,6 +16,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/robfig/cron/v3"
@@ -30,11 +32,13 @@ type ICronjobService interface {
HandleOnce(id uint) error
Update(id uint, req dto.CronjobOperate) error
UpdateStatus(id uint, status string) error
UpdateGroup(req dto.ChangeGroup) error
Delete(req dto.CronjobBatchDelete) error
Download(down dto.CronjobDownload) (string, error)
StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)
CleanRecord(req dto.CronjobClean) error
Export(req dto.OperateByIDs) (string, error)
Import(req []dto.CronjobTrans) error
LoadScriptOptions() []dto.ScriptOptions
LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, error)
@@ -46,7 +50,11 @@ func NewICronjobService() ICronjobService {
}
func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interface{}, error) {
total, cronjobs, err := cronjobRepo.Page(search.Page, search.PageSize, repo.WithByLikeName(search.Info), repo.WithOrderRuleBy(search.OrderBy, search.Order))
total, cronjobs, err := cronjobRepo.Page(search.Page,
search.PageSize,
repo.WithByGroups(search.GroupIDs),
repo.WithByLikeName(search.Info),
repo.WithOrderRuleBy(search.OrderBy, search.Order))
var dtoCronjobs []dto.CronjobInfo
for _, cronjob := range cronjobs {
var item dto.CronjobInfo
@@ -102,6 +110,292 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
return &item, err
}
func (u *CronjobService) Export(req dto.OperateByIDs) (string, error) {
cronjobs, err := cronjobRepo.List(repo.WithByIDs(req.IDs))
if err != nil {
return "", err
}
var data []dto.CronjobTrans
for _, cronjob := range cronjobs {
item := dto.CronjobTrans{
Name: cronjob.Name,
Type: cronjob.Type,
GroupID: cronjob.GroupID,
SpecCustom: cronjob.SpecCustom,
Spec: cronjob.Spec,
Executor: cronjob.Executor,
ScriptMode: cronjob.ScriptMode,
Script: cronjob.Script,
Command: cronjob.Command,
ContainerName: cronjob.ContainerName,
User: cronjob.User,
URL: cronjob.URL,
DBType: cronjob.DBType,
ExclusionRules: cronjob.ExclusionRules,
IsDir: cronjob.IsDir,
SourceDir: cronjob.SourceDir,
RetainCopies: cronjob.RetainCopies,
RetryTimes: cronjob.RetryTimes,
Timeout: cronjob.Timeout,
IgnoreErr: cronjob.IgnoreErr,
Secret: cronjob.Secret,
}
switch cronjob.Type {
case "app":
if cronjob.AppID == "all" {
break
}
apps := loadAppsForJob(cronjob)
for _, app := range apps {
item.Apps = append(item.Apps, dto.TransHelper{Name: app.App.Key, DetailName: app.Name})
}
case "website", "cutWebsiteLog":
if cronjob.Website == "all" {
break
}
websites := loadWebsForJob(cronjob)
for _, website := range websites {
item.Websites = append(item.Websites, website.Alias)
}
case "database":
if cronjob.DBName == "all" {
break
}
databases := loadDbsForJob(cronjob)
for _, db := range databases {
item.DBNames = append(item.DBNames, dto.TransHelper{Name: db.Database, DetailName: db.Name})
}
case "shell":
if cronjob.ScriptMode == "library" {
script, err := scriptRepo.Get(repo.WithByID(cronjob.ScriptID))
if err != nil {
return "", err
}
item.ScriptName = script.Name
}
case "snapshot":
if len(cronjob.SnapshotRule) == 0 {
break
}
var snapRule dto.SnapshotRule
if err := json.Unmarshal([]byte(cronjob.SnapshotRule), &snapRule); err != nil {
return "", err
}
item.SnapshotRule.WithImage = snapRule.WithImage
if len(snapRule.IgnoreAppIDs) != 0 {
ignoreApps, _ := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(snapRule.IgnoreAppIDs))
for _, app := range ignoreApps {
item.SnapshotRule.IgnoreApps = append(item.SnapshotRule.IgnoreApps, dto.TransHelper{Name: app.App.Key, DetailName: app.Name})
}
}
}
item.SourceAccounts, item.DownloadAccount, _ = loadBackupNamesByID(cronjob.SourceAccountIDs, cronjob.DownloadAccountID)
alertInfo, _ := alertRepo.Get(alertRepo.WithByType(cronjob.Type), alertRepo.WithByProject(strconv.Itoa(int(cronjob.ID))), repo.WithByStatus(constant.AlertEnable))
if alertInfo.SendCount != 0 {
item.AlertCount = alertInfo.SendCount
item.AlertTitle = alertInfo.Title
item.AlertMethod = alertInfo.Method
} else {
item.AlertCount = 0
}
data = append(data, item)
}
itemJson, err := json.Marshal(&data)
if err != nil {
return "", err
}
return string(itemJson), nil
}
func (u *CronjobService) Import(req []dto.CronjobTrans) error {
for _, item := range req {
cronjobItem, _ := cronjobRepo.Get(repo.WithByName(item.Name))
if cronjobItem.ID != 0 {
continue
}
cronjob := model.Cronjob{
Name: item.Name,
Type: item.Type,
GroupID: item.GroupID,
SpecCustom: item.SpecCustom,
Spec: item.Spec,
Executor: item.Executor,
ScriptMode: item.ScriptMode,
Command: item.Command,
ContainerName: item.ContainerName,
User: item.User,
URL: item.URL,
DBType: item.DBType,
ExclusionRules: item.ExclusionRules,
IsDir: item.IsDir,
RetainCopies: item.RetainCopies,
RetryTimes: item.RetryTimes,
Timeout: item.Timeout,
IgnoreErr: item.IgnoreErr,
Secret: item.Secret,
}
hasNotFound := false
switch item.Type {
case "app":
if len(item.Apps) == 0 {
cronjob.AppID = "all"
break
}
var appIDs []string
for _, app := range item.Apps {
appItem, err := appInstallRepo.LoadInstallAppByKeyAndName(app.Name, app.DetailName)
if err != nil {
hasNotFound = true
continue
}
appIDs = append(appIDs, fmt.Sprintf("%v", appItem.ID))
}
cronjob.AppID = strings.Join(appIDs, ",")
case "website", "cutWebsiteLog":
if len(item.Websites) == 0 {
cronjob.Website = "all"
break
}
var webIDs []string
for _, web := range item.Websites {
webItem, err := websiteRepo.GetFirst(websiteRepo.WithAlias(web))
if err != nil {
hasNotFound = true
continue
}
webIDs = append(webIDs, fmt.Sprintf("%v", webItem.ID))
}
cronjob.Website = strings.Join(webIDs, ",")
case "database":
if len(item.DBNames) == 0 {
cronjob.DBName = "all"
break
}
var dbIDs []string
if strings.Contains(cronjob.DBType, "postgresql") {
for _, db := range item.DBNames {
dbItem, err := postgresqlRepo.Get(postgresqlRepo.WithByPostgresqlName(db.Name), repo.WithByName(db.DetailName))
if err != nil {
hasNotFound = true
continue
}
dbIDs = append(dbIDs, fmt.Sprintf("%v", dbItem.ID))
}
} else {
for _, db := range item.DBNames {
dbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(db.Name), repo.WithByName(db.DetailName))
if err != nil {
hasNotFound = true
continue
}
dbIDs = append(dbIDs, fmt.Sprintf("%v", dbItem.ID))
}
}
cronjob.DBName = strings.Join(dbIDs, ",")
case "shell":
if len(item.ContainerName) != 0 {
cronjob.Script = item.Script
client, err := docker.NewDockerClient()
if err != nil {
hasNotFound = true
break
}
defer client.Close()
if _, err := client.ContainerStats(context.Background(), item.ContainerName, false); err != nil {
hasNotFound = true
break
}
}
switch item.ScriptMode {
case "library":
library, _ := scriptRepo.Get(repo.WithByName(item.ScriptName))
if library.ID == 0 {
hasNotFound = true
break
}
cronjob.ScriptID = library.ID
case "select":
if _, err := os.Stat(item.Script); err != nil {
hasNotFound = true
break
}
cronjob.Script = item.Script
case "input":
cronjob.Script = item.Script
}
case "directory":
if item.IsDir {
if _, err := os.Stat(item.SourceDir); err != nil {
hasNotFound = true
break
}
cronjob.SourceDir = item.SourceDir
} else {
fileList := strings.Split(item.SourceDir, ",")
var newFiles []string
for _, item := range fileList {
if len(item) == 0 {
continue
}
if _, err := os.Stat(item); err != nil {
hasNotFound = true
continue
}
newFiles = append(newFiles, item)
}
cronjob.SourceDir = strings.Join(newFiles, ",")
}
case "snapshot":
if len(item.SnapshotRule.IgnoreApps) == 0 && !item.SnapshotRule.WithImage {
break
}
var itemRules dto.SnapshotRule
itemRules.WithImage = item.SnapshotRule.WithImage
for _, app := range item.SnapshotRule.IgnoreApps {
appItem, err := appInstallRepo.LoadInstallAppByKeyAndName(app.Name, app.DetailName)
if err != nil {
hasNotFound = true
continue
}
itemRules.IgnoreAppIDs = append(itemRules.IgnoreAppIDs, appItem.ID)
}
itemRulesStr, _ := json.Marshal(itemRules)
cronjob.SnapshotRule = string(itemRulesStr)
}
var acIDs []string
for _, ac := range item.SourceAccounts {
backup, err := backupRepo.Get(repo.WithByName(ac))
if err != nil {
hasNotFound = true
continue
}
if ac == item.DownloadAccount {
cronjob.DownloadAccountID = backup.ID
}
acIDs = append(acIDs, fmt.Sprintf("%v", backup.ID))
}
cronjob.SourceAccountIDs = strings.Join(acIDs, ",")
if hasNotFound {
cronjob.Status = constant.StatusPending
} else {
cronjob.Status = constant.StatusDisable
}
_ = cronjobRepo.Create(&cronjob)
if item.AlertCount != 0 && item.AlertTitle != "" && item.AlertMethod != "" {
createAlert := dto.AlertCreate{
Title: item.AlertTitle,
SendCount: item.AlertCount,
Method: item.AlertMethod,
Type: cronjob.Type,
Project: strconv.Itoa(int(cronjob.ID)),
Status: constant.AlertEnable,
}
_ = NewIAlertService().CreateAlert(createAlert)
}
}
return nil
}
func (u *CronjobService) LoadScriptOptions() []dto.ScriptOptions {
scripts, err := scriptRepo.List()
if err != nil {
@@ -203,10 +497,7 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
}
if req.CleanData {
if hasBackup(cronjob.Type) {
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
}
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !req.CleanRemoteData {
for key := range accountMap {
if key != constant.Local {
@@ -240,32 +531,6 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
return nil
}
func (u *CronjobService) Download(req dto.CronjobDownload) (string, error) {
record, _ := cronjobRepo.GetRecord(repo.WithByID(req.RecordID))
if record.ID == 0 {
return "", buserr.New("ErrRecordNotFound")
}
account, client, err := NewBackupClientWithID(req.BackupAccountID)
if err != nil {
return "", err
}
if account.Type == "LOCAL" || record.FromLocal {
if _, err := os.Stat(record.File); err != nil && os.IsNotExist(err) {
return "", err
}
return record.File, nil
}
tempPath := fmt.Sprintf("%s/download/%s", global.Dir.DataDir, record.File)
if _, err := os.Stat(tempPath); err != nil && os.IsNotExist(err) {
_ = os.MkdirAll(path.Dir(tempPath), os.ModePerm)
isOK, err := client.Download(record.File, tempPath)
if !isOK || err != nil {
return "", err
}
}
return tempPath, nil
}
func (u *CronjobService) HandleOnce(id uint) error {
cronjob, _ := cronjobRepo.Get(repo.WithByID(id))
if cronjob.ID == 0 {
@@ -311,7 +576,7 @@ func (u *CronjobService) Create(req dto.CronjobOperate) error {
if err := cronjobRepo.Create(&cronjob); err != nil {
return err
}
if req.AlertCount != 0 {
if req.AlertCount != 0 && req.AlertTitle != "" && req.AlertMethod != "" {
createAlert := dto.AlertCreate{
Title: req.AlertTitle,
SendCount: req.AlertCount,
@@ -411,7 +676,11 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
}
}
if cronModel.Status == constant.StatusPending {
upMap["status"] = constant.StatusEnable
}
upMap["name"] = req.Name
upMap["group_id"] = req.GroupID
upMap["spec_custom"] = req.SpecCustom
upMap["spec"] = spec
upMap["script"] = req.Script
@@ -428,6 +697,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
upMap["db_type"] = req.DBType
upMap["db_name"] = req.DBName
upMap["url"] = req.URL
upMap["is_dir"] = req.IsDir
upMap["source_dir"] = req.SourceDir
upMap["snapshot_rule"] = cronjob.SnapshotRule
@@ -482,6 +752,14 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_ids": entryIDs})
}
func (u *CronjobService) UpdateGroup(req dto.ChangeGroup) error {
cronjob, _ := cronjobRepo.Get(repo.WithByID(req.ID))
if cronjob.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"group_id": req.GroupID})
}
func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
addFunc := func() {
u.HandleJob(cronjob)
+118 -67
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"time"
@@ -24,28 +25,13 @@ import (
)
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
var apps []model.AppInstall
if cronjob.AppID == "all" {
apps, _ = appInstallRepo.ListBy(context.Background())
} else {
appIds := strings.Split(cronjob.AppID, ",")
var idItems []uint
for i := 0; i < len(appIds); i++ {
itemID, _ := strconv.Atoi(appIds[i])
idItems = append(idItems, uint(itemID))
}
appItems, err := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(idItems))
if err != nil {
return err
}
apps = appItems
}
apps := loadAppsForJob(cronjob)
if len(apps) == 0 {
return errors.New("no such app in database!")
}
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
for _, app := range apps {
retry := 0
@@ -58,31 +44,36 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
record.DetailName = app.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/app/%s/%s", app.App.Key, app.Name))
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
record.FileName = simplifiedFileName(fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))
if err := doAppBackup(&app, task, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
} else {
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
cleanAccountMap(accountMap)
return nil
}
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
src := path.Join(backupDir, record.FileName)
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
}
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
cleanAccountMap(accountMap)
return nil
}
record.FileDir = path.Dir(downloadPath)
record.FileDir = path.Dir(dst)
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
cleanAccountMap(accountMap)
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
@@ -94,9 +85,9 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
if len(webs) == 0 {
return errors.New("no such website in database!")
}
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
for _, web := range webs {
retry := 0
@@ -109,7 +100,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
record.DetailName = web.Alias
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/website/%s", web.Alias))
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.Alias, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
record.FileName = simplifiedFileName(fmt.Sprintf("website_%s_%s.tar.gz", web.Alias, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))
if err := doWebsiteBackup(&web, taskItem, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
@@ -117,25 +108,29 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
return err
} else {
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
cleanAccountMap(accountMap)
return nil
}
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
src := path.Join(backupDir, record.FileName)
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
}
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
cleanAccountMap(accountMap)
return nil
}
record.FileDir = path.Dir(downloadPath)
record.FileDir = path.Dir(dst)
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
cleanAccountMap(accountMap)
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
@@ -147,9 +142,9 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
if len(dbs) == 0 {
return errors.New("no such db in database!")
}
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
for _, dbInfo := range dbs {
retry := 0
@@ -164,7 +159,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
record.FileName = simplifiedFileName(fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)))
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" || cronjob.DBType == "mysql-cluster" {
if err := doMysqlBackup(dbInfo, backupDir, record.FileName); err != nil {
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
@@ -172,6 +167,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
return err
} else {
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
cleanAccountMap(accountMap)
return nil
}
}
@@ -182,26 +178,30 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
return err
} else {
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
cleanAccountMap(accountMap)
return nil
}
}
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
src := path.Join(backupDir, record.FileName)
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
}
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
cleanAccountMap(accountMap)
return nil
}
record.FileDir = path.Dir(downloadPath)
record.FileDir = path.Dir(dst)
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
cleanAccountMap(accountMap)
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
@@ -209,12 +209,16 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
}
func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
taskItem.AddSubTaskWithOps(task.GetTaskName(cronjob.SourceDir, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
fileName := fmt.Sprintf("%s.tar.gz", startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(2))
if cronjob.IsDir || len(strings.Split(cronjob.SourceDir, ",")) == 1 {
fileName = loadFileName(cronjob.SourceDir)
}
fileName := fmt.Sprintf("%s.tar.gz", startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
fileName = simplifiedFileName(fileName)
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/%s/%s", cronjob.Type, cronjob.Name))
fileOp := files.NewFileOp()
@@ -236,15 +240,15 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, fileName))
if err != nil {
taskItem.LogFailedWithErr("Upload backup file", err)
src := path.Join(backupDir, fileName)
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
record.FileDir = path.Dir(dst)
record.FileName = fileName
if err := backupRepo.CreateRecord(&record); err != nil {
taskItem.LogFailedWithErr("Save record", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
@@ -254,11 +258,11 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
}
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
taskItem.AddSubTaskWithOps(task.GetTaskName(i18n.GetMsgByKey("BackupSystemLog"), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
}
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
taskItem.AddSubTaskWithOps(task.GetTaskName(i18n.GetMsgByKey("SystemLog"), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
nameItem := startTime.Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
fileName := fmt.Sprintf("system_log_%s.tar.gz", nameItem)
backupDir := path.Join(global.Dir.LocalBackupDir, "tmp/log", nameItem)
@@ -271,15 +275,15 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(path.Dir(backupDir), fileName))
if err != nil {
taskItem.LogFailedWithErr("Upload backup file", err)
src := path.Join(path.Dir(backupDir), fileName)
dst := strings.TrimPrefix(src, global.Dir.LocalBackupDir+"/tmp/")
if err := uploadWithMap(*task, accountMap, src, dst, cronjob.SourceAccountIDs, cronjob.DownloadAccountID, cronjob.RetryTimes); err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
record.FileDir = path.Dir(dst)
record.FileName = fileName
if err := backupRepo.CreateRecord(&record); err != nil {
taskItem.LogFailedWithErr("Save record", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
@@ -289,11 +293,10 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
}
func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.JobRecords, taskItem *task.Task) error {
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
var record model.BackupRecord
record.From = "cronjob"
record.Type = "snapshot"
@@ -313,9 +316,10 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
return err
}
req := dto.SnapshotCreate{
Name: fmt.Sprintf("snapshot-1panel-%s-%s-linux-%s-%s", scope, versionItem.Value, loadOs(), jobRecord.StartTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)),
Secret: cronjob.Secret,
TaskID: jobRecord.TaskID,
Name: fmt.Sprintf("snapshot-1panel-%s-%s-linux-%s-%s", scope, versionItem.Value, loadOs(), jobRecord.StartTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)),
Secret: cronjob.Secret,
TaskID: jobRecord.TaskID,
Timeout: cronjob.Timeout,
SourceAccountIDs: record.SourceAccountIDs,
DownloadAccountID: cronjob.DownloadAccountID,
@@ -331,7 +335,7 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
IgnoreFiles: strings.Split(cronjob.ExclusionRules, ","),
}
if err := NewISnapshotService().SnapshotCreate(taskItem, req, jobRecord.ID, cronjob.RetryTimes, cronjob.Timeout); err != nil {
if err := NewISnapshotService().SnapshotCreate(taskItem, req, jobRecord.ID, cronjob.RetryTimes); err != nil {
return err
}
record.FileName = req.Name + ".tar.gz"
@@ -344,6 +348,23 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
return nil
}
func loadAppsForJob(cronjob model.Cronjob) []model.AppInstall {
var apps []model.AppInstall
if cronjob.AppID == "all" {
apps, _ = appInstallRepo.ListBy(context.Background())
} else {
appIds := strings.Split(cronjob.AppID, ",")
var idItems []uint
for i := 0; i < len(appIds); i++ {
itemID, _ := strconv.Atoi(appIds[i])
idItems = append(idItems, uint(itemID))
}
appItems, _ := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(idItems))
apps = appItems
}
return apps
}
type DatabaseHelper struct {
ID uint
DBType string
@@ -454,6 +475,7 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
}
}
}
taskItem.Logf("%s Website logs...", i18n.GetMsgByKey("TaskBackup"))
systemDir := path.Join(targetDir, "system")
if _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {
@@ -462,7 +484,6 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
}
}
taskItem.Logf("%s System logs...", i18n.GetMsgByKey("TaskBackup"))
systemLogFiles, _ := os.ReadDir(global.Dir.LogDir)
if len(systemLogFiles) != 0 {
for i := 0; i < len(systemLogFiles); i++ {
@@ -471,8 +492,8 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
}
}
}
taskItem.Logf("%s System logs...", i18n.GetMsgByKey("TaskBackup"))
taskItem.Logf("%s SSH logs...", i18n.GetMsgByKey("TaskBackup"))
loginLogFiles, _ := os.ReadDir("/var/log")
loginDir := path.Join(targetDir, "login")
if _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {
@@ -487,7 +508,7 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
}
}
}
taskItem.Log("backup ssh log successful!")
taskItem.Logf("%s SSH logs...", i18n.GetMsgByKey("TaskBackup"))
if err := fileOp.TarGzCompressPro(true, targetDir, path.Join(path.Dir(targetDir), fileName), secret, ""); err != nil {
return err
@@ -546,3 +567,33 @@ func loadSnapWithRule(cronjob model.Cronjob) (dto.SnapshotData, error) {
}
return itemData, nil
}
func loadFileName(src string) string {
dirs := strings.Split(filepath.ToSlash(src), "/")
var keyPart string
if len(dirs) >= 3 {
keyPart = filepath.Join(dirs[len(dirs)-3], dirs[len(dirs)-2], dirs[len(dirs)-1])
}
cleanName := strings.ReplaceAll(keyPart, string(filepath.Separator), "_")
timestamp := time.Now().Format(constant.DateTimeSlimLayout)
return fmt.Sprintf("%s_%s_%s.tar.gz", cleanName, timestamp, common.RandStrAndNum(2))
}
func simplifiedFileName(name string) string {
name = strings.ReplaceAll(name, "/", "_")
name = strings.ReplaceAll(name, ":", "_")
name = strings.ReplaceAll(name, "*", "_")
name = strings.ReplaceAll(name, "?", "_")
name = strings.ReplaceAll(name, "\"", "_")
name = strings.ReplaceAll(name, "<", "_")
name = strings.ReplaceAll(name, ">", "_")
name = strings.ReplaceAll(name, "|", "_")
return name
}
func cleanAccountMap(accountMap map[string]backupClientHelper) {
for key, val := range accountMap {
val.hasBackup = false
accountMap[key] = val
}
}
+42 -54
View File
@@ -10,6 +10,7 @@ import (
"time"
"github.com/1Panel-dev/1Panel/agent/utils/alert_push"
"github.com/pkg/errors"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
@@ -25,33 +26,45 @@ import (
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
record := cronjobRepo.StartRecords(cronjob.ID, "", cronjob.Type)
go func() {
taskItem, err := task.NewTaskWithOps(fmt.Sprintf("cronjob-%s", cronjob.Name), task.TaskHandle, task.TaskScopeCronjob, record.TaskID, cronjob.ID)
if err != nil {
global.LOG.Errorf("new task for exec shell failed, err: %v", err)
return
}
err = u.loadTask(cronjob, &record, taskItem)
if cronjob.Type == "snapshot" {
if err != nil {
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
if len(taskItem.ID) == 0 {
record.TaskID = ""
taskItem, err := task.NewTaskWithOps(fmt.Sprintf("cronjob-%s", cronjob.Name), task.TaskHandle, task.TaskScopeCronjob, record.TaskID, cronjob.ID)
if err != nil {
global.LOG.Errorf("new task for exec shell failed, err: %v", err)
return
}
if cronjob.Type == "snapshot" {
go func() {
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
if err := taskRepo.Save(context.Background(), taskItem.Task); err != nil {
global.LOG.Errorf("save task for snapshot cronjob failed, err: %v", err)
return
}
if err = u.handleSnapshot(*cronjob, record, taskItem); err != nil {
if len(taskItem.Task.CurrentStep) == 0 {
taskItem.Log(err.Error())
taskItem.Task.Status = constant.StatusFailed
taskItem.Task.ErrorMsg = err.Error()
taskItem.Task.EndAt = time.Now()
_ = taskRepo.Save(context.Background(), taskItem.Task)
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
handleCronJobAlert(cronjob)
return
}
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
return
}
if err != nil {
global.LOG.Debugf("preper to handle cron job [%s] %s failed, err: %v", cronjob.Type, cronjob.Name, err)
}()
return
}
if err = u.loadTask(cronjob, &record, taskItem); err != nil {
global.LOG.Debugf("preper to handle cron job [%s] %s failed, err: %v", cronjob.Type, cronjob.Name, err)
item, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
if len(item.ID) == 0 {
record.TaskID = ""
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
handleCronJobAlert(cronjob)
return
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
handleCronJobAlert(cronjob)
return
}
go func() {
if err := taskItem.Execute(); err != nil {
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
if len(taskItem.ID) == 0 {
@@ -109,9 +122,6 @@ func (u *CronjobService) loadTask(cronjob *model.Cronjob, record *model.JobRecor
err = u.handleDirectory(*cronjob, record.StartTime, taskItem)
case "log":
err = u.handleSystemLog(*cronjob, record.StartTime, taskItem)
case "snapshot":
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
err = u.handleSnapshot(*cronjob, *record, taskItem)
}
return err
}
@@ -191,14 +201,14 @@ func (u *CronjobService) handleNtpSync(cronjob model.Cronjob, taskItem *task.Tas
}
func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime time.Time, taskItem *task.Task) error {
clientMap := NewBackupClientMap([]string{fmt.Sprintf("%v", cronjob.DownloadAccountID)})
if !clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].isOk {
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", clientMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
taskItem.AddSubTaskWithOps(i18n.GetWithName("CutWebsiteLog", cronjob.Name), func(t *task.Task) error {
websites := loadWebsForJob(*cronjob)
fileOp := files.NewFileOp()
baseDir := GetOpenrestyDir(SitesRootDir)
clientMap, err := NewBackupClientMap([]string{fmt.Sprintf("%v", cronjob.DownloadAccountID)})
if err != nil {
return fmt.Errorf("load local backup client failed, err: %v", err)
}
for _, website := range websites {
taskItem.Log(website.Alias)
var record model.BackupRecord
@@ -264,34 +274,6 @@ func (u *CronjobService) handleSystemClean(cronjob model.Cronjob, taskItem *task
taskItem.AddSubTaskWithOps(i18n.GetMsgByKey("HandleSystemClean"), cleanTask, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, task *task.Task, accountMap map[string]backupClientHelper, file string) (string, error) {
defer func() {
_ = os.Remove(file)
}()
var errItem error
accounts := strings.Split(cronjob.SourceAccountIDs, ",")
cloudSrc := strings.TrimPrefix(file, global.Dir.LocalBackupDir+"/tmp/")
for _, account := range accounts {
if len(account) != 0 {
task.LogStart(i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
"file": pathUtils.Join(accountMap[account].backupPath, cloudSrc),
"backup": accountMap[account].name,
}))
_, err := accountMap[account].client.Upload(file, pathUtils.Join(accountMap[account].backupPath, cloudSrc))
task.LogWithStatus(
i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
"file": pathUtils.Join(accountMap[account].backupPath, cloudSrc),
"backup": accountMap[account].name,
}), err)
if err != nil {
errItem = err
continue
}
}
}
return cloudSrc, errItem
}
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]backupClientHelper, record model.BackupRecord) {
var opts []repo.DBOption
opts = append(opts, repo.WithByFrom("cronjob"))
@@ -314,6 +296,9 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
if _, ok := accountMap[account]; !ok {
continue
}
if !accountMap[account].isOk {
continue
}
_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName))
}
}
@@ -324,6 +309,9 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
if _, ok := accountMap[account]; !ok {
continue
}
if !accountMap[account].isOk {
continue
}
_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName))
}
}
+19 -25
View File
@@ -22,12 +22,12 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/copier"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/host"
"github.com/shirou/gopsutil/v4/load"
"github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net"
)
type DashboardService struct{}
@@ -268,10 +268,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
}
func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error) {
var (
data []dto.AppLauncher
recommendList []dto.AppLauncher
)
var data []dto.AppLauncher
appInstalls, err := appInstallRepo.ListBy(context.Background())
if err != nil {
return data, err
@@ -281,8 +278,11 @@ func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher,
return data, err
}
showList, _ := launcherRepo.ListName()
defaultList := []string{"openresty", "mysql", "halo", "redis", "maxkb", "wordpress"}
showList, err := launcherRepo.ListName()
defaultList, err := appRepo.GetTopRecomment()
if err != nil {
return data, nil
}
allList := common.RemoveRepeatStr(append(defaultList, showList...))
for _, showItem := range allList {
var itemData dto.AppLauncher
@@ -317,24 +317,18 @@ func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher,
})
}
}
if ArryContains(defaultList, showItem) && len(itemData.Detail) == 0 {
itemData.IsRecommend = true
recommendList = append(recommendList, itemData)
continue
if (ArryContains(showList, showItem) && len(itemData.Detail) != 0) ||
(ArryContains(defaultList, showItem) && len(itemData.Detail) == 0) {
data = append(data, itemData)
}
if !ArryContains(showList, showItem) && len(itemData.Detail) != 0 {
continue
}
data = append(data, itemData)
}
sort.Slice(recommendList, func(i, j int) bool {
return recommendList[i].Recommend < recommendList[j].Recommend
})
sort.Slice(data, func(i, j int) bool {
return data[i].Name < data[j].Name
if data[i].IsInstall != data[j].IsInstall {
return data[i].IsInstall
}
return data[i].Recommend < data[j].Recommend
})
data = append(data, recommendList...)
return data, nil
}
+20 -12
View File
@@ -6,19 +6,17 @@ import (
"os"
"path"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/utils/postgresql"
pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
redisclient "github.com/1Panel-dev/1Panel/agent/utils/redis"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/mysql"
"github.com/1Panel-dev/1Panel/agent/utils/mysql/client"
"github.com/1Panel-dev/1Panel/agent/utils/postgresql"
pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
redisclient "github.com/1Panel-dev/1Panel/agent/utils/redis"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
@@ -118,6 +116,9 @@ func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
}
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
if req.Timeout == 0 {
req.Timeout = 30
}
switch req.Type {
case constant.AppPostgresql:
_, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
@@ -126,7 +127,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
Timeout: req.Timeout,
})
return err == nil
case constant.AppRedis:
@@ -134,6 +135,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
Address: req.Address,
Port: req.Port,
Password: req.Password,
Timeout: req.Timeout,
})
return err == nil
case "mysql", "mariadb":
@@ -149,7 +151,7 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
ClientKey: req.ClientKey,
ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify,
Timeout: 6,
Timeout: req.Timeout,
})
return err == nil
}
@@ -158,6 +160,9 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
}
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
if req.Timeout == 0 {
req.Timeout = 30
}
db, _ := databaseRepo.Get(repo.WithByName(req.Name))
if db.ID != 0 {
if db.From == "local" {
@@ -173,7 +178,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 6,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -182,6 +187,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
Address: req.Address,
Port: req.Port,
Password: req.Password,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -198,7 +204,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
ClientKey: req.ClientKey,
ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify,
Timeout: 6,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -273,7 +279,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: 300,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -282,6 +288,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
Address: req.Address,
Port: req.Port,
Password: req.Password,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -298,7 +305,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
ClientKey: req.ClientKey,
ClientCert: req.ClientCert,
SkipVerify: req.SkipVerify,
Timeout: 300,
Timeout: req.Timeout,
}); err != nil {
return err
}
@@ -318,6 +325,7 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
upMap["port"] = req.Port
upMap["username"] = req.Username
upMap["password"] = pass
upMap["timeout"] = req.Timeout
upMap["description"] = req.Description
upMap["ssl"] = req.SSL
upMap["client_key"] = req.ClientKey
+3 -3
View File
@@ -74,11 +74,11 @@ func (u *DBCommonService) UpdateConfByFile(req dto.DBConfUpdateByFile) error {
}
path := ""
switch req.Type {
case constant.AppMariaDB, constant.AppMysql:
case constant.AppMariaDB, constant.AppMysql, constant.AppMysqlCluster:
path = fmt.Sprintf("%s/%s/%s/conf/my.cnf", global.Dir.AppInstallDir, req.Type, app.Name)
case constant.AppPostgresql:
case constant.AppPostgresql, constant.AppPostgresqlCluster:
path = fmt.Sprintf("%s/%s/%s/data/postgresql.conf", global.Dir.AppInstallDir, req.Type, app.Name)
case constant.AppRedis:
case constant.AppRedis, constant.AppRedisCluster:
path = fmt.Sprintf("%s/%s/%s/conf/redis.conf", global.Dir.AppInstallDir, req.Type, app.Name)
}
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
+43 -11
View File
@@ -6,7 +6,7 @@ import (
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
@@ -42,7 +42,7 @@ type IMysqlService interface {
ChangePassword(info dto.ChangeDBInfo) error
UpdateVariables(req dto.MysqlVariablesUpdate) error
UpdateDescription(req dto.UpdateDescription) error
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]dto.DBResource, error)
Delete(ctx context.Context, req dto.MysqlDBDelete) error
LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)
@@ -229,23 +229,41 @@ func (u *MysqlService) UpdateDescription(req dto.UpdateDescription) error {
return mysqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
}
func (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error) {
var appInUsed []string
func (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]dto.DBResource, error) {
var res []dto.DBResource
db, err := mysqlRepo.Get(repo.WithByID(req.ID))
if err != nil {
return appInUsed, err
return res, err
}
website, _ := websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppMysql), websiteRepo.WithDBID(req.ID))
if website.ID != 0 {
res = append(res, dto.DBResource{
Type: constant.TypeWebsite,
Name: website.PrimaryDomain,
})
}
website, _ = websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppMysqlCluster), websiteRepo.WithDBID(req.ID))
if website.ID != 0 {
res = append(res, dto.DBResource{
Type: constant.TypeWebsite,
Name: website.PrimaryDomain,
})
}
if db.From == "local" {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return appInUsed, err
return res, err
}
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
appInUsed = append(appInUsed, appInstall.Name)
res = append(res, dto.DBResource{
Type: constant.TypeApp,
Name: appInstall.Name,
})
}
}
} else {
@@ -253,12 +271,15 @@ func (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
appInUsed = append(appInUsed, appInstall.Name)
res = append(res, dto.DBResource{
Type: constant.TypeApp,
Name: appInstall.Name,
})
}
}
}
return appInUsed, nil
return res, nil
}
func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error {
@@ -282,11 +303,11 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
}
if req.DeleteBackup {
uploadDir := path.Join(global.Dir.DataDir, fmt.Sprintf("uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
uploadDir := filepath.Join(global.Dir.DataDir, fmt.Sprintf("uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
}
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("database/%s/%s/%s", req.Type, db.MysqlName, db.Name))
backupDir := filepath.Join(global.Dir.LocalBackupDir, fmt.Sprintf("database/%s/%s/%s", req.Type, db.MysqlName, db.Name))
if _, err := os.Stat(backupDir); err == nil {
_ = os.RemoveAll(backupDir)
}
@@ -438,6 +459,17 @@ func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
group := "[mysqld]"
for _, info := range req.Variables {
if info.Param == "slow_query_log" && info.Value == "ON" {
logFilePath := filepath.Join(global.Dir.DataDir, fmt.Sprintf("apps/%s/%s/data/1Panel-slow.log", app.Key, app.Name))
if req.Type == "mariadb" {
logFilePath = filepath.Join(global.Dir.DataDir, fmt.Sprintf("apps/%s/%s/db/data/1Panel-slow.log", app.Key, app.Name))
}
file, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer file.Close()
}
if !strings.HasPrefix(app.Version, "5.7") && !strings.HasPrefix(app.Version, "5.6") {
if info.Param == "query_cache_size" {
continue
+34 -8
View File
@@ -7,6 +7,8 @@ import (
"path"
"strings"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/dto"
@@ -33,7 +35,7 @@ type IPostgresqlService interface {
ChangePrivileges(req dto.PostgresqlPrivileges) error
ChangePassword(info dto.ChangeDBInfo) error
UpdateDescription(req dto.UpdateDescription) error
DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error)
DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]dto.DBResource, error)
Delete(ctx context.Context, req dto.PostgresqlDBDelete) error
}
@@ -250,23 +252,41 @@ func (u *PostgresqlService) UpdateDescription(req dto.UpdateDescription) error {
return postgresqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
}
func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]string, error) {
var appInUsed []string
func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]dto.DBResource, error) {
var res []dto.DBResource
db, err := postgresqlRepo.Get(repo.WithByID(req.ID))
if err != nil {
return appInUsed, err
return res, err
}
website, _ := websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppPostgresql), websiteRepo.WithDBID(req.ID))
if website.ID != 0 {
res = append(res, dto.DBResource{
Type: constant.TypeWebsite,
Name: website.PrimaryDomain,
})
}
website, _ = websiteRepo.GetFirst(websiteRepo.WithDBType(constant.AppPostgresqlCluster), websiteRepo.WithDBID(req.ID))
if website.ID != 0 {
res = append(res, dto.DBResource{
Type: constant.TypeWebsite,
Name: website.PrimaryDomain,
})
}
if db.From == "local" {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return appInUsed, err
return res, err
}
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
appInUsed = append(appInUsed, appInstall.Name)
res = append(res, dto.DBResource{
Type: constant.TypeApp,
Name: appInstall.Name,
})
}
}
} else {
@@ -274,12 +294,15 @@ func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]stri
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(repo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
appInUsed = append(appInUsed, appInstall.Name)
res = append(res, dto.DBResource{
Type: constant.TypeApp,
Name: appInstall.Name,
})
}
}
}
return appInUsed, nil
return res, nil
}
func (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDelete) error {
@@ -298,6 +321,9 @@ func (u *PostgresqlService) Delete(ctx context.Context, req dto.PostgresqlDBDele
ForceDelete: req.ForceDelete,
Timeout: 300,
}); err != nil && !req.ForceDelete {
if strings.HasPrefix(err.Error(), "drop user") {
_ = postgresqlRepo.Update(db.ID, map[string]interface{}{"is_delete": true})
}
return err
}
+5 -1
View File
@@ -53,7 +53,7 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
if err := confSet(redisInfo.Name, req.DBType, "", confs); err != nil {
return err
}
if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", global.Dir.AppInstallDir, redisInfo.Name)); err != nil {
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", global.Dir.AppInstallDir, req.DBType, redisInfo.Name)); err != nil {
return err
}
@@ -244,6 +244,10 @@ func confSet(redisName string, redisType string, updateType string, changeConf [
}
newFiles = append(newFiles, files[i])
}
if startIndex == 0 {
newFiles = append(newFiles, "# Redis configuration rewrite by 1Panel")
startIndex = len(newFiles) - 1
}
endIndex = endIndex - emptyLine
for _, item := range changeConf {
if item.key == "save" {
+1 -1
View File
@@ -18,7 +18,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v4/mem"
)
const defaultDNSPath = "/etc/resolv.conf"
+83 -194
View File
@@ -11,6 +11,7 @@ import (
"time"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/build"
@@ -78,19 +79,19 @@ func (u *DeviceService) Scan() dto.CleanData {
}
treeData = append(treeData, upgradeTree)
snapTree := loadSnapshotTree(fileOp)
snapSize := uint64(0)
for _, snap := range snapTree {
snapSize += snap.Size
tmpBackupTree := loadTmpBackupTree(fileOp)
tmpBackupSize := uint64(0)
for _, tmp := range tmpBackupTree {
tmpBackupSize += tmp.Size
}
treeData = append(treeData, dto.CleanTree{
ID: uuid.NewString(),
Label: "snapshot",
Size: snapSize,
IsCheck: snapSize > 0,
Label: "backup",
Size: tmpBackupSize,
IsCheck: tmpBackupSize > 0,
IsRecommend: true,
Type: "snapshot",
Children: snapTree,
Type: "backup",
Children: tmpBackupTree,
})
rollBackTree := loadRollBackTree(fileOp)
@@ -118,21 +119,6 @@ func (u *DeviceService) Scan() dto.CleanData {
IsRecommend: false,
Type: "cache",
})
unusedTree := loadUnusedFile(fileOp)
unusedSize := uint64(0)
for _, unused := range unusedTree {
unusedSize += unused.Size
}
treeData = append(treeData, dto.CleanTree{
ID: uuid.NewString(),
Label: "unused",
Size: unusedSize,
IsCheck: unusedSize > 0,
IsRecommend: true,
Type: "unused",
Children: unusedTree,
})
SystemClean.SystemClean = treeData
uploadTreeData := loadUploadTree(fileOp)
@@ -162,13 +148,13 @@ func (u *DeviceService) Clean(req []dto.Clean) {
case "upgrade":
dropFileOrDir(path.Join(global.Dir.BaseDir, upgradePath, item.Name))
case "snapshot":
dropFileOrDir(path.Join(global.Dir.BaseDir, snapshotTmpPath, item.Name))
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "system", item.Name))
case "snapshot_tmp":
dropFileOrDir(path.Join(global.Dir.BaseDir, snapshotTmpPath, item.Name))
case "snapshot_local":
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "system", item.Name))
case "backup":
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/app"))
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/database"))
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/website"))
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/directory"))
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/log"))
dropFileOrDir(path.Join(global.Dir.LocalBackupDir, "tmp/system"))
case "rollback":
dropFileOrDir(path.Join(global.Dir.BaseDir, rollbackPath, "app"))
@@ -185,40 +171,6 @@ func (u *DeviceService) Clean(req []dto.Clean) {
dropFileOrDir(path.Join(global.Dir.BaseDir, cachePath, item.Name))
restart = true
case "unused":
dropFileOrDir(path.Join(global.Dir.BaseDir, oldOriginalPath))
dropFileOrDir(path.Join(global.Dir.BaseDir, oldAppBackupPath))
dropFileOrDir(path.Join(global.Dir.BaseDir, oldDownloadPath))
files, _ := os.ReadDir(path.Join(global.Dir.BaseDir, oldUpgradePath))
if len(files) == 0 {
continue
}
for _, file := range files {
if strings.HasPrefix(file.Name(), "upgrade_") {
dropFileOrDir(path.Join(global.Dir.BaseDir, oldUpgradePath, file.Name()))
}
}
case "old_original":
dropFileOrDir(path.Join(global.Dir.BaseDir, oldOriginalPath, item.Name))
case "old_apps_bak":
dropFileOrDir(path.Join(global.Dir.BaseDir, oldAppBackupPath, item.Name))
case "old_download":
dropFileOrDir(path.Join(global.Dir.BaseDir, oldDownloadPath, item.Name))
case "old_upgrade":
if len(item.Name) == 0 {
files, _ := os.ReadDir(path.Join(global.Dir.BaseDir, oldUpgradePath))
if len(files) == 0 {
continue
}
for _, file := range files {
if strings.HasPrefix(file.Name(), "upgrade_") {
dropFileOrDir(path.Join(global.Dir.BaseDir, oldUpgradePath, file.Name()))
}
}
} else {
dropFileOrDir(path.Join(global.Dir.BaseDir, oldUpgradePath, item.Name))
}
case "upload":
dropFileOrDir(path.Join(global.Dir.BaseDir, uploadPath, item.Name))
if len(item.Name) == 0 {
@@ -283,13 +235,13 @@ func (u *DeviceService) Clean(req []dto.Clean) {
case "script":
dropFileOrDir(path.Join(global.Dir.TmpDir, "script", item.Name))
case "images":
dropImages()
_, _ = dropImages()
case "containers":
dropContainers()
_, _ = dropContainers()
case "volumes":
dropVolumes()
_, _ = dropVolumes()
case "build_cache":
dropBuildCache()
_, _ = dropBuildCache()
}
}
@@ -322,7 +274,7 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
}
dropWithTask(path.Join(global.Dir.BaseDir, snapshotTmpPath), taskItem, &size, &fileCount)
dropWithTask(path.Join(global.Dir.LocalBackupDir, "system"), taskItem, &size, &fileCount)
dropWithTask(path.Join(global.Dir.LocalBackupDir, "tmp/system"), taskItem, &size, &fileCount)
dropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, "app"), taskItem, &size, &fileCount)
dropWithTask(path.Join(global.Dir.BaseDir, rollbackPath, "website"), taskItem, &size, &fileCount)
@@ -354,9 +306,17 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
}
}
}
count1, size1 := dropVolumes()
size += int64(size1)
fileCount += count1
count2, size2 := dropBuildCache()
size += int64(size2)
fileCount += count2
timeNow := time.Now().Format(constant.DateTimeLayout)
if fileCount != 0 {
taskItem.LogSuccessF("%s: total clean: %s, total count: %d", timeNow, common.LoadSizeUnit2F(float64(size)), fileCount)
taskItem.Log(i18n.GetMsgWithMap("FileDropSum", map[string]interface{}{"size": common.LoadSizeUnit2F(float64(size)), "count": fileCount}))
}
_ = settingRepo.Update("LastCleanTime", timeNow)
@@ -367,106 +327,32 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
}
}
func loadSnapshotTree(fileOp fileUtils.FileOp) []dto.CleanTree {
func loadTmpBackupTree(fileOp fileUtils.FileOp) []dto.CleanTree {
var treeData []dto.CleanTree
path1 := path.Join(global.Dir.BaseDir, snapshotTmpPath)
list1 := loadTreeWithAllFile(true, path1, "snapshot_tmp", path1, fileOp)
if len(list1) != 0 {
size, _ := fileOp.GetDirSize(path1)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "snapshot_tmp", Size: uint64(size), Children: list1, Type: "snapshot_tmp", IsRecommend: true})
}
path2 := path.Join(global.Dir.LocalBackupDir, "system")
list2 := loadTreeWithAllFile(true, path2, "snapshot_local", path2, fileOp)
if len(list2) != 0 {
size, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "snapshot_local", Size: uint64(size), Children: list2, Type: "snapshot_local", IsRecommend: true})
}
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/app"), "tmp_backup_app", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/website"), "tmp_backup_website", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/database"), "tmp_backup_database", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/system"), "tmp_backup_snapshot", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/directory"), "tmp_backup_directory", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.LocalBackupDir, "tmp/log"), "tmp_backup_log", fileOp)
return treeData
}
func loadRollBackTree(fileOp fileUtils.FileOp) []dto.CleanTree {
var treeData []dto.CleanTree
path1 := path.Join(global.Dir.BaseDir, rollbackPath, "app")
list1 := loadTreeWithAllFile(true, path1, "rollback_app", path1, fileOp)
size1, _ := fileOp.GetDirSize(path1)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "rollback_app", Size: uint64(size1), Children: list1, Type: "rollback_app", IsRecommend: true})
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, "app"), "rollback_app", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, "website"), "rollback_website", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, rollbackPath, "database"), "rollback_database", fileOp)
path2 := path.Join(global.Dir.BaseDir, rollbackPath, "website")
list2 := loadTreeWithAllFile(true, path2, "rollback_website", path2, fileOp)
size2, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "rollback_website", Size: uint64(size2), Children: list2, Type: "rollback_website", IsRecommend: true})
path3 := path.Join(global.Dir.BaseDir, rollbackPath, "database")
list3 := loadTreeWithAllFile(true, path3, "rollback_database", path3, fileOp)
size3, _ := fileOp.GetDirSize(path3)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "rollback_database", Size: uint64(size3), Children: list3, Type: "rollback_database", IsRecommend: true})
return treeData
}
func loadUnusedFile(fileOp fileUtils.FileOp) []dto.CleanTree {
var treeData []dto.CleanTree
path1 := path.Join(global.Dir.BaseDir, oldOriginalPath)
list1 := loadTreeWithAllFile(true, path1, "old_original", path1, fileOp)
if len(list1) != 0 {
size, _ := fileOp.GetDirSize(path1)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_original", Size: uint64(size), Children: list1, Type: "old_original"})
}
path2 := path.Join(global.Dir.BaseDir, oldAppBackupPath)
list2 := loadTreeWithAllFile(true, path2, "old_apps_bak", path2, fileOp)
if len(list2) != 0 {
size, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_apps_bak", Size: uint64(size), Children: list2, Type: "old_apps_bak"})
}
path3 := path.Join(global.Dir.BaseDir, oldDownloadPath)
list3 := loadTreeWithAllFile(true, path3, "old_download", path3, fileOp)
if len(list3) != 0 {
size, _ := fileOp.GetDirSize(path3)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_download", Size: uint64(size), Children: list3, Type: "old_download"})
}
path4 := path.Join(global.Dir.BaseDir, oldUpgradePath)
list4 := loadTreeWithDir(true, "old_upgrade", path4, fileOp)
itemSize := uint64(0)
for _, item := range list4 {
itemSize += item.Size
}
if len(list4) != 0 {
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_upgrade", Size: itemSize, Children: list4, Type: "old_upgrade"})
}
return treeData
}
func loadUploadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
var treeData []dto.CleanTree
path0 := path.Join(global.Dir.BaseDir, tmpUploadPath)
list0 := loadTreeWithAllFile(true, path0, "upload_tmp", path0, fileOp)
size0, _ := fileOp.GetDirSize(path0)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_tmp", Size: uint64(size0), Children: list0, Type: "upload_tmp", IsRecommend: true})
path1 := path.Join(global.Dir.BaseDir, uploadPath, "app")
list1 := loadTreeWithAllFile(true, path1, "upload_app", path1, fileOp)
size1, _ := fileOp.GetDirSize(path1)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_app", Size: uint64(size1), Children: list1, Type: "upload_app", IsRecommend: true})
path2 := path.Join(global.Dir.BaseDir, uploadPath, "website")
list2 := loadTreeWithAllFile(true, path2, "upload_website", path2, fileOp)
size2, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_website", Size: uint64(size2), Children: list2, Type: "upload_website", IsRecommend: true})
path3 := path.Join(global.Dir.BaseDir, uploadPath, "database")
list3 := loadTreeWithAllFile(true, path3, "upload_database", path3, fileOp)
size3, _ := fileOp.GetDirSize(path3)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_database", Size: uint64(size3), Children: list3, Type: "upload_database", IsRecommend: true})
path4 := path.Join(global.Dir.BaseDir, uploadPath, "directory")
list4 := loadTreeWithAllFile(true, path4, "upload_directory", path4, fileOp)
size4, _ := fileOp.GetDirSize(path4)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_directory", Size: uint64(size4), Children: list4, Type: "upload_directory", IsRecommend: true})
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, tmpUploadPath), "upload_tmp", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, uploadPath, "app"), "upload_app", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, uploadPath, "website"), "upload_website", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, uploadPath, "database"), "upload_database", fileOp)
path5 := path.Join(global.Dir.BaseDir, uploadPath)
uploadTreeData := loadTreeWithAllFile(true, path5, "upload", path5, fileOp)
@@ -477,25 +363,9 @@ func loadUploadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
func loadDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
var treeData []dto.CleanTree
path1 := path.Join(global.Dir.BaseDir, downloadPath, "app")
list1 := loadTreeWithAllFile(true, path1, "download_app", path1, fileOp)
size1, _ := fileOp.GetDirSize(path1)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_app", Size: uint64(size1), Children: list1, Type: "download_app", IsRecommend: true})
path2 := path.Join(global.Dir.BaseDir, downloadPath, "website")
list2 := loadTreeWithAllFile(true, path2, "download_website", path2, fileOp)
size2, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_website", Size: uint64(size2), Children: list2, Type: "download_website", IsRecommend: true})
path3 := path.Join(global.Dir.BaseDir, downloadPath, "database")
list3 := loadTreeWithAllFile(true, path3, "download_database", path3, fileOp)
size3, _ := fileOp.GetDirSize(path3)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_database", Size: uint64(size3), Children: list3, Type: "download_database", IsRecommend: true})
path4 := path.Join(global.Dir.BaseDir, downloadPath, "directory")
list4 := loadTreeWithAllFile(true, path4, "download_directory", path4, fileOp)
size4, _ := fileOp.GetDirSize(path4)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_directory", Size: uint64(size4), Children: list4, Type: "download_directory", IsRecommend: true})
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, downloadPath, "app"), "download_app", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, downloadPath, "website"), "download_website", fileOp)
treeData = loadTreeWithCheck(treeData, path.Join(global.Dir.BaseDir, downloadPath, "database"), "download_database", fileOp)
path5 := path.Join(global.Dir.BaseDir, downloadPath)
uploadTreeData := loadTreeWithAllFile(true, path5, "download", path5, fileOp)
@@ -558,7 +428,7 @@ func loadContainerTree() []dto.CleanTree {
volumeSize += uint64(file.UsageData.Size)
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_volumes", Size: volumeSize, Children: nil, Type: "volumes", IsRecommend: true})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_volumes", Size: volumeSize, IsCheck: volumeSize > 0, Children: nil, Type: "volumes", IsRecommend: true})
var buildCacheTotalSize int64
for _, cache := range diskUsage.BuildCache {
@@ -566,7 +436,17 @@ func loadContainerTree() []dto.CleanTree {
buildCacheTotalSize += cache.Size
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "build_cache", Size: uint64(buildCacheTotalSize), Type: "build_cache", IsRecommend: true})
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "build_cache", Size: uint64(buildCacheTotalSize), IsCheck: buildCacheTotalSize > 0, Type: "build_cache", IsRecommend: true})
return treeData
}
func loadTreeWithCheck(treeData []dto.CleanTree, pathItem, treeType string, fileOp fileUtils.FileOp) []dto.CleanTree {
size, _ := fileOp.GetDirSize(pathItem)
if size == 0 {
return treeData
}
list := loadTreeWithAllFile(true, pathItem, treeType, pathItem, fileOp)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: treeType, Size: uint64(size), IsCheck: size > 0, Children: list, Type: treeType, IsRecommend: true})
return treeData
}
@@ -577,9 +457,6 @@ func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.F
return lists
}
for _, file := range files {
if treeType == "old_upgrade" {
continue
}
if file.Name() == "ssl" {
continue
}
@@ -659,67 +536,79 @@ func dropFileOrDir(itemPath string) {
}
}
func dropBuildCache() {
func dropBuildCache() (int, int) {
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
opts := build.CachePruneOptions{}
opts.All = true
_, err = client.BuildCachePrune(context.Background(), opts)
res, err := client.BuildCachePrune(context.Background(), opts)
if err != nil {
global.LOG.Errorf("drop build cache failed, err %v", err)
return 0, 0
}
return len(res.CachesDeleted), int(res.SpaceReclaimed)
}
func dropImages() {
func dropImages() (int, int) {
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
pruneFilters := filters.NewArgs()
pruneFilters.Add("dangling", "false")
_, err = client.ImagesPrune(context.Background(), pruneFilters)
res, err := client.ImagesPrune(context.Background(), pruneFilters)
if err != nil {
global.LOG.Errorf("drop images failed, err %v", err)
return 0, 0
}
return len(res.ImagesDeleted), int(res.SpaceReclaimed)
}
func dropContainers() {
func dropContainers() (int, int) {
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
pruneFilters := filters.NewArgs()
_, err = client.ContainersPrune(context.Background(), pruneFilters)
res, err := client.ContainersPrune(context.Background(), pruneFilters)
if err != nil {
global.LOG.Errorf("drop containers failed, err %v", err)
return 0, 0
}
return len(res.ContainersDeleted), int(res.SpaceReclaimed)
}
func dropVolumes() {
func dropVolumes() (int, int) {
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("do not get docker client")
return 0, 0
}
pruneFilters := filters.NewArgs()
versions, err := client.ServerVersion(context.Background())
if err != nil {
global.LOG.Errorf("do not get docker api versions")
return 0, 0
}
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
pruneFilters.Add("all", "true")
}
_, err = client.VolumesPrune(context.Background(), pruneFilters)
res, err := client.VolumesPrune(context.Background(), pruneFilters)
if err != nil {
global.LOG.Errorf("drop volumes failed, err %v", err)
return 0, 0
}
return len(res.VolumesDeleted), int(res.SpaceReclaimed)
}
func dropWithExclude(pathToDelete string, excludeSubDirs []string, taskItem *task.Task, size *int64, count *int) {
entries, err := os.ReadDir(pathToDelete)
if err != nil {
taskItem.LogFailed(fmt.Sprintf("read dir %s failed: %v", pathToDelete, err))
return
}
@@ -747,11 +636,11 @@ func dropWithTask(itemPath string, taskItem *task.Task, size *int64, count *int)
*size += itemSize
*count += itemCount
if err := os.RemoveAll(itemPath); err != nil {
taskItem.LogFailed(fmt.Sprintf("drop file %s, err %v", itemPath, err))
taskItem.Log(i18n.GetWithNameAndErr("FileDropFailed", itemPath, err))
return
}
if itemCount != 0 {
taskItem.LogSuccessF("drop file %s, size: %s, count: %d", itemPath, common.LoadSizeUnit2F(float64(itemSize)), itemCount)
taskItem.Log(i18n.GetMsgWithMap("FileDropSuccess", map[string]interface{}{"name": itemPath, "count": itemCount, "size": common.LoadSizeUnit2F(float64(itemSize))}))
}
}
+70 -24
View File
@@ -4,10 +4,6 @@ import (
"bufio"
"context"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/jinzhu/copier"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/simplifiedchinese"
"io"
"io/fs"
"os"
@@ -20,6 +16,11 @@ import (
"time"
"unicode/utf8"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/jinzhu/copier"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/simplifiedchinese"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
@@ -97,19 +98,25 @@ func (f *FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (in
files []response.UploadInfo
backData []response.UploadInfo
)
_ = filepath.Walk(req.Path, func(path string, info os.FileInfo, err error) error {
fileList, err := os.ReadDir(req.Path)
if err != nil {
return 0, files, nil
}
for _, item := range fileList {
if item.IsDir() {
continue
}
fileItem, err := item.Info()
if err != nil {
return nil
continue
}
if !info.IsDir() {
files = append(files, response.UploadInfo{
CreatedAt: info.ModTime().Format(constant.DateTimeLayout),
Size: int(info.Size()),
Name: info.Name(),
})
}
return nil
})
files = append(files, response.UploadInfo{
CreatedAt: fileItem.ModTime().Format(constant.DateTimeLayout),
Size: int(fileItem.Size()),
Name: item.Name(),
})
}
total, start, end := len(files), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
backData = make([]response.UploadInfo, 0)
@@ -416,6 +423,14 @@ func (f *FileService) MvFile(m request.FileMove) error {
global.LOG.Errorf("copy file [%s] to [%s] failed, err: %s", src, m.NewPath, err.Error())
}
}
if len(m.CoverPaths) > 0 {
for _, src := range m.CoverPaths {
if err := fo.CopyAndReName(src, m.NewPath, "", true); err != nil {
errs = append(errs, err)
global.LOG.Errorf("copy file [%s] to [%s] failed, err: %s", src, m.NewPath, err.Error())
}
}
}
}
var errString string
@@ -478,6 +493,7 @@ func (f *FileService) DepthDirSize(req request.DirSizeReq) ([]response.DepthDirS
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
logFilePath := ""
taskStatus := ""
switch req.Type {
case constant.TypeWebsite:
website, err := websiteRepo.GetFirst(repo.WithByID(req.ID))
@@ -533,29 +549,59 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
return nil, err
}
logFilePath = taskModel.LogFile
taskStatus = taskModel.Status
case "mysql-slow-logs":
logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
case "mariadb-slow-logs":
logFilePath = path.Join(global.Dir.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
}
lines, isEndOfFile, total, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)
file, err := os.Open(logFilePath)
if err != nil {
return nil, err
}
if req.Latest && req.Page == 1 && len(lines) < 1000 && total > 1 {
preLines, _, _, err := files.ReadFileByLine(logFilePath, total-1, req.PageSize, false)
defer file.Close()
stat, err := file.Stat()
if err != nil {
return nil, err
}
var (
lines []string
isEndOfFile bool
scope string
logFileRes *dto.LogFileRes
)
if stat.Size() > 500*1024*1024 {
lines, err = files.TailFromEnd(logFilePath, req.PageSize)
isEndOfFile = true
scope = "tail"
} else {
logFileRes, err = files.ReadFileByLine(logFilePath, req.Page, req.PageSize, req.Latest)
if err != nil {
return nil, err
}
lines = append(preLines, lines...)
if req.Latest && req.Page == 1 && len(logFileRes.Lines) < 1000 && logFileRes.TotalPages > 1 {
res, err := files.ReadFileByLine(logFilePath, logFileRes.TotalPages-1, req.PageSize, false)
if err != nil {
return nil, err
}
logFileRes.Lines = append(res.Lines, logFileRes.Lines...)
}
scope = "page"
lines = logFileRes.Lines
}
res := &response.FileLineContent{
Content: strings.Join(lines, "\n"),
End: isEndOfFile,
Path: logFilePath,
Total: total,
Lines: lines,
End: isEndOfFile,
Path: logFilePath,
TaskStatus: taskStatus,
Lines: lines,
Scope: scope,
}
if logFileRes != nil {
res.TotalLines = logFileRes.TotalLines
res.Total = logFileRes.TotalPages
res.End = logFileRes.IsEndOfFile
}
return res, nil
}
+7 -1
View File
@@ -323,6 +323,7 @@ func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, c
_, _ = section.NewKey("command", strings.TrimSpace(req.Command))
_, _ = section.NewKey("directory", req.Dir)
_, _ = section.NewKey("autorestart", req.AutoRestart)
_, _ = section.NewKey("autostart", req.AutoStart)
_, _ = section.NewKey("startsecs", "3")
_, _ = section.NewKey("stdout_logfile", outLog)
_, _ = section.NewKey("stderr_logfile", errLog)
@@ -360,6 +361,8 @@ func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, c
numprocsKey.SetValue(req.Numprocs)
autoRestart := section.Key("autorestart")
autoRestart.SetValue(req.AutoRestart)
autoStart := section.Key("autostart")
autoStart.SetValue(req.AutoStart)
if err = configFile.SaveTo(iniPath); err != nil {
return err
@@ -429,6 +432,9 @@ func handleProcessConfig(configDir, containerName string) ([]response.Supervisor
if autoRestart, _ := section.GetKey("autorestart"); autoRestart != nil {
config.AutoRestart = autoRestart.Value()
}
if autoStart, _ := section.GetKey("autostart"); autoStart != nil {
config.AutoStart = autoStart.Value()
}
_ = getProcessStatus(&config, containerName)
result = append(result, config)
}
@@ -609,7 +615,7 @@ func getProcessStatus(config *response.SupervisorProcessConfig, containerName st
for _, line := range lines {
line = strings.TrimPrefix(line, "stdout:")
fields := strings.Fields(line)
if len(fields) >= 5 {
if len(fields) >= 4 {
status := response.ProcessStatus{
Name: fields[0],
Status: fields[1],
+77 -46
View File
@@ -10,6 +10,7 @@ import (
"io"
"os"
"path"
"sort"
"strings"
"time"
@@ -21,6 +22,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
@@ -33,7 +35,7 @@ import (
type ImageService struct{}
type IImageService interface {
Page(req dto.SearchWithPage) (int64, interface{}, error)
Page(req dto.PageImage) (int64, interface{}, error)
List() ([]dto.Options, error)
ListAll() ([]dto.ImageInfo, error)
ImageBuild(req dto.ImageBuild) error
@@ -41,14 +43,14 @@ type IImageService interface {
ImageLoad(req dto.ImageLoad) error
ImageSave(req dto.ImageSave) error
ImagePush(req dto.ImagePush) error
ImageRemove(req dto.BatchDelete) (dto.ContainerPruneReport, error)
ImageRemove(req dto.BatchDelete) error
ImageTag(req dto.ImageTag) error
}
func NewIImageService() IImageService {
return &ImageService{}
}
func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error) {
func (u *ImageService) Page(req dto.PageImage) (int64, interface{}, error) {
var (
list []image.Summary
records []dto.ImageInfo
@@ -64,12 +66,12 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
return 0, nil, err
}
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
if len(req.Info) != 0 {
if len(req.Name) != 0 {
length, count := len(list), 0
for count < length {
hasTag := false
for _, tag := range list[count].RepoTags {
if strings.Contains(tag, req.Info) {
if strings.Contains(tag, req.Name) {
hasTag = true
break
}
@@ -84,15 +86,48 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
}
for _, image := range list {
size := formatFileSize(image.Size)
records = append(records, dto.ImageInfo{
ID: image.ID,
Tags: image.RepoTags,
IsUsed: checkUsed(image.ID, containers),
CreatedAt: time.Unix(image.Created, 0),
Size: size,
Size: image.Size,
})
}
switch req.OrderBy {
case "size":
sort.Slice(records, func(i, j int) bool {
if req.Order == constant.OrderAsc {
return records[i].Size < records[j].Size
}
return records[i].Size > records[j].Size
})
case "isUsed":
sort.Slice(records, func(i, j int) bool {
if req.Order == constant.OrderAsc {
return !records[i].IsUsed
}
return records[i].IsUsed
})
case "tags":
sort.Slice(records, func(i, j int) bool {
if len(records[i].Tags) == 0 || len(records[j].Tags) == 0 {
return true
}
if req.Order == constant.OrderAsc {
return records[i].Tags[0] < records[j].Tags[0]
}
return records[i].Tags[0] > records[j].Tags[0]
})
default:
sort.Slice(records, func(i, j int) bool {
if req.Order == constant.OrderAsc {
return records[i].CreatedAt.Before(records[j].CreatedAt)
}
return records[i].CreatedAt.After(records[j].CreatedAt)
})
}
total, start, end := len(records), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
backDatas = make([]dto.ImageInfo, 0)
@@ -119,13 +154,12 @@ func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
}
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
for _, image := range list {
size := formatFileSize(image.Size)
records = append(records, dto.ImageInfo{
ID: image.ID,
Tags: image.RepoTags,
IsUsed: checkUsed(image.ID, containers),
CreatedAt: time.Unix(image.Created, 0),
Size: size,
Size: image.Size,
})
}
return records, nil
@@ -386,50 +420,47 @@ func (u *ImageService) ImagePush(req dto.ImagePush) error {
return nil
}
func (u *ImageService) ImageRemove(req dto.BatchDelete) (dto.ContainerPruneReport, error) {
report := dto.ContainerPruneReport{}
func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
client, err := docker.NewDockerClient()
if err != nil {
return report, err
return err
}
defer client.Close()
for _, id := range req.Names {
imageItem, _, err := client.ImageInspectWithRaw(context.TODO(), id)
if err != nil {
return report, err
}
if _, err := client.ImageRemove(context.TODO(), id, image.RemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
if strings.Contains(id, "sha256:") {
return report, buserr.New("ErrObjectInUsed")
}
return report, buserr.WithDetail("ErrInUsed", id, nil)
}
if strings.Contains(err.Error(), "image has dependent") {
return report, buserr.New("ErrObjectBeDependent")
}
return report, err
}
report.DeletedNumber++
report.SpaceReclaimed += int(imageItem.Size)
taskItem, err := task.NewTaskWithOps(task.TaskScopeImage, task.TaskDelete, task.TaskScopeContainer, req.TaskID, 1)
if err != nil {
global.LOG.Errorf("new task for create container failed, err: %v", err)
return err
}
return report, nil
}
func formatFileSize(fileSize int64) (size string) {
if fileSize < 1024 {
return fmt.Sprintf("%.2fB", float64(fileSize)/float64(1))
} else if fileSize < (1024 * 1024) {
return fmt.Sprintf("%.2fKB", float64(fileSize)/float64(1024))
} else if fileSize < (1024 * 1024 * 1024) {
return fmt.Sprintf("%.2fMB", float64(fileSize)/float64(1024*1024))
} else if fileSize < (1024 * 1024 * 1024 * 1024) {
return fmt.Sprintf("%.2fGB", float64(fileSize)/float64(1024*1024*1024))
} else if fileSize < (1024 * 1024 * 1024 * 1024 * 1024) {
return fmt.Sprintf("%.2fTB", float64(fileSize)/float64(1024*1024*1024*1024))
} else {
return fmt.Sprintf("%.2fEB", float64(fileSize)/float64(1024*1024*1024*1024*1024))
for _, id := range req.Names {
taskItem.AddSubTask(i18n.GetMsgByKey("TaskDelete")+id, func(t *task.Task) error {
imageItem, err := client.ImageInspect(context.TODO(), id)
if err != nil {
return err
}
if _, err := client.ImageRemove(context.TODO(), id, image.RemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
if strings.Contains(id, "sha256:") {
return buserr.New("ErrObjectInUsed")
}
return buserr.WithDetail("ErrInUsed", id, nil)
}
if strings.Contains(err.Error(), "image has dependent") {
return buserr.New("ErrObjectBeDependent")
}
return err
}
taskItem.Log(i18n.GetMsgWithMap("ImageRemoveHelper", map[string]interface{}{"name": id, "size": common.LoadSizeUnit2F(float64(imageItem.Size))}))
return nil
}, nil)
}
if len(req.TaskID) == 0 {
return taskItem.Execute()
}
go func() {
_ = taskItem.Execute()
}()
return nil
}
func checkUsed(imageID string, containers []container.Summary) bool {
+25 -9
View File
@@ -86,6 +86,11 @@ func (m McpServerService) Page(req request.McpServerSearch) response.McpServersR
}
func (m McpServerService) Update(req request.McpServerUpdate) error {
go func() {
if err := docker.PullImage("supercorp/supergateway:latest"); err != nil {
global.LOG.Errorf("docker pull mcp image error: %s", err.Error())
}
}()
mcpServer, err := mcpServerRepo.GetFirst(repo.WithByID(req.ID))
if err != nil {
return err
@@ -108,6 +113,8 @@ func (m McpServerService) Update(req request.McpServerUpdate) error {
mcpServer.BaseURL = req.BaseURL
mcpServer.SsePath = req.SsePath
mcpServer.HostIP = req.HostIP
mcpServer.StreamableHttpPath = req.StreamableHttpPath
mcpServer.OutputTransport = req.OutputTransport
if err := handleCreateParams(mcpServer, req.Environments, req.Volumes); err != nil {
return err
}
@@ -130,6 +137,11 @@ func (m McpServerService) Update(req request.McpServerUpdate) error {
}
func (m McpServerService) Create(create request.McpServerCreate) error {
go func() {
if err := docker.PullImage("supercorp/supergateway:latest"); err != nil {
global.LOG.Errorf("docker pull mcp image error: %s", err.Error())
}
}()
servers, _ := mcpServerRepo.List()
for _, server := range servers {
if server.Port == create.Port {
@@ -154,15 +166,17 @@ func (m McpServerService) Create(create request.McpServerCreate) error {
}
mcpDir := path.Join(global.Dir.McpDir, create.Name)
mcpServer := &model.McpServer{
Name: create.Name,
ContainerName: create.ContainerName,
Port: create.Port,
Command: create.Command,
Status: constant.StatusStarting,
BaseURL: create.BaseURL,
SsePath: create.SsePath,
Dir: mcpDir,
HostIP: create.HostIP,
Name: create.Name,
ContainerName: create.ContainerName,
Port: create.Port,
Command: create.Command,
Status: constant.StatusStarting,
BaseURL: create.BaseURL,
SsePath: create.SsePath,
Dir: mcpDir,
HostIP: create.HostIP,
StreamableHttpPath: create.StreamableHttpPath,
OutputTransport: create.OutputTransport,
}
if err := handleCreateParams(mcpServer, create.Environments, create.Volumes); err != nil {
return err
@@ -524,6 +538,8 @@ func handleEnv(mcpServer *model.McpServer) gotenv.Env {
env["BASE_URL"] = mcpServer.BaseURL
env["SSE_PATH"] = mcpServer.SsePath
env["HOST_IP"] = mcpServer.HostIP
env["STREAMABLE_HTTP_PATH"] = mcpServer.StreamableHttpPath
env["OUTPUT_TRANSPORT"] = mcpServer.OutputTransport
envStr, _ := gotenv.Marshal(env)
mcpServer.Env = envStr
return env
+5 -5
View File
@@ -16,11 +16,11 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/robfig/cron/v3"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/load"
"github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net"
)
type MonitorService struct {
+72
View File
@@ -4,6 +4,8 @@ import (
"bufio"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"io"
"net/http"
"os"
@@ -42,6 +44,9 @@ type INginxService interface {
Build(req request.NginxBuildReq) error
GetModules() (*response.NginxBuildConfig, error)
UpdateModule(req request.NginxModuleUpdate) error
OperateDefaultHTTPs(req request.NginxOperateReq) error
GetDefaultHttpsStatus() (*response.NginxConfigRes, error)
}
func NewINginxService() INginxService {
@@ -348,3 +353,70 @@ func (n NginxService) UpdateModule(req request.NginxModuleUpdate) error {
}
return fileOp.SaveFileWithByte(moduleConfigPath, moduleByte, constant.DirPerm)
}
func (n NginxService) OperateDefaultHTTPs(req request.NginxOperateReq) error {
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
websites, _ := websiteRepo.List()
hasDefaultWebsite := false
for _, website := range websites {
if website.DefaultServer {
hasDefaultWebsite = true
break
}
}
defaultConfigPath := path.Join(appInstall.GetPath(), "conf", "default", "00.default.conf")
content, err := os.ReadFile(defaultConfigPath)
if err != nil {
return err
}
if req.Operate == "enable" {
if err := handleSSLConfig(&appInstall, hasDefaultWebsite); err != nil {
return err
}
} else if req.Operate == "disable" {
defaultConfig, err := parser.NewStringParser(string(content)).Parse()
if err != nil {
return err
}
defaultConfig.FilePath = defaultConfigPath
defaultServer := defaultConfig.FindServers()[0]
defaultServer.RemoveListen(fmt.Sprintf("%d", appInstall.HttpsPort))
defaultServer.RemoveListen(fmt.Sprintf("[::]:%d", appInstall.HttpsPort))
defaultServer.RemoveDirective("include", []string{"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf"})
defaultServer.RemoveDirective("http2", []string{"on"})
if err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {
return err
}
}
return nginxCheckAndReload(string(content), defaultConfigPath, appInstall.ContainerName)
}
func (n NginxService) GetDefaultHttpsStatus() (*response.NginxConfigRes, error) {
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return nil, err
}
defaultConfigPath := path.Join(appInstall.GetPath(), "conf", "default", "00.default.conf")
content, err := os.ReadFile(defaultConfigPath)
if err != nil {
return nil, err
}
defaultConfig, err := parser.NewStringParser(string(content)).Parse()
if err != nil {
return nil, err
}
defaultConfig.FilePath = defaultConfigPath
defaultServer := defaultConfig.FindServers()[0]
res := &response.NginxConfigRes{}
for _, directive := range defaultServer.GetDirectives() {
if directive.GetName() == "include" && directive.GetParameters()[0] == "/usr/local/openresty/nginx/conf/ssl/root_ssl.conf" {
return &response.NginxConfigRes{
Https: true,
}, nil
}
}
return res, nil
}
+1 -1
View File
@@ -2,7 +2,7 @@ package service
import (
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/shirou/gopsutil/v3/process"
"github.com/shirou/gopsutil/v4/process"
)
type ProcessService struct{}
+1 -1
View File
@@ -17,7 +17,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v4/disk"
)
type RecycleBinService struct {
+67 -3
View File
@@ -5,6 +5,8 @@ import (
"context"
"encoding/json"
"fmt"
fcgiclient "github.com/tomasen/fcgi_client"
"maps"
"os"
"path"
"path/filepath"
@@ -71,6 +73,8 @@ type IRuntimeService interface {
GetSupervisorProcess(id uint) ([]response.SupervisorProcessConfig, error)
OperateSupervisorProcess(req request.PHPSupervisorProcessConfig) error
OperateSupervisorProcessFile(req request.PHPSupervisorProcessFileReq) (string, error)
GetFPMStatus(runtimeID uint) ([]response.FpmStatusItem, error)
}
func NewRuntimeService() IRuntimeService {
@@ -450,6 +454,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
if err := checkContainerName(containerName.(string)); err != nil {
return err
}
runtime.ContainerName = containerName.(string)
}
projectDir := path.Join(global.Dir.RuntimeDir, runtime.Type, runtime.Name)
@@ -990,6 +995,9 @@ func (r *RuntimeService) UpdateFPMConfig(req request.FPMConfig) error {
if err := cfg.SaveTo(runtime.GetFPMPath()); err != nil {
return err
}
if _, err := compose.Restart(runtime.GetComposePath()); err != nil {
return err
}
return nil
}
@@ -1085,9 +1093,7 @@ func (r *RuntimeService) UpdatePHPContainer(req request.PHPContainerConfig) erro
}
newMap := make(map[string]string)
handleMap(create.Params, newMap)
for k, v := range newMap {
envs[k] = v
}
maps.Copy(envs, newMap)
envs["PANEL_APP_PORT_HTTP"] = runtime.Port
envStr, err := gotenv.Marshal(envs)
if err != nil {
@@ -1161,3 +1167,61 @@ func (r *RuntimeService) UpdateRemark(req request.RuntimeRemark) error {
runtime.Remark = req.Remark
return runtimeRepo.Save(runtime)
}
func (r *RuntimeService) GetFPMStatus(runtimeID uint) ([]response.FpmStatusItem, error) {
runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(runtimeID))
if err != nil {
return nil, err
}
fcgiClient, err := fcgiclient.DialTimeout("tcp", "127.0.0.1:"+runtime.Port, 10*time.Second)
if err != nil {
return nil, errors.New("<UNK> FastCGI <UNK>: " + err.Error())
}
defer fcgiClient.Close()
reqEnv := map[string]string{
"REQUEST_METHOD": "GET",
"REQUEST_URI": "/status",
"SCRIPT_FILENAME": "/status",
"SCRIPT_NAME": "/status",
"QUERY_STRING": "",
"CONTENT_TYPE": "",
"CONTENT_LENGTH": "0",
"SERVER_SOFTWARE": "go-fcgi-client",
"SERVER_NAME": "localhost",
"SERVER_PORT": runtime.Port,
"REMOTE_ADDR": "127.0.0.1",
"GATEWAY_INTERFACE": "CGI/1.1",
}
resp, err := fcgiClient.Get(reqEnv)
if err != nil {
return nil, errors.New("<UNK> FastCGI <UNK>: " + err.Error())
}
defer resp.Body.Close()
var status []response.FpmStatusItem
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
parts := strings.SplitN(line, ":", 2)
if len(parts) != 2 {
continue
}
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
status = append(status, response.FpmStatusItem{
Key: key,
Value: value,
})
}
if err := scanner.Err(); err != nil {
return nil, errors.New(fmt.Sprintf("<UNK> FastCGI <UNK>: %v", err))
}
return status, nil
}
+2 -3
View File
@@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
"io"
"maps"
"os"
"os/exec"
"path"
@@ -522,9 +523,7 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
newMap := make(map[string]string)
handleMap(create.Params, newMap)
for k, v := range newMap {
env[k] = v
}
maps.Copy(env, newMap)
envStr, err := gotenv.Marshal(env)
if err != nil {
+11 -8
View File
@@ -21,7 +21,7 @@ import (
"github.com/docker/docker/api/types/image"
"github.com/google/uuid"
"github.com/jinzhu/copier"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v4/host"
)
type SnapshotService struct {
@@ -31,7 +31,7 @@ type SnapshotService struct {
type ISnapshotService interface {
SearchWithPage(req dto.PageSnapshot) (int64, interface{}, error)
LoadSnapshotData() (dto.SnapshotData, error)
SnapshotCreate(parentTask *task.Task, req dto.SnapshotCreate, jobID, retry, timeout uint) error
SnapshotCreate(parentTask *task.Task, req dto.SnapshotCreate, jobID, retry uint) error
SnapshotReCreate(id uint) error
SnapshotRecover(req dto.SnapshotRecover) error
SnapshotRollback(req dto.SnapshotRecover) error
@@ -121,7 +121,7 @@ func (u *SnapshotService) LoadSnapshotData() (dto.SnapshotData, error) {
}
i := 0
for _, item := range itemBackups {
if item.Label != "app" && item.Label != "system_snapshot" {
if item.Label != "app" && item.Label != "system_snapshot" && item.Label != "tmp" {
itemBackups[i] = item
i++
}
@@ -146,16 +146,18 @@ func (u *SnapshotService) Delete(req dto.SnapshotBatchDelete) error {
snaps, _ := snapshotRepo.GetList(repo.WithByIDs(req.Ids))
for _, snap := range snaps {
if req.DeleteWithFile {
accounts, err := NewBackupClientMap(strings.Split(snap.SourceAccountIDs, ","))
if err != nil {
return err
}
accounts := NewBackupClientMap(strings.Split(snap.SourceAccountIDs, ","))
for _, item := range accounts {
global.LOG.Debugf("remove snapshot file %s.tar.gz from %s", snap.Name, item.name)
if !item.isOk {
global.LOG.Errorf("remove snapshot file %s.tar.gz from %s failed, err: %s", snap.Name, item.name, item.message)
continue
}
_, _ = item.client.Delete(path.Join(item.backupPath, "system_snapshot", snap.Name+".tar.gz"))
}
_ = backupRepo.DeleteRecord(context.Background(), repo.WithByType("snapshot"), backupRepo.WithByFileName(snap.Name+".tar.gz"))
}
_ = os.Remove(path.Join(global.Dir.LocalBackupDir, "tmp/system", snap.Name+".tar.gz"))
if err := snapshotRepo.Delete(repo.WithByID(snap.ID)); err != nil {
return err
@@ -169,7 +171,8 @@ func hasOs(name string) bool {
strings.Contains(name, "arm64") ||
strings.Contains(name, "armv7") ||
strings.Contains(name, "ppc64le") ||
strings.Contains(name, "s390x")
strings.Contains(name, "s390x") ||
strings.Contains(name, "riscv64")
}
func loadOs() string {
+14 -31
View File
@@ -27,7 +27,7 @@ import (
"gorm.io/gorm"
)
func (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.SnapshotCreate, jobID, retry, timeout uint) error {
func (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.SnapshotCreate, jobID, retry uint) error {
versionItem, _ := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
scope := "core"
@@ -85,7 +85,7 @@ func (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.Snapshot
return nil
}
return handleSnapshot(req, taskItem, jobID, retry, timeout)
return handleSnapshot(req, taskItem, jobID, retry, snap.Timeout)
}
func (u *SnapshotService) SnapshotReCreate(id uint) error {
@@ -93,10 +93,6 @@ func (u *SnapshotService) SnapshotReCreate(id uint) error {
if err != nil {
return err
}
taskModel, err := taskRepo.GetFirst(taskRepo.WithResourceID(snap.ID), repo.WithByType(task.TaskScopeSnapshot))
if err != nil {
return err
}
var req dto.SnapshotCreate
_ = copier.Copy(&req, snap)
@@ -109,7 +105,7 @@ func (u *SnapshotService) SnapshotReCreate(id uint) error {
if err := json.Unmarshal([]byte(snap.BackupData), &req.BackupData); err != nil {
return err
}
req.TaskID = taskModel.ID
req.TaskID = snap.TaskID
taskItem, err := task.ReNewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeSnapshot, req.TaskID, req.ID)
if err != nil {
global.LOG.Errorf("new task for create snapshot failed, err: %v", err)
@@ -117,7 +113,7 @@ func (u *SnapshotService) SnapshotReCreate(id uint) error {
}
_ = snapshotRepo.Update(req.ID, map[string]interface{}{"status": constant.StatusWaiting, "message": ""})
go func() {
_ = handleSnapshot(req, taskItem, 0, 3, 0)
_ = handleSnapshot(req, taskItem, 0, 3, req.Timeout)
}()
return nil
@@ -197,7 +193,7 @@ func handleSnapshot(req dto.SnapshotCreate, taskItem *task.Task, jobID, retry, t
taskItem.AddSubTaskWithAliasAndOps(
"SnapUpload",
func(t *task.Task) error {
return snapUpload(itemHelper, req.SourceAccountIDs, fmt.Sprintf("%s.tar.gz", rootDir))
return snapUpload(itemHelper, req.SourceAccountIDs, req.DownloadAccountID, retry, fmt.Sprintf("%s.tar.gz", rootDir))
}, nil, int(retry), time.Duration(timeout)*time.Second,
)
req.InterruptStep = ""
@@ -242,6 +238,7 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro
return err
}
snap.snapAgentDB = agentDb
_ = snap.snapAgentDB.Model(&model.Setting{}).Where("key = ?", "SystemIP").Updates(map[string]interface{}{"value": ""}).Error
coreDb, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, "db/core.db"), "core.db")
snap.Task.LogWithStatus(i18n.GetWithName("SnapNewDB", "core"), err)
if err != nil {
@@ -426,7 +423,7 @@ func loadBackupExcludes(snap snapHelper, req []dto.DataTree) []string {
if err := snap.snapAgentDB.Where("file_dir = ? AND file_name = ?", strings.TrimPrefix(path.Dir(item.Path), global.Dir.LocalBackupDir+"/"), path.Base(item.Path)).Delete(&model.BackupRecord{}).Error; err != nil {
snap.Task.LogWithStatus("delete backup file from database", err)
}
itemDir, _ := filepath.Rel(item.Path, global.Dir.LocalBackupDir)
itemDir, _ := filepath.Rel(global.Dir.LocalBackupDir, item.Path)
excludes = append(excludes, itemDir)
} else {
excludes = append(excludes, loadBackupExcludes(snap, item.Children)...)
@@ -439,7 +436,7 @@ func loadAppBackupExcludes(req []dto.DataTree) []string {
for _, item := range req {
if len(item.Children) == 0 {
if !item.IsCheck {
itemDir, _ := filepath.Rel(item.Path, global.Dir.LocalBackupDir)
itemDir, _ := filepath.Rel(global.Dir.LocalBackupDir, item.Path)
excludes = append(excludes, itemDir)
}
} else {
@@ -501,7 +498,7 @@ func loadPanelExcludes(req []dto.DataTree) []string {
for _, item := range req {
if len(item.Children) == 0 {
if !item.IsCheck {
itemDir, _ := filepath.Rel(item.Path, path.Join(global.Dir.BaseDir, "1panel"))
itemDir, _ := filepath.Rel(path.Join(global.Dir.BaseDir, "1panel"), item.Path)
excludes = append(excludes, itemDir)
}
} else {
@@ -535,26 +532,12 @@ func snapCompress(snap snapHelper, rootDir string, secret string) error {
return nil
}
func snapUpload(snap snapHelper, accounts string, file string) error {
func snapUpload(snap snapHelper, accounts string, downloadID, retry uint, file string) error {
snap.Task.Log("---------------------- 8 / 8 ----------------------")
snap.Task.LogStart(i18n.GetMsgByKey("SnapUpload"))
source := path.Join(global.Dir.LocalBackupDir, "tmp/system", path.Base(file))
accountMap, err := NewBackupClientMap(strings.Split(accounts, ","))
snap.Task.LogWithStatus(i18n.GetMsgByKey("SnapLoadBackup"), err)
if err != nil {
return err
}
targetAccounts := strings.Split(accounts, ",")
for _, item := range targetAccounts {
snap.Task.LogStart(i18n.GetWithName("SnapUploadTo", fmt.Sprintf("[%s] %s", accountMap[item].name, path.Join("system_snapshot", path.Base(file)))))
_, err := accountMap[item].client.Upload(source, path.Join(accountMap[item].backupPath, "system_snapshot", path.Base(file)))
snap.Task.LogWithStatus(i18n.GetWithName("SnapUploadRes", accountMap[item].name), err)
if err != nil {
return err
}
}
_ = os.Remove(source)
return nil
src := path.Join(global.Dir.LocalBackupDir, "tmp/system", path.Base(file))
dst := path.Join("system_snapshot", path.Base(file))
accountMap := NewBackupClientMap(strings.Split(accounts, ","))
return uploadWithMap(snap.Task, accountMap, src, dst, accounts, downloadID, retry)
}
+227 -65
View File
@@ -1,24 +1,31 @@
package service
import (
"bytes"
"encoding/base64"
"fmt"
"os"
"os/user"
"path"
"sort"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/utils/copier"
csvexport "github.com/1Panel-dev/1Panel/agent/utils/csv_export"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/geo"
"github.com/gin-gonic/gin"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/systemctl"
"github.com/pkg/errors"
)
@@ -32,11 +39,15 @@ type ISSHService interface {
OperateSSH(operation string) error
UpdateByFile(value string) error
Update(req dto.SSHUpdate) error
GenerateSSH(req dto.GenerateSSH) error
LoadSSHSecret(mode string) (string, error)
LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (*dto.SSHLog, error)
LoadSSHConf() (string, error)
LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []dto.SSHHistory, error)
ExportLog(ctx *gin.Context, req dto.SearchSSHLog) (string, error)
SyncRootCert() error
CreateRootCert(req dto.CreateRootCert) error
SearchRootCerts(req dto.SearchWithPage) (int64, interface{}, error)
DeleteRootCerts(req dto.ForceDelete) error
}
func NewISSHService() ISSHService {
@@ -110,6 +121,14 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
data.UseDNS = strings.ReplaceAll(line, "UseDNS ", "")
}
}
currentUser, err := user.Current()
if err != nil || len(currentUser.Name) == 0 {
data.CurrentUser = "root"
} else {
data.CurrentUser = currentUser.Name
}
return &data, nil
}
@@ -214,82 +233,175 @@ func (u *SSHService) UpdateByFile(value string) error {
return nil
}
func (u *SSHService) GenerateSSH(req dto.GenerateSSH) error {
if cmd.CheckIllegal(req.EncryptionMode, req.Password) {
func (u *SSHService) SyncRootCert() error {
currentUser, err := user.Current()
if err != nil {
return fmt.Errorf("load current user failed, err: %v", err)
}
sshDir := fmt.Sprintf("%s/.ssh", currentUser.HomeDir)
fileList, err := os.ReadDir(sshDir)
if err != nil {
return err
}
var rootCerts []model.RootCert
fileMap := make(map[string]bool)
for _, item := range fileList {
if !item.IsDir() {
fileMap[item.Name()] = true
}
}
for item := range fileMap {
if !strings.HasSuffix(item, ".pub") {
continue
}
if !fileMap[strings.TrimSuffix(item, ".pub")] {
continue
}
cert := model.RootCert{Name: strings.TrimSuffix(item, ".pub"), PublicKeyPath: path.Join(sshDir, item), PrivateKeyPath: path.Join(sshDir, strings.TrimSuffix(item, ".pub"))}
pubItem, err := os.ReadFile(path.Join(sshDir, item))
if err != nil {
global.LOG.Errorf("read pubic key of %s for sync failed, err: %v", item, err)
continue
}
cert.EncryptionMode = loadEncryptioMode(string(pubItem))
rootCerts = append(rootCerts, cert)
}
return hostRepo.SyncCert(rootCerts)
}
func (u *SSHService) CreateRootCert(req dto.CreateRootCert) error {
if cmd.CheckIllegal(req.EncryptionMode, req.PassPhrase) {
return buserr.New("ErrCmdIllegal")
}
certItem, _ := hostRepo.GetCert(repo.WithByName(req.Name))
if certItem.ID != 0 {
return buserr.New("ErrRecordExist")
}
currentUser, err := user.Current()
if err != nil {
return fmt.Errorf("load current user failed, err: %v", err)
}
secretFile := fmt.Sprintf("%s/.ssh/id_item_%s", currentUser.HomeDir, req.EncryptionMode)
secretPubFile := fmt.Sprintf("%s/.ssh/id_item_%s.pub", currentUser.HomeDir, req.EncryptionMode)
var cert model.RootCert
if err := copier.Copy(&cert, req); err != nil {
return err
}
privatePath := fmt.Sprintf("%s/.ssh/%s", currentUser.HomeDir, req.Name)
publicPath := fmt.Sprintf("%s/.ssh/%s.pub", currentUser.HomeDir, req.Name)
authFilePath := currentUser.HomeDir + "/.ssh/authorized_keys"
command := fmt.Sprintf("ssh-keygen -t %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, currentUser.HomeDir, req.EncryptionMode)
if len(req.Password) != 0 {
command = fmt.Sprintf("ssh-keygen -t %s -P %s -f %s/.ssh/id_item_%s | echo y", req.EncryptionMode, req.Password, currentUser.HomeDir, req.EncryptionMode)
if req.Mode == "input" || req.Mode == "import" {
if err := os.WriteFile(privatePath, []byte(req.PrivateKey), constant.FilePerm); err != nil {
return err
}
if err := os.WriteFile(publicPath, []byte(req.PublicKey), constant.FilePerm); err != nil {
return err
}
} else {
command := fmt.Sprintf("ssh-keygen -t %s -f %s/.ssh/%s -N ''", req.EncryptionMode, currentUser.HomeDir, req.Name)
if len(req.PassPhrase) != 0 {
command = fmt.Sprintf("ssh-keygen -t %s -P %s -f %s/.ssh/%s | echo y", req.EncryptionMode, req.PassPhrase, currentUser.HomeDir, req.Name)
}
stdout, err := cmd.RunDefaultWithStdoutBashC(command)
if err != nil {
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout)
}
}
stdout, err := cmd.RunDefaultWithStdoutBashC(command)
stdout, err := cmd.RunDefaultWithStdoutBashCf("cat %s >> %s", publicPath, authFilePath)
if err != nil {
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout)
}
defer func() {
_ = os.Remove(secretFile)
}()
defer func() {
_ = os.Remove(secretPubFile)
}()
if _, err := os.Stat(authFilePath); err != nil && errors.Is(err, os.ErrNotExist) {
authFile, err := os.Create(authFilePath)
if err != nil {
cert.PrivateKeyPath = privatePath
cert.PublicKeyPath = publicPath
if len(cert.PassPhrase) != 0 {
cert.PassPhrase, _ = encrypt.StringEncrypt(cert.PassPhrase)
}
return hostRepo.CreateCert(&cert)
}
func (u *SSHService) SearchRootCerts(req dto.SearchWithPage) (int64, interface{}, error) {
total, records, err := hostRepo.PageCert(req.Page, req.PageSize)
if err != nil {
return 0, nil, err
}
var datas []dto.RootCert
for i := 0; i < len(records); i++ {
publicItem, err := os.ReadFile(records[i].PublicKeyPath)
var publicBase64 string
if err == nil && len(publicItem) != 0 {
publicBase64 = base64.StdEncoding.EncodeToString(publicItem)
}
privateItem, _ := os.ReadFile(records[i].PrivateKeyPath)
var privateBase64 string
if err == nil && len(publicItem) != 0 {
privateBase64 = base64.StdEncoding.EncodeToString(privateItem)
}
passPhrase, _ := encrypt.StringDecryptWithBase64(records[i].PassPhrase)
datas = append(datas, dto.RootCert{
ID: records[i].ID,
CreatedAt: records[i].CreatedAt,
Name: records[i].Name,
EncryptionMode: records[i].EncryptionMode,
PassPhrase: passPhrase,
PublicKey: publicBase64,
PrivateKey: privateBase64,
Description: records[i].Description,
})
}
return total, datas, err
}
func (u *SSHService) DeleteRootCerts(req dto.ForceDelete) error {
currentUser, err := user.Current()
if err != nil && !req.ForceDelete {
return fmt.Errorf("load current user failed, err: %v", err)
}
authFilePath := currentUser.HomeDir + "/.ssh/authorized_keys"
authItem, err := os.ReadFile(authFilePath)
if err != nil && !req.ForceDelete {
return err
}
for _, id := range req.IDs {
cert, _ := hostRepo.GetCert(repo.WithByID(id))
if cert.ID == 0 {
if !req.ForceDelete {
return buserr.New("ErrRecordNotFound")
} else {
continue
}
}
publicItem, err := os.ReadFile(cert.PublicKeyPath)
if err != nil && !req.ForceDelete {
return err
}
newFile := bytes.ReplaceAll(authItem, publicItem, nil)
if err := os.WriteFile(authFilePath, newFile, constant.FilePerm); err != nil && !req.ForceDelete {
return fmt.Errorf("refresh authorized_keys failed, err: %v", err)
}
_ = os.Remove(cert.PublicKeyPath)
_ = os.Remove(cert.PrivateKeyPath)
if err := hostRepo.DeleteCert(repo.WithByID(id)); err != nil && !req.ForceDelete {
return err
}
defer authFile.Close()
}
stdout1, err := cmd.RunDefaultWithStdoutBashCf("cat %s >> %s/.ssh/authorized_keys", secretPubFile, currentUser.HomeDir)
if err != nil {
return fmt.Errorf("generate failed, err: %v, message: %s", err, stdout1)
}
fileOp := files.NewFileOp()
if err := fileOp.Rename(secretFile, fmt.Sprintf("%s/.ssh/id_%s%s", currentUser.HomeDir, req.EncryptionMode, req.Name)); err != nil {
return err
}
if err := fileOp.Rename(secretPubFile, fmt.Sprintf("%s/.ssh/id_%s%s.pub", currentUser.HomeDir, req.EncryptionMode, req.Name)); err != nil {
return err
}
return nil
}
func (u *SSHService) LoadSSHSecret(mode string) (string, error) {
currentUser, err := user.Current()
if err != nil {
return "", fmt.Errorf("load current user failed, err: %v", err)
}
homeDir := currentUser.HomeDir
if _, err := os.Stat(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode)); err != nil {
return "", nil
}
file, err := os.ReadFile(fmt.Sprintf("%s/.ssh/id_%s", homeDir, mode))
return string(file), err
}
type sshFileItem struct {
Name string
Year int
}
func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (*dto.SSHLog, error) {
func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (int64, []dto.SSHHistory, error) {
var fileList []sshFileItem
var data dto.SSHLog
var data []dto.SSHHistory
baseDir := "/var/log"
fileItems, err := os.ReadDir(baseDir)
if err != nil {
return &data, err
return 0, data, err
}
for _, item := range fileItems {
if item.IsDir() || (!strings.HasPrefix(item.Name(), "secure") && !strings.HasPrefix(item.Name(), "auth")) {
@@ -318,6 +430,7 @@ func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (*dto.SSHLo
showCountFrom := (req.Page - 1) * req.PageSize
showCountTo := req.Page * req.PageSize
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
itemFailed, itemTotal := 0, 0
for _, file := range fileList {
commandItem := ""
if strings.HasPrefix(path.Base(file.Name), "secure") {
@@ -341,15 +454,41 @@ func (u *SSHService) LoadLog(ctx *gin.Context, req dto.SearchSSHLog) (*dto.SSHLo
}
}
dataItem, successCount, failedCount := loadSSHData(ctx, commandItem, showCountFrom, showCountTo, file.Year, nyc)
data.FailedCount += failedCount
data.TotalCount += successCount + failedCount
itemFailed += failedCount
itemTotal += successCount + failedCount
showCountFrom = showCountFrom - (successCount + failedCount)
showCountTo = showCountTo - (successCount + failedCount)
data.Logs = append(data.Logs, dataItem...)
if showCountTo != -1 {
showCountTo = showCountTo - (successCount + failedCount)
}
data = append(data, dataItem...)
}
data.SuccessfulCount = data.TotalCount - data.FailedCount
return &data, nil
total := itemTotal
if req.Status == constant.StatusFailed {
total = itemFailed
}
if req.Status == constant.StatusSuccess {
total = itemTotal - itemFailed
}
return int64(total), data, nil
}
func (u *SSHService) ExportLog(ctx *gin.Context, req dto.SearchSSHLog) (string, error) {
_, logs, err := u.LoadLog(ctx, req)
if err != nil {
return "", err
}
if len(logs) == 0 {
return "", buserr.New("ErrRecordNotFound")
}
tmpFileName := path.Join(global.Dir.TmpDir, "export/ssh-log", fmt.Sprintf("1panel-ssh-log-%s.csv", time.Now().Format(constant.DateTimeSlimLayout)))
if _, err := os.Stat(path.Dir(tmpFileName)); err != nil {
_ = os.MkdirAll(path.Dir(tmpFileName), constant.DirPerm)
}
if err := csvexport.ExportSSHLogs(tmpFileName, logs); err != nil {
return "", err
}
return tmpFileName, nil
}
func (u *SSHService) LoadSSHConf() (string, error) {
@@ -433,8 +572,8 @@ func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, c
switch {
case strings.Contains(lines[i], "Failed password for"):
itemData = loadFailedSecureDatas(lines[i])
if len(itemData.Address) != 0 {
if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo {
if checkIsStandard(itemData) {
if successCount+failedCount >= showCountFrom && (showCountTo == -1 || successCount+failedCount < showCountTo) {
itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))
itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
datas = append(datas, itemData)
@@ -443,8 +582,8 @@ func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, c
}
case strings.Contains(lines[i], "Connection closed by authenticating user"):
itemData = loadFailedAuthDatas(lines[i])
if len(itemData.Address) != 0 {
if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo {
if checkIsStandard(itemData) {
if successCount+failedCount >= showCountFrom && (showCountTo == -1 || successCount+failedCount < showCountTo) {
itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))
itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
datas = append(datas, itemData)
@@ -453,8 +592,8 @@ func loadSSHData(ctx *gin.Context, command string, showCountFrom, showCountTo, c
}
case strings.Contains(lines[i], "Accepted "):
itemData = loadSuccessDatas(lines[i])
if len(itemData.Address) != 0 {
if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo {
if checkIsStandard(itemData) {
if successCount+failedCount >= showCountFrom && (showCountTo == -1 || successCount+failedCount < showCountTo) {
itemData.Area, _ = geo.GetIPLocation(getLoc, itemData.Address, common.GetLang(ctx))
itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
datas = append(datas, itemData)
@@ -481,7 +620,6 @@ func loadSuccessDatas(line string) dto.SSHHistory {
data.Status = constant.StatusSuccess
return data
}
func loadFailedAuthDatas(line string) dto.SSHHistory {
var data dto.SSHHistory
parts := strings.Fields(line)
@@ -531,6 +669,14 @@ func loadFailedSecureDatas(line string) dto.SSHHistory {
return data
}
func checkIsStandard(item dto.SSHHistory) bool {
if len(item.Address) == 0 {
return false
}
portItem, _ := strconv.Atoi(item.Port)
return portItem != 0
}
func handleGunzip(path string) error {
if _, err := cmd.RunDefaultWithStdoutBashCf("gunzip %s", path); err != nil {
return err
@@ -576,3 +722,19 @@ func analyzeDateStr(parts []string) (int, string) {
}
return 2, fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2])
}
func loadEncryptioMode(content string) string {
if strings.HasPrefix(content, "ssh-rsa") {
return "rsa"
}
if strings.HasPrefix(content, "ssh-ed25519") {
return "ed25519"
}
if strings.HasPrefix(content, "ssh-ecdsa") {
return "ecdsa"
}
if strings.HasPrefix(content, "ssh-dsa") {
return "dsa"
}
return ""
}
+177 -57
View File
@@ -126,6 +126,8 @@ type IWebsiteService interface {
ChangeDatabase(req request.ChangeDatabase) error
OperateCrossSiteAccess(req request.CrossSiteAccessOp) error
ExecComposer(req request.ExecComposerReq) error
}
func NewIWebsiteService() IWebsiteService {
@@ -137,7 +139,7 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
websiteDTOs []response.WebsiteRes
opts []repo.DBOption
)
opts = append(opts, repo.WithOrderRuleBy(req.OrderBy, req.Order))
opts = append(opts, repo.WithOrderRuleBy(req.OrderBy, req.Order), repo.WithOrderRuleBy("updated_at", "descending"))
if req.Name != "" {
domains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithDomainLike(req.Name))
if len(domains) > 0 {
@@ -202,6 +204,7 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
AppInstallID: appInstallID,
RuntimeType: runtimeType,
Favorite: web.Favorite,
IPV6: web.IPV6,
}
sites, _ := websiteRepo.List(websiteRepo.WithParentID(web.ID))
@@ -257,7 +260,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
var (
domains []model.WebsiteDomain
)
domains, _, _, err = getWebsiteDomains(create.Domains, defaultHttpPort, 0)
domains, _, _, err = getWebsiteDomains(create.Domains, defaultHttpPort, nginxInstall.HttpsPort, 0)
if err != nil {
return err
}
@@ -388,7 +391,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
switch runtime.Type {
case constant.RuntimePHP:
if runtime.Resource == constant.ResourceAppstore {
if !checkImageExist(nil, runtime.Image) {
if !checkImageLike(nil, runtime.Image) {
return buserr.WithName("ErrImageNotExist", runtime.Name)
}
website.Proxy = fmt.Sprintf("127.0.0.1:%s", runtime.Port)
@@ -404,9 +407,10 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython, constant.RuntimeDotNet:
proxyPort := runtime.Port
if create.Port > 0 {
proxyPort = strconv.Itoa(create.Port)
if proxyPort == "" {
return buserr.New("ErrRuntimeNoPort")
}
proxyPort = strconv.Itoa(create.Port)
website.Proxy = fmt.Sprintf("127.0.0.1:%s", proxyPort)
}
case constant.Subsite:
@@ -465,14 +469,15 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
website.Protocol = constant.ProtocolHTTPS
website.WebsiteSSLID = create.WebsiteSSLID
appSSLReq := request.WebsiteHTTPSOp{
WebsiteID: website.ID,
Enable: true,
WebsiteSSLID: websiteModel.ID,
Type: "existed",
HttpConfig: "HTTPToHTTPS",
SSLProtocol: []string{"TLSv1.3", "TLSv1.2"},
Algorithm: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED",
Hsts: true,
WebsiteID: website.ID,
Enable: true,
WebsiteSSLID: websiteModel.ID,
Type: "existed",
HttpConfig: "HTTPToHTTPS",
SSLProtocol: []string{"TLSv1.3", "TLSv1.2"},
Algorithm: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED",
Hsts: true,
HstsIncludeSubDomains: true,
}
if err = applySSL(website, *websiteModel, appSSLReq); err != nil {
return err
@@ -680,7 +685,7 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
domainModels []model.WebsiteDomain
addPorts []int
)
httpPort, _, err := getAppInstallPort(constant.AppOpenresty)
httpPort, httpsPort, err := getAppInstallPort(constant.AppOpenresty)
if err != nil {
return nil, err
}
@@ -689,7 +694,7 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
return nil, err
}
domainModels, addPorts, _, err = getWebsiteDomains(create.Domains, httpPort, create.WebsiteID)
domainModels, addPorts, _, err = getWebsiteDomains(create.Domains, httpPort, httpsPort, create.WebsiteID)
if err != nil {
return nil, err
}
@@ -958,6 +963,12 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS,
if p.Name == "add_header" && len(p.Params) > 0 {
if p.Params[0] == "Strict-Transport-Security" {
res.Hsts = true
if len(p.Params) > 1 {
hstsValue := p.Params[1]
if strings.Contains(hstsValue, "includeSubDomains") {
res.HstsIncludeSubDomains = true
}
}
}
if p.Params[0] == "Alt-Svc" {
res.Http3 = true
@@ -976,12 +987,13 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
res response.WebsiteHTTPS
websiteSSL model.WebsiteSSL
)
if err = ChangeHSTSConfig(req.Hsts, req.Http3, website); err != nil {
if err = ChangeHSTSConfig(req.Hsts, req.HstsIncludeSubDomains, req.Http3, website); err != nil {
return nil, err
}
res.Enable = req.Enable
res.SSLProtocol = req.SSLProtocol
res.Algorithm = req.Algorithm
res.HstsIncludeSubDomains = req.HstsIncludeSubDomains
if !req.Enable {
website.Protocol = constant.ProtocolHTTP
website.WebsiteSSLID = 0
@@ -1221,13 +1233,13 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
}
}
filePath := path.Join(sitePath, "log", req.LogType)
lines, end, _, err := files.ReadFileByLine(filePath, req.Page, req.PageSize, false)
logFileRes, err := files.ReadFileByLine(filePath, req.Page, req.PageSize, false)
if err != nil {
return nil, err
}
res.End = end
res.End = logFileRes.IsEndOfFile
res.Path = filePath
res.Content = strings.Join(lines, "\n")
res.Content = strings.Join(logFileRes.Lines, "\n")
return res, nil
case constant.DisableLog:
params := dto.NginxParam{}
@@ -1354,7 +1366,7 @@ func (w WebsiteService) ChangePHPVersion(req request.WebsitePHPVersionReq) error
return err
}
defer client.Close()
if !checkImageExist(client, oldRuntime.Image) {
if !checkImageLike(client, oldRuntime.Image) {
return buserr.WithName("ErrImageNotExist", oldRuntime.Name)
}
}
@@ -1632,9 +1644,16 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
config.FilePath = includePath
directives := config.Directives
location, ok := directives[0].(*components.Location)
if !ok {
err = errors.New("error")
var location *components.Location
for _, directive := range directives {
if loc, ok := directive.(*components.Location); ok {
location = loc
break
}
}
if location == nil {
err = errors.New("invalid proxy config, no location found")
return
}
location.UpdateDirective("proxy_pass", []string{req.ProxyPass})
@@ -1644,7 +1663,7 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
if err = openProxyCache(website); err != nil {
return
}
location.AddCache(req.CacheTime, req.CacheUnit, fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias))
location.AddCache(req.CacheTime, req.CacheUnit, fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias), req.ServerCacheTime, req.ServerCacheUnit)
} else {
location.RemoveCache(fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias))
}
@@ -1787,9 +1806,15 @@ func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, e
}
directives := config.GetDirectives()
location, ok := directives[0].(*components.Location)
if !ok {
err = errors.New("error")
var location *components.Location
for _, directive := range directives {
if loc, ok := directive.(*components.Location); ok {
location = loc
break
}
}
if location == nil {
err = errors.New("invalid proxy config, no location found")
return
}
proxyConfig.ProxyPass = location.ProxyPass
@@ -1798,6 +1823,10 @@ func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, e
proxyConfig.CacheTime = location.CacheTime
proxyConfig.CacheUnit = location.CacheUint
}
if location.ServerCacheTime > 0 {
proxyConfig.ServerCacheTime = location.ServerCacheTime
proxyConfig.ServerCacheUnit = location.ServerCacheUint
}
proxyConfig.Match = location.Match
proxyConfig.Modifier = location.Modifier
proxyConfig.ProxyHost = location.Host
@@ -1806,6 +1835,9 @@ func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, e
if directive.GetName() == "proxy_ssl_server_name" {
proxyConfig.SNI = directive.GetParameters()[0] == "on"
}
if directive.GetName() == "proxy_ssl_name" && len(directive.GetParameters()) > 0 {
proxyConfig.ProxySSLName = directive.GetParameters()[0]
}
}
res = append(res, proxyConfig)
}
@@ -2198,7 +2230,7 @@ func (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err e
}
}
}
if req.Enable {
if req.Enable || req.Cache {
exts := strings.Split(req.Extends, ",")
newDirective := components.Directive{
Name: "location",
@@ -2208,44 +2240,85 @@ func (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err e
newBlock := &components.Block{}
newBlock.Directives = make([]components.IDirective, 0)
if req.Cache {
newBlock.Directives = append(newBlock.Directives, &components.Directive{
newBlock.AppendDirectives(&components.Directive{
Name: "expires",
Parameters: []string{strconv.Itoa(req.CacheTime) + req.CacheUint},
})
}
newBlock.Directives = append(newBlock.Directives, &components.Directive{
if !req.LogEnable {
newBlock.AppendDirectives(&components.Directive{
Name: "access_log",
Parameters: []string{"off"},
})
}
newBlock.AppendDirectives(&components.Directive{
Name: "log_not_found",
Parameters: []string{"off"},
})
validDir := &components.Directive{
Name: "valid_referers",
Parameters: []string{},
}
if req.NoneRef {
validDir.Parameters = append(validDir.Parameters, "none")
}
if len(req.ServerNames) > 0 {
validDir.Parameters = append(validDir.Parameters, strings.Join(req.ServerNames, " "))
}
newBlock.Directives = append(newBlock.Directives, validDir)
if req.Enable {
validDir := &components.Directive{
Name: "valid_referers",
Parameters: []string{},
}
if req.NoneRef {
validDir.Parameters = append(validDir.Parameters, "none")
}
if req.Blocked {
validDir.Parameters = append(validDir.Parameters, "blocked")
}
if len(req.ServerNames) > 0 {
validDir.Parameters = append(validDir.Parameters, strings.Join(req.ServerNames, " "))
}
newBlock.AppendDirectives(validDir)
ifDir := &components.Directive{
Name: "if",
Parameters: []string{"($invalid_referer)"},
ifDir := &components.Directive{
Name: "if",
Parameters: []string{"($invalid_referer)"},
}
if !req.LogEnable {
ifDir.Block = &components.Block{
Directives: []components.IDirective{
&components.Directive{
Name: "access_log",
Parameters: []string{"off"},
},
&components.Directive{
Name: "return",
Parameters: []string{req.Return},
},
},
}
} else {
ifDir.Block = &components.Block{
Directives: []components.IDirective{
&components.Directive{
Name: "return",
Parameters: []string{req.Return},
},
},
}
}
newBlock.AppendDirectives(ifDir)
}
ifDir.Block = &components.Block{
Directives: []components.IDirective{
if website.Type == constant.Deployment {
newBlock.AppendDirectives(
&components.Directive{
Name: "return",
Parameters: []string{req.Return},
Name: "proxy_set_header",
Parameters: []string{"Host", "$host"},
},
&components.Directive{
Name: "access_log",
Parameters: []string{"off"},
Name: "proxy_set_header",
Parameters: []string{"X-Real-IP", "$remote_addr"},
},
},
&components.Directive{
Name: "proxy_set_header",
Parameters: []string{"X-Forwarded-For", "$proxy_add_x_forwarded_for"},
},
&components.Directive{
Name: "proxy_pass",
Parameters: []string{fmt.Sprintf("http://%s", website.Proxy)},
})
}
newBlock.Directives = append(newBlock.Directives, ifDir)
newDirective.Block = newBlock
index := -1
for i, directive := range block.Directives {
@@ -2298,6 +2371,11 @@ func (w WebsiteService) GetAntiLeech(id uint) (*response.NginxAntiLeechRes, erro
}
lDirectives := location.GetBlock().GetDirectives()
for _, lDir := range lDirectives {
if lDir.GetName() == "access_log" {
if strings.Join(lDir.GetParameters(), "") == "off" {
res.LogEnable = false
}
}
if lDir.GetName() == "valid_referers" {
res.Enable = true
params := lDir.GetParameters()
@@ -2322,11 +2400,6 @@ func (w WebsiteService) GetAntiLeech(id uint) (*response.NginxAntiLeechRes, erro
if dir.GetName() == "return" {
res.Return = strings.Join(dir.GetParameters(), " ")
}
if dir.GetName() == "access_log" {
if strings.Join(dir.GetParameters(), "") == "off" {
res.LogEnable = false
}
}
}
}
if lDir.GetName() == "expires" {
@@ -3292,3 +3365,50 @@ func (w WebsiteService) OperateCrossSiteAccess(req request.CrossSiteAccessOp) er
}
return nil
}
func (w WebsiteService) ExecComposer(req request.ExecComposerReq) error {
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
sitePath := GetSitePath(website, SiteDir)
if !strings.Contains(req.Dir, sitePath) {
return buserr.New("ErrWebsiteDir")
}
if !files.NewFileOp().Stat(path.Join(req.Dir, "composer.json")) {
return buserr.New("ErrComposerFileNotFound")
}
if task.CheckResourceTaskIsExecuting(task.TaskExec, req.Command, website.ID) {
return buserr.New("ErrInstallExtension")
}
runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))
if err != nil {
return err
}
var command string
if req.Command != "custom" {
command = fmt.Sprintf("%s %s", req.Command, req.ExtCommand)
} else {
command = req.ExtCommand
}
resourceName := fmt.Sprintf("composer %s", command)
composerTask, err := task.NewTaskWithOps(resourceName, task.TaskExec, req.Command, req.TaskID, website.ID)
if err != nil {
return err
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*composerTask), cmd.WithTimeout(20*time.Minute))
siteDir, _ := settingRepo.Get(settingRepo.WithByKey("WEBSITE_DIR"))
execDir := strings.ReplaceAll(req.Dir, siteDir.Value, "/www")
composerTask.AddSubTask("", func(t *task.Task) error {
cmdStr := fmt.Sprintf("docker exec -u %s %s sh -c 'composer config -g repo.packagist composer %s && composer %s --working-dir=%s'", req.User, runtime.ContainerName, req.Mirror, command, execDir)
err = cmdMgr.RunBashC(cmdStr)
if err != nil {
return err
}
return nil
}, nil)
go func() {
_ = composerTask.Execute()
}()
return nil
}
+91 -14
View File
@@ -5,6 +5,7 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/go-acme/lego/v4/certificate"
"log"
"os"
@@ -46,6 +47,7 @@ type IWebsiteSSLService interface {
ObtainSSL(apply request.WebsiteSSLApply) error
SyncForRestart() error
DownloadFile(id uint) (*os.File, error)
ImportMasterSSL(create model.WebsiteSSL) error
}
func NewIWebsiteSSLService() IWebsiteSSLService {
@@ -146,6 +148,10 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
}
websiteSSL.Dir = create.Dir
}
if create.PushNode && global.IsMaster && len(create.Nodes) > 0 {
websiteSSL.PushNode = true
websiteSSL.Nodes = create.Nodes
}
var domains []string
if create.OtherDomains != "" {
@@ -185,6 +191,8 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
return res, err
}
create.ID = websiteSSL.ID
logFile, _ := os.OpenFile(path.Join(global.Dir.SSLLogDir, fmt.Sprintf("%s-ssl-%d.log", websiteSSL.PrimaryDomain, websiteSSL.ID)), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)
logFile.Close()
go func() {
if create.Provider != constant.DnsManual {
if err = w.ObtainSSL(request.WebsiteSSLApply{
@@ -205,6 +213,9 @@ func printSSLLog(logger *log.Logger, msgKey string, params map[string]interface{
}
func reloadSystemSSL(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
if !global.IsMaster {
return
}
systemSSLEnable, sslID := GetSystemSSL()
if systemSSLEnable && sslID == websiteSSL.ID {
fileOp := files.NewFileOp()
@@ -382,6 +393,14 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
printSSLLog(logger, "ApplyWebSiteSSLSuccess", nil, apply.DisableLog)
}
reloadSystemSSL(websiteSSL, logger)
if websiteSSL.PushNode {
printSSLLog(logger, "StartPushSSLToNode", nil, apply.DisableLog)
if err = xpack.PushSSLToNode(websiteSSL); err != nil {
printSSLLog(logger, "PushSSLToNodeFailed", map[string]interface{}{"err": err.Error()}, apply.DisableLog)
return
}
printSSLLog(logger, "PushSSLToNodeSuccess", nil, apply.DisableLog)
}
}()
return nil
@@ -442,12 +461,15 @@ func (w WebsiteSSLService) GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO
}
func (w WebsiteSSLService) Delete(ids []uint) error {
var names []string
var (
websiteSSLS []string
applySSLS []string
)
for _, id := range ids {
if websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(id)); len(websites) > 0 {
oldSSL, _ := websiteSSLRepo.GetFirst(repo.WithByID(id))
if oldSSL.ID > 0 {
names = append(names, oldSSL.PrimaryDomain)
websiteSSLS = append(websiteSSLS, oldSSL.PrimaryDomain)
}
continue
}
@@ -463,23 +485,36 @@ func (w WebsiteSSLService) Delete(ids []uint) error {
if err != nil {
return err
}
if websiteSSL.Status == constant.SSLApply {
applySSLS = append(applySSLS, websiteSSL.PrimaryDomain)
continue
}
if websiteSSL.Provider != constant.Manual && websiteSSL.Provider != constant.SelfSigned {
acmeAccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(websiteSSL.AcmeAccountID))
if err != nil {
return err
}
client, err := ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))
if err != nil {
return err
}
go func() {
_ = client.RevokeSSL([]byte(websiteSSL.Pem))
acmeAccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(websiteSSL.AcmeAccountID))
if err != nil {
global.LOG.Errorf("Failed to get acme account for SSL revoke, err: %v", err)
return
}
client, err := ssl.NewAcmeClient(acmeAccount, getSystemProxy(acmeAccount.UseProxy))
if err != nil {
global.LOG.Errorf("Failed to create ACME client for SSL revoke, err: %v", err)
return
}
err = client.RevokeSSL([]byte(websiteSSL.Pem))
if err != nil {
global.LOG.Errorf("Failed to revoke SSL for domain %s, err: %v", websiteSSL.PrimaryDomain, err)
return
}
}()
}
_ = websiteSSLRepo.DeleteBy(repo.WithByID(id))
}
if len(names) > 0 {
return buserr.WithName("ErrSSLCannotDelete", strings.Join(names, ","))
if len(websiteSSLS) > 0 {
return buserr.WithName("ErrSSLCannotDelete", strings.Join(websiteSSLS, ","))
}
if len(applySSLS) > 0 {
return buserr.WithName("ErrApplySSLCanNotDelete", strings.Join(applySSLS, ","))
}
return nil
}
@@ -504,6 +539,13 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
} else {
updateParams["shell"] = ""
}
if update.PushNode {
updateParams["push_node"] = true
updateParams["nodes"] = update.Nodes
} else {
updateParams["push_node"] = false
updateParams["nodes"] = ""
}
if websiteSSL.Provider != constant.SelfSigned && websiteSSL.Provider != constant.Manual {
acmeAccount, err := websiteAcmeRepo.GetFirst(repo.WithByID(update.AcmeAccountID))
@@ -608,7 +650,7 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
}
pemData = reset
}
if pemData == nil {
if pemData == nil || cert == nil {
return buserr.New("ErrSSLCertificateFormat")
}
@@ -691,3 +733,38 @@ func (w WebsiteSSLService) SyncForRestart() error {
}
return nil
}
func (w WebsiteSSLService) ImportMasterSSL(create model.WebsiteSSL) error {
websiteSSL, _ := websiteSSLRepo.GetFirst(websiteSSLRepo.WithByMasterSSLID(create.ID))
websiteSSL.Status = constant.SSLReady
websiteSSL.Provider = constant.FromMaster
websiteSSL.PrimaryDomain = create.PrimaryDomain
websiteSSL.StartDate = create.StartDate
websiteSSL.ExpireDate = create.ExpireDate
websiteSSL.KeyType = create.KeyType
websiteSSL.Description = create.Description
websiteSSL.PrivateKey = create.PrivateKey
websiteSSL.Pem = create.Pem
websiteSSL.Type = create.Type
websiteSSL.Organization = create.Organization
websiteSSL.MasterSSLID = create.ID
if err := websiteSSLRepo.Save(websiteSSL); err != nil {
return err
}
websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))
if len(websites) == 0 {
return nil
}
for _, website := range websites {
if err := createPemFile(website, *websiteSSL); err != nil {
continue
}
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err == nil {
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
return err
}
}
return nil
}
+35 -17
View File
@@ -216,6 +216,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
case constant.Proxy:
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
server.UpdateDirective("include", []string{nginxInclude})
server.UpdateRoot(rootIndex)
case constant.Runtime:
switch runtime.Type {
case constant.RuntimePHP:
@@ -752,6 +753,7 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
if !req.Hsts {
server.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
server.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000; includeSubDomains\""})
}
if !req.Http3 {
for port := range httpsPorts {
@@ -792,9 +794,15 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
}
}
if req.Hsts {
var hstsValue string
if req.HstsIncludeSubDomains {
hstsValue = "\"max-age=31536000; includeSubDomains\""
} else {
hstsValue = "\"max-age=31536000\""
}
nginxParams = append(nginxParams, dto.NginxParam{
Name: "add_header",
Params: []string{"Strict-Transport-Security", "\"max-age=31536000\""},
Params: []string{"Strict-Transport-Security", hstsValue},
})
}
if req.Http3 {
@@ -955,7 +963,7 @@ func opWebsite(website *model.Website, operate string) error {
}
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
} else {
proxy := fmt.Sprintf("http://127.0.0.1:%s", runtime.Port)
proxy := fmt.Sprintf("http://%s", website.Proxy)
server.UpdateRootProxy([]string{proxy})
}
}
@@ -1031,7 +1039,7 @@ func chownRootDir(path string) error {
return nil
}
func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) {
func getWebsiteDomains(domains []request.WebsiteDomain, defaultHTTPPort, defaultHTTPsPort int, websiteID uint) (domainModels []model.WebsiteDomain, addPorts []int, addDomains []string, err error) {
var (
ports = make(map[int]struct{})
existPort = make(map[int]struct{})
@@ -1056,7 +1064,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, website
domainModel.Domain = strings.ToLower(domainModel.Domain)
domainModel.Port = domain.Port
if domain.Port == 0 {
domain.Port = defaultPort
domain.Port = defaultHTTPPort
}
domainModel.SSL = domain.SSL
domainModel.WebsiteID = websiteID
@@ -1077,7 +1085,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, website
}
for port := range ports {
if port == defaultPort {
if port == defaultHTTPPort || port == defaultHTTPsPort {
addPorts = append(addPorts, port)
continue
}
@@ -1098,7 +1106,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, website
err = buserr.WithMap("ErrPortExist", errMap, nil)
return
}
if port != 443 && common.ScanPort(port) {
if port != defaultHTTPsPort && common.ScanPort(port) {
err = buserr.WithDetail("ErrPortInUsed", port, nil)
return
}
@@ -1135,13 +1143,12 @@ func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
}
func GetSystemSSL() (bool, uint) {
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
if err != nil {
return false, 0
}
if sslSetting.Value == "enable" {
sslID, _ := settingRepo.Get(settingRepo.WithByKey("SSLID"))
idValue, _ := strconv.Atoi(sslID.Value)
var sslSetting model.Setting
_ = global.CoreDB.Model(&model.Setting{}).Where("key = ?", "SSL").First(&sslSetting).Error
if sslSetting.Value == "Enable" {
var sslIDSetting model.Setting
_ = global.CoreDB.Model(&model.Setting{}).Where("key = ?", "SSLID").First(&sslIDSetting).Error
idValue, _ := strconv.Atoi(sslIDSetting.Value)
if idValue > 0 {
return true, uint(idValue)
}
@@ -1165,6 +1172,9 @@ func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
return buserr.WithErr("ErrSSLApply", err)
}
}
if !global.IsMaster {
return nil
}
enable, sslID := GetSystemSSL()
if enable && sslID == websiteSSL.ID {
fileOp := files.NewFileOp()
@@ -1181,7 +1191,7 @@ func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
return nil
}
func ChangeHSTSConfig(enable bool, http3Enable bool, website model.Website) error {
func ChangeHSTSConfig(enable bool, includeSubDomains bool, http3Enable bool, website model.Website) error {
includeDir := GetSitePath(website, SiteProxyDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(includeDir) {
@@ -1207,11 +1217,19 @@ func ChangeHSTSConfig(enable bool, http3Enable bool, website model.Website) erro
if !ok {
return nil
}
//前置移除HSTS配置
location.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
location.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000; includeSubDomains\""})
if enable {
location.UpdateDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
} else {
location.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
var hstsValue string
if includeSubDomains {
hstsValue = "\"max-age=31536000; includeSubDomains\""
} else {
hstsValue = "\"max-age=31536000\""
}
location.UpdateDirective("add_header", []string{"Strict-Transport-Security", hstsValue})
}
if http3Enable {
location.UpdateDirective("add_header", []string{"Alt-Svc", "'h3=\":443\"; ma=2592000'"})
} else {
+2
View File
@@ -65,7 +65,9 @@ const (
TaskPull = "TaskPull"
TaskCommit = "TaskCommit"
TaskPush = "TaskPush"
TaskClean = "TaskClean"
TaskHandle = "TaskHandle"
TaskExec = "TaskExec"
)
const (
+2
View File
@@ -7,9 +7,11 @@ services:
- "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${PANEL_APP_PORT_HTTP}"
command: [
"--stdio", "${COMMAND}",
"--outputTransport","${OUTPUT_TRANSPORT}",
"--port", "${PANEL_APP_PORT_HTTP}",
"--baseUrl", "${BASE_URL}",
"--ssePath", "${SSE_PATH}",
"--streamableHttpPath", "${STREAMABLE_HTTP_PATH}",
"--messagePath", "${SSE_PATH}/messages"
]
networks:
@@ -4,15 +4,6 @@ server {
index index.php index.html index.htm default.php default.htm default.html;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
access_log /www/sites/domain/log/access.log main;
error_log /www/sites/domain/log/error.log;
+1
View File
@@ -12,6 +12,7 @@ const (
TypeSSL = "ssl"
TypeSystem = "system"
TypeTask = "task"
TypeApp = "app"
TypeImagePull = "image-pull"
TypeImagePush = "image-push"
TypeImageBuild = "image-build"
+1
View File
@@ -5,6 +5,7 @@ const (
StatusCanceled = "Canceled"
StatusDone = "Done"
StatusWaiting = "Waiting"
StatusPending = "Pending"
StatusSuccess = "Success"
StatusFailed = "Failed"
StatusUploading = "Uploading"
+1
View File
@@ -25,6 +25,7 @@ const (
Http = "http"
Manual = "manual"
SelfSigned = "selfSigned"
FromMaster = "fromMaster"
StartWeb = "start"
StopWeb = "stop"
+46 -38
View File
@@ -1,6 +1,8 @@
module github.com/1Panel-dev/1Panel/agent
go 1.23.9
go 1.24.0
toolchain go1.24.6
require (
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
@@ -8,12 +10,12 @@ require (
github.com/compose-spec/compose-go/v2 v2.6.4
github.com/creack/pty v1.1.24
github.com/docker/compose/v2 v2.37.0
github.com/docker/docker v28.2.2+incompatible
github.com/docker/docker v28.3.3+incompatible
github.com/docker/go-connections v0.5.0
github.com/fsnotify/fsnotify v1.9.0
github.com/gin-gonic/gin v1.10.0
github.com/glebarez/sqlite v1.11.0
github.com/go-acme/lego/v4 v4.23.1
github.com/go-acme/lego/v4 v4.25.2
github.com/go-gormigrate/gormigrate/v2 v2.1.2
github.com/go-playground/validator/v10 v10.26.0
github.com/go-redis/redis v6.15.9+incompatible
@@ -37,20 +39,20 @@ require (
github.com/pkg/sftp v1.13.6
github.com/qiniu/go-sdk/v7 v7.21.1
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v3 v3.24.5
github.com/shirou/gopsutil/v4 v4.25.7
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.11.0
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.19.0
github.com/studio-b12/gowebdav v0.9.0
github.com/subosito/gotenv v1.6.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.54
github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19
github.com/upyun/go-sdk v2.1.0+incompatible
golang.org/x/crypto v0.39.0
golang.org/x/net v0.41.0
golang.org/x/crypto v0.40.0
golang.org/x/net v0.42.0
golang.org/x/oauth2 v0.30.0
golang.org/x/sys v0.33.0
golang.org/x/text v0.26.0
golang.org/x/sys v0.34.0
golang.org/x/text v0.27.0
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
@@ -63,9 +65,16 @@ require (
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.8 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
github.com/alibabacloud-go/openapi-util v0.1.1 // indirect
github.com/alibabacloud-go/tea v1.3.9 // indirect
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
github.com/aliyun/credentials-go v1.4.6 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/baidubce/bce-sdk-go v0.9.223 // indirect
github.com/baidubce/bce-sdk-go v0.9.235 // indirect
github.com/bodgit/plumbing v1.2.0 // indirect
github.com/bodgit/sevenzip v1.3.0 // indirect
github.com/bodgit/windows v1.0.0 // indirect
@@ -73,7 +82,7 @@ require (
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
github.com/cloudflare/cloudflare-go v0.115.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/connesc/cipherio v0.2.1 // indirect
@@ -95,19 +104,22 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-acme/alidns-20150109/v4 v4.5.10 // indirect
github.com/go-acme/tencentclouddnspod v1.0.1208 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -122,7 +134,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.159 // indirect
github.com/in-toto/in-toto-golang v0.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@@ -142,7 +154,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/miekg/dns v1.1.64 // indirect
github.com/miekg/dns v1.1.67 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -161,7 +173,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
github.com/namedotcom/go/v4 v4.0.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nrdcg/dnspod-go v0.4.0 // indirect
github.com/nrdcg/freemyip v0.3.0 // indirect
@@ -169,7 +181,6 @@ require (
github.com/nrdcg/namesilo v0.2.1 // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
@@ -182,16 +193,14 @@ require (
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1210 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 // indirect
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
@@ -199,8 +208,8 @@ require (
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/volcengine/volc-sdk-golang v1.0.199 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/volcengine/volc-sdk-golang v1.0.216 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
@@ -210,28 +219,27 @@ require (
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
google.golang.org/grpc v1.72.2 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
modernc.org/libc v1.66.1 // indirect
modernc.org/mathutil v1.7.1 // indirect
+174 -88
View File
@@ -38,7 +38,6 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
@@ -68,10 +67,56 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 h1:7dONQ3WNZ1zy960TmkxJPuwoolZwL7xKtpcM04MBnt4=
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82/go.mod h1:nLnM0KdK1CmygvjpDUO6m1TjSsiQtL61juhNsvV/JVI=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 h1:yUkCbrSM1cWtgBfRVKMQtdt22KhDvKY7g4V+92eG9wA=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA=
github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8=
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.11/go.mod h1:wHxkgZT1ClZdcwEVP/pDgYK/9HucsnCfMipmJgCz4xY=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.8 h1:AL+nH363NJFS1NXIjCdmj5MOElgKEqgFeoq7vjje350=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.8/go.mod h1:d+z3ScRqc7PFzg4h9oqE3h8yunRZvAvU7u+iuPYEhpU=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg=
github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/openapi-util v0.1.1 h1:ujGErJjG8ncRW6XtBBMphzHTvCxn4DjrVw4m04HsS28=
github.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea v1.3.9 h1:bjgt1bvdY780vz/17iWNNtbXl4A77HWntWMeaUF3So0=
github.com/alibabacloud-go/tea v1.3.9/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/aliyun/credentials-go v1.4.6 h1:CG8rc/nxCNKfXbZWpWDzI9GjF4Tuu3Es14qT8Y0ClOk=
github.com/aliyun/credentials-go v1.4.6/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@@ -90,8 +135,8 @@ github.com/aws/aws-sdk-go v1.55.0/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQ
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/baidubce/bce-sdk-go v0.9.223 h1:vvDeIemf7ePPP59nLHCntQ/vS++ok2HKbRPgmz1VZKU=
github.com/baidubce/bce-sdk-go v0.9.223/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/baidubce/bce-sdk-go v0.9.235 h1:iAi+seH9w1Go2szFNzyGumahLGDsuYZ3i8hduX3qiM8=
github.com/baidubce/bce-sdk-go v0.9.235/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
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=
@@ -127,9 +172,10 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM=
github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
@@ -200,8 +246,8 @@ github.com/docker/compose/v2 v2.37.0 h1:R8Yik9ssiRz7T9BRfdOZy0xHDzOFPIJX40DrxzJ6
github.com/docker/compose/v2 v2.37.0/go.mod h1:twDoqUBFO2L5+vccJjkR6shQOH8C50V8AAQPxlkFr2Q=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw=
github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@@ -222,6 +268,8 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -261,8 +309,12 @@ github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec
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-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV4=
github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI=
github.com/go-acme/alidns-20150109/v4 v4.5.10 h1:epLD0VaHR5XUpiM6mjm4MzQFICrk+zpuqDz2aO1/R/k=
github.com/go-acme/alidns-20150109/v4 v4.5.10/go.mod h1:qGRq8kD0xVgn82qRSQmhHwh/oWxKRjF4Db5OI4ScV5g=
github.com/go-acme/lego/v4 v4.25.2 h1:+D1Q+VnZrD+WJdlkgUEGHFFTcDrwGlE7q24IFtMmHDI=
github.com/go-acme/lego/v4 v4.25.2/go.mod h1:OORYyVNZPaNdIdVYCGSBNRNZDIjhQbPuFxwGDgWj/yM=
github.com/go-acme/tencentclouddnspod v1.0.1208 h1:xAVy1lmg2KcKKeYmFSBQUttwc1o1S++9QTjAotGC+BM=
github.com/go-acme/tencentclouddnspod v1.0.1208/go.mod h1:yxG02mkbbVd7lTb97nOn7oj09djhm7hAwxNQw4B9dpQ=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -270,8 +322,8 @@ github.com/go-gormigrate/gormigrate/v2 v2.1.2 h1:F/d1hpHbRAvKezziV2CC5KUE82cVe9z
github.com/go-gormigrate/gormigrate/v2 v2.1.2/go.mod h1:9nHVX6z3FCMCQPA7PThGcA55t22yKQfK/Dnsf5i7hUo=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
@@ -311,8 +363,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
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/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
@@ -325,7 +377,6 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goh-chunlin/go-onedrive v1.1.1 h1:HGtHk5iG0MZ92zYUtaY04czfZPBIJUr12UuFc+PW8m4=
github.com/goh-chunlin/go-onedrive v1.1.1/go.mod h1:N8qIGHD7tryO734epiBKk5oXcpGwxKET/u3LuBHciTs=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
@@ -381,6 +432,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -409,6 +461,7 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
@@ -461,8 +514,8 @@ github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141 h1:8i57QAi5u+iPAYze92bkIvZoHiS0J45ndul5glr/NE8=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.159 h1:6LZysc4iyO4cHB1aJsRklWfSEJr8CEhW7BmcM0SkYcU=
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.159/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -518,7 +571,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
@@ -579,8 +631,8 @@ github.com/mholt/archiver/v4 v4.0.0-alpha.8/go.mod h1:5f7FUYGXdJWUjESffJaYR4R60V
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ=
github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
@@ -640,8 +692,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g=
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
github.com/namedotcom/go/v4 v4.0.2 h1:4gNkPaPRG/2tqFNUUof7jAVsA6vDutFutEOd7ivnDwA=
github.com/namedotcom/go/v4 v4.0.2/go.mod h1:J6sVueHMb0qbarPgdhrzEVhEaYp+R1SCaTGl2s6/J1Q=
github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q=
github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY=
github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g=
@@ -687,8 +739,6 @@ github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
@@ -783,12 +833,8 @@ github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/
github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
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/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -797,6 +843,7 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
@@ -819,6 +866,7 @@ github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1Sd
github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -833,15 +881,12 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU=
github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128 h1:NGnqDc8FQL0YdiCHgTO4Wkso6ToD8rE3JW9VOzoPBNA=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 h1:mrJ5Fbkd7sZIJ5F6oRfh5zebPQaudPH9Y0+GUmFytYU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128/go.mod h1:zbsYIBT+VTX4z4ocjTAdLBIWyNYj3z0BRqd0iPdnjsk=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1208/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1210 h1:waSk2KyI2VvXtR+XQJm0v1lWfgbJg51iSWJh4hWnyeo=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1210/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
github.com/tencentyun/cos-go-sdk-v5 v0.7.54 h1:FRamEhNBbSeggyYfWfzFejTLftgbICocSYFk4PKTSV4=
github.com/tencentyun/cos-go-sdk-v5 v0.7.54/go.mod h1:UN+VdbCl1hg+kKi5RXqZgaP+Boqfmk+D04GRc4XFk70=
@@ -849,12 +894,15 @@ github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+x
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
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/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19 h1:ZCmSnT6CLGhfoQ2lPEhL4nsJstKDCw1F1RfN8/smTCU=
github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19/go.mod h1:SXTY+QvI+KTTKXQdg0zZ7nx0u94QWh8ZAwBQYsW9cqk=
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 h1:k9tdF32oJYwtjzMx+D26M6eYiCaAPdJ7tyN7tF1oU5Q=
@@ -868,22 +916,18 @@ github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/upyun/go-sdk v2.1.0+incompatible h1:OdjXghQ/TVetWV16Pz3C1/SUpjhGBVPr+cLiqZLLyq0=
github.com/upyun/go-sdk v2.1.0+incompatible/go.mod h1:eu3F5Uz4b9ZE5bE5QsCL6mgSNWRwfj0zpJ9J626HEqs=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/volcengine/volc-sdk-golang v1.0.199 h1:zv9QOqTl/IsLwtfC37GlJtcz6vMAHi+pjq8ILWjLYUc=
github.com/volcengine/volc-sdk-golang v1.0.199/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
github.com/volcengine/volc-sdk-golang v1.0.216 h1:+wAq8RvxpGECveRJaAXZFpzrZoQ33WjMuRyd9iY2Oc0=
github.com/volcengine/volc-sdk-golang v1.0.216/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
@@ -901,6 +945,7 @@ github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtX
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@@ -927,10 +972,10 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
@@ -939,27 +984,26 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU=
@@ -976,7 +1020,9 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -989,8 +1035,15 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1031,6 +1084,9 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.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.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1079,10 +1135,18 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
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=
@@ -1090,8 +1154,6 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1107,8 +1169,11 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
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.7.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=
@@ -1149,6 +1214,7 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1178,18 +1244,31 @@ 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.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
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.1.0/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.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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=
@@ -1203,15 +1282,19 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
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.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
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.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/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=
@@ -1250,6 +1333,7 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@@ -1263,6 +1347,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1329,10 +1415,10 @@ google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxH
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
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=
@@ -1350,8 +1436,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1378,6 +1464,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
@@ -1395,7 +1482,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/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.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
+37 -8
View File
@@ -62,7 +62,6 @@ ErrHttpReqTimeOut: 'Request timed out {{ .err }}'
ErrHttpReqFailed: 'Request failed {{ .err }}'
ErrNoSuchHost: 'Unable to find the requested server {{ .err }}'
ErrHttpReqNotFound: 'The requested resource {{ .err }} could not be found'
ErrImagePullTimeOut: 'Image pull timeout'
ErrContainerNotFound: '{{ .name }} container does not exist'
ErrContainerMsg: '{{ .name }} container is abnormal. Please check the log on the container page for details'
ErrAppBackup: '{{ .name }} application backup failed {{ .err }}'
@@ -80,9 +79,7 @@ AppStoreIsLastVersion: 'The App Store is already the latest version'
AppStoreSyncSuccess: 'App Store synchronization successful'
SyncAppDetail: 'Synchronize application configuration'
AppVersionNotMatch: '{{ .name }} application requires a higher 1Panel version, skipping synchronization'
MoveSiteDir: 'The current upgrade requires migration of the OpenResty website directory'
MoveSiteToDir: 'Migrate site directory to {{ .name }}'
ErrMoveSiteDir: 'Failed to migrate site directory'
MoveSiteDir: "Detected website directory change. Need to migrate OpenResty website directory to {{ .name }}"
MoveSiteDirSuccess: 'Successful migration of website directory'
DeleteRuntimePHP: 'Delete PHP runtime'
CustomAppStoreFileValid: 'App store packages need to be in .tar.gz format'
@@ -94,6 +91,15 @@ ErrAppVersionDeprecated: "The {{ .name }} application is not compatible with the
ErrDockerFailed: "The state of Docker is abnormal, please check the service status"
ErrDockerComposeCmdNotFound: "The Docker Compose command does not exist, please install this command on the host machine first"
#ssh
ExportIP: "Login IP"
ExportArea: "Location"
ExportPort: "Port"
ExportAuthMode: "Login Method"
ExportUser: "User"
ExportStatus: "Login Status"
ExportDate: "Time"
#file
ErrFileCanNotRead: 'This file does not support preview'
ErrFileToLarge: 'The file is larger than 10M and cannot be opened'
@@ -109,6 +115,9 @@ ErrFavoriteExist: 'This path has already been favorited'
ErrInvalidChar: 'Illegal characters are not allowed'
ErrPathNotDelete: 'The selected directory cannot be deleted'
ErrLogFileToLarge: "The log file exceeds 500MB and cannot be opened"
FileDropFailed: "Failed to clean up file {{ .name }}: {{ .err }}"
FileDropSuccess: "Successfully cleaned up file {{ .name }}, cleaned {{ .count }} files, freed {{ .size }} disk space"
FileDropSum: "File cleanup completed successfully, total {{ .count }} files cleaned, total {{ .size }} disk space freed"
#website
ErrAliasIsExist: 'Alias already exists'
@@ -124,6 +133,9 @@ ErrBuildDirNotFound: 'The build directory does not exist'
ErrImageNotExist: 'The operating environment {{ .name }} image does not exist, please re-edit the operating environment'
ErrProxyIsUsed: "Load balancing has been used by reverse proxy, cannot be deleted"
ErrSSLValid: 'Certificate file is abnormal, please check the certificate status!'
ErrWebsiteDir: "Please select a directory within the website directory."
ErrComposerFileNotFound: "composer.json file does not exist"
ErrRuntimeNoPort: "The runtime environment is not set with a port, please edit the runtime environment first."
#ssl
ErrSSLCannotDelete: 'The {{ .name }} certificate is being used by a website and cannot be deleted'
@@ -156,6 +168,10 @@ ExecShellSuccess: 'Script execution successful'
StartUpdateSystemSSL: 'Start updating system certificate'
UpdateSystemSSLSuccess: 'Update system certificate successfully'
ErrWildcardDomain: 'Unable to apply for wildcard domain name certificate in HTTP mode'
ErrApplySSLCanNotDelete: "The certificate {{.name}} being applied for cannot be deleted, please try again later."
StartPushSSLToNode: "Starting to push certificate to node"
PushSSLToNodeFailed: "Failed to push certificate to node: {{ .err }}"
PushSSLToNodeSuccess: "Successfully pushed certificate to node"
#mysql
ErrUserIsExist: 'The current user already exists, please re-enter'
@@ -173,6 +189,11 @@ ErrObjectInUsed: 'The object is in use and cannot be deleted'
ErrObjectBeDependent: 'This image depends on other images and cannot be deleted'
ErrPortRules: 'Port number does not match, please re-enter!'
ErrPgImagePull: 'Image pull timed out, please configure image acceleration or manually pull the {{ .name }} image and try again'
PruneHelper: "This cleanup removed {{ .name }} {{ .count }} items, freeing {{ .size }} disk space"
ImageRemoveHelper: "Deleted image {{ .name }}, freeing {{ .size }} disk space"
BuildCache: "Build cache"
Volume: "Storage volume"
Network: "Network"
#runtime
ErrFileNotExist: '{{ .detail }} file does not exist! Please check the integrity of the source file!'
@@ -205,8 +226,10 @@ SystemLog: 'System Log'
CutWebsiteLog: 'Rotate Website Log'
FileOrDir: 'Directory / File'
UploadFile: 'Uploading backup file {{ .file }} to {{ .backup }}'
IgnoreBackupErr: 'Backup failed, error: {{ .detail }}, ignoring this error...'
IgnoreUploadErr: 'Upload failed, error: {{ .detail }}, ignoring this error...'
Upload: "Upload"
IgnoreBackupErr: "Backup failed, error: {{ .detail }}, ignoring this error..."
IgnoreUploadErr: "Upload failed, error: {{ .detail }}, ignoring this error..."
LoadBackupFailed: "Failed to get backup account connection, error: {{ .detail }}"
#toolbox
ErrNotExistUser: 'The current user does not exist, please modify and try again!'
@@ -266,6 +289,7 @@ allow: 'allow'
deny: 'deny'
OpenrestyNotFound: 'Openresty is not installed'
remoteIpIsNull: "IP list is empty"
OpenrestyVersionErr: "Openresty version is too low, please upgrade Openresty to 1.27.1.2-2-2-focal"
#task
TaskStart: '{{ .name }} task starts [START]'
@@ -290,6 +314,7 @@ TaskPull: 'Pull'
TaskCommit: 'Commit'
TaskBuild: 'Build'
TaskPush: 'Push'
TaskClean: "Cleanup"
TaskHandle: 'Execute'
Website: 'Website'
App: 'Application'
@@ -321,6 +346,7 @@ SubTask: 'Subtask'
RuntimeExtension: 'Runtime Environment Extension'
TaskIsExecuting: 'Task is running'
CustomAppstore: 'Custom application warehouse'
TaskExec: 'Execute'
# task - ai
OllamaModelPull: 'Pull Ollama model {{ .name }}'
@@ -350,7 +376,6 @@ SnapCompressFile: 'Compress snapshot file'
SnapCheckCompress: 'Check snapshot compression file'
SnapCompressSize: 'Snapshot file size {{ .name }}'
SnapUpload: 'Upload snapshot file'
SnapLoadBackup: 'Get backup account information'
SnapUploadTo: 'Upload snapshot file to {{ .name }}'
SnapUploadRes: 'Upload snapshot file to {{ .name }}'
@@ -438,4 +463,8 @@ SSLAlert: "There are {{ .num }} SSL certificates on your 1Panel that will expire
DiskUsedAlert: "Your 1Panel disk '{{ .name }}' has used {{ .used }}. Please log in to the panel for details."
ResourceAlert: "The average {{ .name }} usage over {{ .time }} minutes on your 1Panel is {{ .used }}. Please log in to the panel for details."
PanelVersionAlert: "A new version of 1Panel is available. Please log in to the panel to upgrade."
PanelPwdExpirationAlert: "Your 1Panel password will expire in {{ .day }} days. Please log in to the panel for details."
PanelPwdExpirationAlert: "Your 1Panel password will expire in {{ .day }} days. Please log in to the panel for details."
CommonAlert: "Your 1Panel, {{ .msg }}, please log in to the panel for details."
NodeExceptionAlert: "Your 1Panel, {{ .num }} nodes are abnormal, please log in to the panel for details."
LicenseExceptionAlert: "Your 1Panel, {{ .num }} licenses are abnormal, please log in to the panel for details."
SSHAndPanelLoginAlert: "Your 1Panel, abnormal panel {{ .name }} login from {{ .ip }}, please log in to the panel for details."

Some files were not shown because too many files have changed in this diff Show More