Compare commits

...
288 Commits
Author SHA1 Message Date
ssongliu 217d780ed1 fix: Fix the issue of abnormal time display on the monitoring page (#11001) 2025-11-20 02:11:01 +00:00
蘭 fe62dc18fa fix: Fix the issue of creating file/folder hierarchy errors (#10999) 2025-11-19 08:42:59 +00:00
KOMATA d0e95903c9 chore: disable CSS code splitting in Vite config (#10996) 2025-11-19 14:03:23 +08:00
CityFun 1ec7f519c6 fix: resolve certificate push losing other domain data (#10998)
Refs https://github.com/1Panel-dev/1Panel/issues/10988
2025-11-19 06:02:59 +00:00
CityFun 450e8b18cf refactor: remove unused code (#10995) 2025-11-19 03:50:59 +00:00
ssongliu bb882286aa fix: Fix the monitoring settings menu title display issue (#10994) 2025-11-19 03:48:58 +00:00
蘭 d9bbde9e50 feat: code editor supports creating files and folders (#10989)
#10424
2025-11-18 14:34:58 +00:00
CityFun 29746d1b32 refactor: remove unused code (#10987) 2025-11-18 19:12:37 +08:00
ssongliu 5f211961ae fix: Fix the issue of abnormal disk display on the overview page (#10986)
Refs #10982
2025-11-18 09:26:57 +00:00
CityFun 91d364b18b feat: improve LVM partition recognition in disk management (#10985) 2025-11-18 06:22:56 +00:00
CityFun f1677f5a67 fix: resolve network connection count always showing 0 in process list (#10979) 2025-11-18 14:04:23 +08:00
KOMATA fd270263f4 fix: Update Enter key event handling in terminal input to prevent default behavior (#10984) 2025-11-18 14:04:00 +08:00
ssongliu 81c1e90e54 chore: Clean up some unused code (#10977) 2025-11-17 08:32:54 +00:00
ssongliu 3b737184ea fix: Fix the issue of postgres database not being synchronized (#10976)
Refs #10966
2025-11-17 08:30:55 +00:00
ssongliu 4779bffceb fix: Fix the system proxy validation issue (#10975)
Refs #10974
2025-11-17 08:28:55 +00:00
KOMATA 4209ee4486 feat: enhance S3 region selection with dropdown and custom input option (#10967)
* feat: enhance S3 region selection with dropdown and custom input option

* refactor: streamline S3 region selection and endpoint handling in backup account settings
2025-11-17 13:39:33 +08:00
ssongliu 6ca352bf85 fix: Fix the issue where pressing Enter to submit in description edit… (#10972)
…ing does not take effect
2025-11-17 04:18:53 +00:00
CityFun a4d77a1cc2 fix: resolve incorrect container filter condition when redirecting applications (#10971) 2025-11-17 11:44:32 +08:00
ssongliu b9367abfb9 feat: Optimize container cleanup task logs (#10970) 2025-11-17 03:40:53 +00:00
ssongliu a6062897d5 fix: Support encrypted backup of databases (#10963) 2025-11-14 22:12:55 +08:00
蘭 1afe84ed02 fix: Fix menu drag and drop issue (#10962)
#10961
2025-11-14 10:24:53 +00:00
ssongliu 388458f759 feat: Image build supports setting build parameters (#10960)
Refs #10898
2025-11-14 10:22:49 +00:00
CityFun 512ff23f19 feat: add tooltips for transfer application (#10959) 2025-11-14 05:44:49 +00:00
ssongliu 45a32ff255 chore: remove debug log in controller (#10958) 2025-11-14 05:42:50 +00:00
蘭 e4e2d76f75 feat: File manager multi-tab start path optimization (#10957)
#10302
2025-11-14 05:40:52 +00:00
KOMATA 282e196534 refactor: Simplify PHP config update logic by replacing regex with a dedicated function for key matching (#10956) 2025-11-14 13:39:39 +08:00
ssongliu e4fbd3cf86 fix: Fix the issue of abnormal ufw status loading (#10949) 2025-11-13 21:36:35 +08:00
ssongliu 402931862f fix: Fix the issue where iptables description does not take effect (#10947) 2025-11-13 08:36:47 +00:00
CityFun a70e2635ab fix(ssl): prevent domain loss when editing certificate remark (#10946) 2025-11-13 15:43:25 +08:00
ssongliu 8c9ae38017 fix: Modify the binding position of the 1PANEL_OUTPUT chain (#10945) 2025-11-13 15:43:10 +08:00
CityFun 3920076bd9 feat(website): support 128 characters for site alias (#10943)
Refs https://github.com/1Panel-dev/1Panel/issues/10787
2025-11-13 07:42:46 +00:00
ssongliu 55cbf572e0 fix: Fix the issue where deleting rules in iptables advanced settings… (#10942)
… fails
2025-11-13 06:38:47 +00:00
CityFun c4bd4734a5 feat(log): optimize log display (#10941) 2025-11-13 14:37:39 +08:00
ssongliu e7810febd6 fix: Fix the issue of abnormal context in the cmd method (#10939) 2025-11-13 14:37:28 +08:00
ssongliu 0bc9e85734 fix: Fix the issue of abnormal iptables range port addition (#10938) 2025-11-12 22:26:59 +08:00
蘭 4d85ae5fa6 fix: Fix the issue of initializing SMS sending quantity as a string (#10936) 2025-11-12 14:24:45 +00:00
蘭 a7342d6ef2 fix: More button operations rename using drawer processing parameters (#10933)
#10591
2025-11-12 10:30:46 +00:00
ssongliu 5b0fccd19a fix: Fix the issue of license status refresh after import (#10934) 2025-11-12 18:29:10 +08:00
ssongliu ebbfed711c fix: Modify the service backup directory (#10930) 2025-11-12 10:28:44 +00:00
ssongliu d631888d09 fix: Modify the unbound status of iptables firewall (#10928) 2025-11-12 06:46:44 +00:00
KOMATA afd62e8539 fix: Fix bug in iptables firewall module (#10927) 2025-11-12 11:49:24 +08:00
ssongliu 4aca66c417 fix: Add existence check when restarting Docker (#10926)
Refs  #10890
2025-11-12 02:24:42 +00:00
ssongliu 944463e9a8 fix: Fix the issue of excessively long container detail ports (#10924) 2025-11-11 22:52:57 +08:00
ssongliu cf4274afdc feat: support paginated viewing of imported data (#10923) 2025-11-11 22:41:28 +08:00
ssongliu 102f7c316d fix: display iptables default allowed ports (#10922) 2025-11-11 14:22:42 +00:00
KOMATA ffc81ea85a fix: enhance ping status and update logic in firewall service (#10920)
* fix: Update ping status check method

* fix: Enhance ping status update logic and support for alternative sysctl configuration path
2025-11-11 22:20:09 +08:00
KOMATA 02d22ba63f feat: refactor regex usage across multiple files and centralize patterns in a new utility (#10896) 2025-11-11 16:14:09 +08:00
CityFun 51e98623c9 feat: add MODEL_PATH environment for tensorrt llm (#10921) 2025-11-11 15:41:29 +08:00
ssongliu 8e94170fed fix: modify some API documentation (#10919)
Refs #10893
2025-11-11 06:26:41 +00:00
ssongliu 42cee513e0 style: modify license binding display information (#10918)
Refs #10895
2025-11-11 06:24:40 +00:00
CityFun 2c18382f46 feat: optimize website log display (#10914) 2025-11-11 06:22:40 +00:00
CityFun 3773412139 feat: order env for tensorrt_llm (#10916) 2025-11-11 14:09:25 +08:00
ssongliu e17bbdd6a7 fix: Fix the issue of abnormal option switching on the monitoring page (#10913) 2025-11-11 11:26:24 +08:00
巴山夜语 82a0f3a770 feat: Add utility to retrieve service configuration paths (#10891) 2025-11-11 11:12:34 +08:00
ssongliu 11e16abe64 fix: modify cronjob name validation (#10911) 2025-11-10 22:37:42 +08:00
ssongliu 6db79f4b7b feat: support iptables firewall control (#10903) 2025-11-10 21:17:53 +08:00
ssongliu 977a4be3c1 fix: Fix the issue of abnormal application port forwarding (#10910) 2025-11-10 10:52:40 +00:00
CityFun 51ae668154 fix: Fixed issue with update runtime ExtraHosts failed (#10909) 2025-11-10 10:38:39 +00:00
CityFun c6fe487e46 fix: Fixed the issue of database transfer failure to child nodes (#10906) 2025-11-10 18:09:39 +08:00
KOMATA ec4533e7de feat: enhance terminal connection UI with improved popover design and search functionality (#10902) 2025-11-10 17:27:47 +08:00
CityFun 3b73de32ad feat: support tensorrt LLM speedup (#10905) 2025-11-10 08:40:37 +00:00
CityFun da5668ac27 fix: Fix incorrect upgrade notification issue (#10901) 2025-11-10 04:02:38 +00:00
刘瑞斌 c1039fb7f2 feat: Add ResizeObserver to dynamically adjust terminal size on container resize (#10894) 2025-11-10 09:39:02 +08:00
CityFun c82d6838c5 feat: Optimize system disk detection logic (#10889)
Refs https://github.com/1Panel-dev/1Panel/issues/10888
2025-11-07 10:06:31 +00:00
CityFun c40c7b57cb feat: Change the order of backup buttons (#10886) 2025-11-07 08:36:31 +00:00
CityFun 009237a35a feat: change gin start mode (#10885) 2025-11-07 08:34:30 +00:00
KOMATA f4f6f65b4c feat: add lighter fill color and table row hover background for dark theme (#10883) 2025-11-07 14:51:27 +08:00
CityFun 313a8a3380 chore(deps): bump dependencies in go.mod (#10884) 2025-11-07 06:50:30 +00:00
CityFun f686e7cbc4 fix: Fixed issue where applications failed to load when creating deployment websites (#10882) 2025-11-07 06:48:30 +00:00
CityFun 7fa39687b5 fix: Fixed issue where original Compose file content was cleared during comparison (#10880) 2025-11-07 06:46:30 +00:00
CityFun 9d84120181 feat: Add log cleanup support for scheduled tasks (#10871)
Refs https://github.com/1Panel-dev/1Panel/issues/5639
2025-11-05 10:08:25 +00:00
CityFun 3be0dab8a3 fix: Fix the issue where PHP extensions fail to install. (#10869) 2025-11-05 15:34:02 +08:00
CityFun 3a9275d5fa feat: Optimize the check for deleting associated databases when removing app (#10868) 2025-11-05 07:32:27 +00:00
ssongliu 6296aa04c6 feat: Cronjob reports add an unexecuted status (#10866)
Refs #10861
2025-11-05 07:30:25 +00:00
KOMATA cfe9e94e16 feat: add sensitive info mask (#10865) 2025-11-05 09:38:42 +08:00
ssongliu 902d329e78 feat: Container overview page supports displaying disk usage information (#10864) 2025-11-04 22:53:54 +08:00
CityFun 62f59eb1c3 feat: Add a check during upgrade to detect whether the Compose file has been modified. (#10863) 2025-11-04 22:53:33 +08:00
CityFun a7d4c4ebec feat: Optimize environment port mappings (#10856) 2025-11-04 15:55:16 +08:00
CityFun 0ca96621d4 feat: Edit the application Compose content to add a comparison. (#10855) 2025-11-04 15:36:34 +08:00
ssongliu 7ea179c2da fix: SSH client execution compatible with controller (#10854) 2025-11-04 15:36:18 +08:00
CityFun 6982396572 style: Optimize batch delete website (#10853)
Refs https://github.com/1Panel-dev/1Panel/issues/10851
2025-11-04 06:14:24 +00:00
KOMATA 36c4fbfeee fix: disable ip input when choosing unsupported network type (#10849) 2025-11-04 09:51:35 +08:00
CityFun 868c6f47a0 style: Optimize application installation style (#10847)
Refs https://github.com/1Panel-dev/1Panel/issues/10845
2025-11-03 10:46:23 +00:00
KOMATA 74695ab276 fix: add copy button for image name (#10837) 2025-11-03 16:47:04 +08:00
KOMATA cb74d8d51b feat: Add network details drawer component (#10836) 2025-11-03 16:46:39 +08:00
ssongliu eaa620b081 feat: Add real-time process display on the overview page (#10833) 2025-10-31 10:36:19 +00:00
CityFun 18ab07ec4f feat: Support application migration to other nodes. (#10832)
Refs https://github.com/1Panel-dev/1Panel/issues/10509
2025-10-31 10:34:20 +00:00
ssongliu e86bc9a8db feat: Container operations support task logs (#10824)
Refs #10817
2025-10-30 09:42:18 +00:00
ssongliu 75837c87b4 fix: Update swagger API documentation (#10822)
Refs #10816
2025-10-30 08:50:18 +00:00
ssongliu 9a73095a8f feat: System monitoring supports process viewing (#10821)
Refs #3437
2025-10-30 08:48:18 +00:00
wanghe-fit2cloud 9242bf6482 feat: service handling on more platforms 2025-10-30 14:50:33 +08:00
CityFun f552a0ac36 feat: update docker sdk (#10814) 2025-10-29 05:42:17 +00:00
ssongliu 141e2f61eb fix: Optimize the compose env file prompt message (#10813)
Refs #7604
2025-10-29 05:40:19 +00:00
蘭 b6d7b319f2 fix: Fix the issue of failure caused by incorrect email service parameters (#10811)
#10803
2025-10-29 05:38:18 +00:00
ssongliu 4cdc6c80ec feat: Compose templates support import and export (#10809) 2025-10-29 13:36:24 +08:00
Jimes cee9d7ffb0 chore: Correct typo in redirect text 2025-10-29 09:05:24 +08:00
Jimes 3f48bdb216 chore: replace '登陆' with '登录' 2025-10-29 09:03:58 +08:00
CityFun 2cef8dcea4 feat: change tensorrt button logic (#10805) 2025-10-28 18:56:45 +08:00
CityFun c327936db1 core: update github.com/docker/compose/v2 version (#10804) 2025-10-28 10:20:16 +00:00
CityFun 1b53cd4ca8 feat: add sync waf ip group cron job (#10802)
Refs https://github.com/1Panel-dev/1Panel/issues/8915
2025-10-28 10:18:18 +00:00
ssongliu 08f9f6f8e5 feat: Support using proxies for inter-node requests (#10799)
Refs #9903
2025-10-28 10:16:18 +00:00
CityFun fb6b2729c6 feat: Website Load balancer config support fail_timeout (#10796)
Refs https://github.com/1Panel-dev/1Panel/issues/10565
2025-10-28 08:28:16 +00:00
CityFun aa55a18ed8 feat: Fix highlighting issues in the ssh config editor. (#10794)
Refs https://github.com/1Panel-dev/1Panel/issues/6496
2025-10-28 08:26:18 +00:00
ssongliu 6c74c64b29 style: Optimize the container details page style (#10791) 2025-10-28 08:24:17 +00:00
maolei fddbf34684 feat: Add automatic SSL certificate selection when creating a website with HTTPS enabled (#10789)
Optimize the default selection logic after the SSL list is loaded.
Add a flagging mechanism for manual SSL certificate selection, which disables automatic selection once triggered.
Add listeners for changes to the primary and more domains to trigger this automatic selection logic.
2025-10-28 13:49:34 +08:00
KOMATA ea878bba73 feat: add Docker Inspect dialog with tabbed container info view and enhanced visual formatting (#10783)
* feat: Expand Docker inspect detail drawer

* chore: i18n

* fix: switch order in mount detail card.
2025-10-27 18:11:50 +08:00
CityFun db002ec915 feat: Add settings to prevent certificate leakage. (#10785)
Refs https://github.com/1Panel-dev/1Panel/issues/10755
2025-10-27 10:06:17 +00:00
ssongliu 606d3c1717 fix: Improve system monitoring settings (#10784)
Refs #5121 #8390
2025-10-27 10:04:17 +00:00
CityFun b0fbb39cd3 style: Optimize app name display for large screens (#10782)
Refs https://github.com/1Panel-dev/1Panel/issues/10764
2025-10-27 10:02:18 +00:00
ssongliu 2478d26c59 fix: Add validation rules for scheduled task names (#10780)
Refs #10741
2025-10-27 10:00:18 +00:00
ssongliu e072620f9b style: Optimize the style of firewall import and export (#10778) 2025-10-27 17:59:06 +08:00
CityFun dd3419bf52 feat: support architecture detail display for local app (#10777)
Refs https://github.com/1Panel-dev/1Panel/issues/10770
2025-10-27 09:58:16 +00:00
ssongliu f12e70ef5d feat: Support importing licenses when adding nodes (#10776)
Refs #9793
2025-10-27 09:56:16 +00:00
ssongliu 2836207850 fix: Fix the issue of abnormal redirection after deleting a tab (#10774)
Refs #10767
2025-10-27 09:54:16 +00:00
ssongliu 4253b48bba fix: Fix the issue of abnormal menu tab switching (#10773)
Refs #10765
2025-10-27 09:52:18 +00:00
KOMATA ff36b65af4 feat: Optimize i18n by dynamically loading module (#10759)
* feat: Optimize internationalization support, dynamically load language packs and update language settings

* feat: Refactor i18n initialization to load initial messages dynamically
2025-10-27 17:49:52 +08:00
KOMATA 1ef4803954 feat: Enhance cache display and setting in proxy configuration (#10751)
* feat: Enhance caching options for proxy configuration

* fix: typo

* feat: Update caching and directive handling in proxy configuration

* feat: Update default cache time and unit to improve caching configuration

* feat: Implement RemoveCorsOption method to streamline CORS directive removal

* feat: Enhance cache display and initialization in proxy configuration

* refactor: Clean up AddBrowserCache and RemoveCache methods by removing commented-out code
2025-10-24 18:17:51 +08:00
CityFun 08ebbee328 feat: support bach set website group (#10754) 2025-10-24 10:00:14 +00:00
ssongliu d8e4144d46 feat: Child nodes support upgrading together with the master node (#10753)
Refs #9647
2025-10-24 09:14:15 +00:00
CityFun 0b1819576d feat: runtime support config extra_hosts (#10750)
Refs https://github.com/1Panel-dev/1Panel/issues/10250
2025-10-24 08:56:15 +00:00
BugKing 6ce33754a8 chore: Add id attribute to welcome dialog (#10746) 2025-10-24 15:45:11 +08:00
KOMATA 21e0808a86 fix: Replace RemoveDirective with RemoveDirectiveByFullParams for better parameter matching (#10748) 2025-10-24 15:19:32 +08:00
KOMATA 3480182473 feat: Refactor quick command function, turn cascader into palette (#10732) 2025-10-23 17:20:16 +08:00
CityFun e516158294 feat: Reduce Echarts bundle size (#10731) 2025-10-23 17:17:42 +08:00
ssongliu c8ee0af152 fix: Fix the packaging failure issue (#10734) 2025-10-23 16:40:45 +08:00
ssongliu 3b82d2a1ee feat: Add xplay welcome page (#10733) 2025-10-23 16:27:11 +08:00
CityFun 274fd526b7 feat: optimization waf map load (#10730) 2025-10-23 16:00:23 +08:00
ssongliu 86b0e01d9d fix: Optimize the container command input method (#10724) 2025-10-22 18:33:10 +08:00
ssongliu c06ba18023 feat: compatible with openrc and sysvinit (#10723) 2025-10-22 18:32:42 +08:00
KOMATA f462591e9c feat: add firewall rule import/export with JSON validation and alerts (#10706) 2025-10-22 18:31:26 +08:00
CityFun da23b8e6d9 feat: update zh-Hant.ts (#10717) 2025-10-22 13:54:06 +08:00
david082321 a1799f5b4b feat: Update zh-Hant translation (#10716) 2025-10-22 13:43:24 +08:00
CityFun 34496f6a00 feat: clear model when create llm (#10711) 2025-10-21 17:53:20 +08:00
CityFun 5d8208a061 feat: add some prompt for tensorRT LLM (#10710) 2025-10-21 08:56:12 +00:00
CityFun f94e60c53c feat: support edit command for tensorrtLLM (#10708) 2025-10-21 15:29:14 +08:00
wanghe-fit2cloud dd70be51b3 Revert "fix: set TensorRT LLM menu visibility (#10693)"
This reverts commit 79d9d9dfc2.
2025-10-20 21:49:03 +08:00
蘭 12e1a6a855 fix: App menu and setting menu cannot be set as secondary menu (#10701) 2025-10-20 21:47:32 +08:00
CityFun 07648367c1 feat: change local php runtime helper (#10700) 2025-10-20 10:58:13 +00:00
CityFun 5b86ca38e0 fix: Resolve installation failures of OpenResty on low-memory machines. (#10697) 2025-10-20 18:17:38 +08:00
ssongliu 79d9d9dfc2 fix: set TensorRT LLM menu visibility (#10693) 2025-10-20 12:01:40 +08:00
CityFun e667477f4b feat: change tensorrt-llm command (#10692) 2025-10-20 11:54:27 +08:00
ssongliu 0367149386 feat: support for fusion xplay (#10691) 2025-10-20 03:37:32 +00:00
CityFun 3d0ce2add4 feat: add image input for tensorrt llm (#10690) 2025-10-20 11:21:37 +08:00
CityFun 17224b8920 feat: ai model support tensorRT LLM (#10688) 2025-10-18 22:49:48 +08:00
蘭 05d125c762 fix: Fix the problem of garbled Chinese characters in file names (#10675) 2025-10-17 08:43:51 +00:00
ssongliu e1a268b21d fix: Modify watermark setting prompt information (#10674) 2025-10-17 08:41:50 +00:00
BugKing c486e8a354 style: add id for login container (#10676) 2025-10-17 16:39:48 +08:00
ssongliu eba74d91b3 style: Optimize the overview page style (#10670) 2025-10-17 10:20:21 +08:00
ssongliu 613f8c2b57 fix: Watermark differentiation for light and dark themes (#10669) 2025-10-17 10:10:54 +08:00
ssongliu 16e2ad64a2 style: Fix the issue of incomplete watermark display (#10668) 2025-10-16 18:54:46 +08:00
CityFun 3274895dcf feat: remove unused code (#10667) 2025-10-16 17:29:38 +08:00
蘭 7367d47d27 feat: Optimize menu display sorting (#10666) 2025-10-16 17:17:16 +08:00
CityFun 4ed2a03222 feat: support 1pctl app init command (#10665) 2025-10-16 17:05:31 +08:00
ssongliu 586df4627a style: Fix the issue of incomplete watermark display (#10664) 2025-10-16 16:57:44 +08:00
蘭 25181b6c27 fix: Handling drag and drop uploads that are blocked (#10663) 2025-10-16 15:05:58 +08:00
ssongliu 72e6b9b188 feat: Add python2 and python3 options to the cronjob interpreter (#10662) 2025-10-16 14:40:41 +08:00
蘭 e2efa20582 feat: Menu supports restoring default values (#10658) 2025-10-16 14:40:09 +08:00
CityFun 2cebb1beca Fix Display Issues in the Editing Runtime Environment (#10660) 2025-10-16 06:31:40 +00:00
ssongliu fb24a699e6 fix: Fix the issue of abnormal refresh after container creation/editing (#10657) 2025-10-16 06:29:40 +00:00
蘭 1c16a0a69f feat: Add icons to the menu (#10650) 2025-10-15 17:43:40 +08:00
ssongliu 26e480f242 fix: Fix the issue where pressing Enter in the image tag drawer causes a refresh (#10649) 2025-10-15 17:37:18 +08:00
CityFun fcf864156d feat: Optimize image pulling during application installation (#10648) 2025-10-15 17:37:07 +08:00
ssongliu d773391763 style: Optimize the border style of the cronjob creation card (#10647) 2025-10-15 17:36:55 +08:00
蘭 b47d63330e feat: Optimize favicon (#10646) 2025-10-15 17:36:41 +08:00
ssongliu 026ed1eb42 fix: Fix the issue of abnormal container sharing mode modification (#10644) 2025-10-15 13:38:27 +08:00
蘭 fb80724a96 feat: Favorite files support directory redirection (#10641)
#9936
2025-10-15 03:29:36 +00:00
CityFun a7ab866e95 fix: Fixed issue with install openresty failed (#10643) 2025-10-15 11:29:32 +08:00
ssongliu 06f636c9aa feat: Swap settings support task mode (#10639) 2025-10-14 22:12:27 +08:00
ssongliu bbc5b8b731 fix: Fix the issue of abnormal panel redirection (#10638) 2025-10-14 18:30:06 +08:00
CityFun cd154b13a9 feat: remove unused function (#10637) 2025-10-14 18:20:47 +08:00
蘭 f71a8a1603 feat: Support configuration of SMTP username (#10636) 2025-10-14 18:04:41 +08:00
CityFun d2ecae7e97 feat: Optimize Supervisord log reading (#10634) 2025-10-14 18:04:20 +08:00
蘭 649e7b1912 feat: The panel logo setting supports SVG images (#10633) 2025-10-14 16:51:48 +08:00
CityFun 2a97b0a9de fix: Fixed issue with edit runtime failed (#10631) 2025-10-14 16:51:38 +08:00
ssongliu 5ca02a1dcf style: Optimize watermark style (#10630) 2025-10-14 16:51:26 +08:00
蘭 f088cec4d5 feat: The menu supports drag and drop sorting (#10626) 2025-10-14 16:51:15 +08:00
ssongliu dde40064d2 fix: Fix the issue of abnormal terminal settings (#10621) 2025-10-13 21:29:14 +08:00
CityFun 597000ce52 fix: Solve the issue of being unable to filter applications when the operating system is aarch64. (#10620) 2025-10-13 21:28:53 +08:00
CityFun 4597cb26a1 style: change process management style (#10619) 2025-10-13 21:28:36 +08:00
ssongliu e16ab09253 style: Modify some tamper-proof notification messages (#10617) 2025-10-13 21:28:18 +08:00
ssongliu 595be6f470 fix: Fix the issue of abnormal refreshing on the overview page (#10615) 2025-10-13 21:28:01 +08:00
ssongliu 7afe9ac878 fix: Fix the issue of abnormal task logs when website restoration fails (#10609) 2025-10-11 22:07:58 +08:00
CityFun 1f53b64b08 feat: update github.com/nwaples/rardecode/v2 (#10608) 2025-10-11 17:03:17 +08:00
CityFun 49ca336dd6 feat: add cors config for website (#10607) 2025-10-11 08:04:26 +00:00
ssongliu 7cabb54c4e fix: Fix the issue of abnormal container task log formatting (#10606) 2025-10-11 15:36:14 +08:00
CityFun f55841f41c feat: Added name display to the batch operation website. (#10600) 2025-10-10 10:03:27 +00:00
CityFun 8983cb6186 fix: Fixed the issue of task log crashes. (#10599) 2025-10-10 17:24:11 +08:00
ssongliu 222181baff feat: Support batch operations for image tags (#10597) 2025-10-10 17:23:59 +08:00
CityFun 02c002bda6 support php slow log (#10596)
Refs https://github.com/1Panel-dev/1Panel/issues/9467
2025-10-10 09:23:25 +00:00
ssongliu a5fbd01ea5 style: Adjust the overview page style (#10595) 2025-10-10 06:15:25 +00:00
CityFun 3652e07988 feat: The runtime environment website supports offline mode. (#10589) 2025-10-09 18:08:01 +08:00
ssongliu 6ed000985c style: Adjust the overview page style (#10588) 2025-10-09 10:07:26 +00:00
CityFun 4a8b524a7c fix: Fixed issue with install node module failed (#10587) 2025-10-09 18:06:40 +08:00
Isaac 39509ad9a8 解决目录遍历漏洞问题 (#10586) 2025-10-09 16:01:00 +08:00
CityFun f977a2488d feat: Offline version supports creating PHP (#10585) 2025-10-09 06:51:24 +00:00
ssongliu e7b1aadb0e fix: Fix the issue of missing parameters when creating macvlan (#10583)
Refs #10580
2025-10-09 06:43:25 +00:00
CityFun 339b085db7 feat: Optimize application parameter editing (#10582)
Refs https://github.com/1Panel-dev/1Panel/issues/10558
2025-10-09 06:41:25 +00:00
ssongliu a7c00900d8 fix: Fix the issue of abnormal deletion of cronjob report files (#10581) 2025-10-09 14:40:42 +08:00
ssongliu 99023569fc feat: Support editing ssh key information (#10579)
Refs #10537
2025-10-09 06:39:25 +00:00
Li lingyi e4478da423 Fix misskey's activitypub protocol violation from 1panel's mistake (#10567)
Fix misskey's activitypub protocol violation from 1panel's mistake
<https://akollo.2s.ink/notes/addwemgrqzn70086>
<https://github.com/1Panel-dev/1Panel/issues/10566>
😂😂😂😂😂😂
2025-10-09 11:54:15 +08:00
双向极端andlive d0bb9165bf feat: Add inbound interface restriction option for ufw forward rules (#10345) (#10549)
* 修复https防窜站关闭时修改默认站点报错的问题

* feat: Add inbound interface restriction option for ufw forward rules (#10345)

---------

Co-authored-by: live <Anxys@outlook.com>
2025-10-02 20:33:20 +08:00
CityFun 72a0e0200e feat: support config cors in website proxy (#10551)
Refs https://github.com/1Panel-dev/1Panel/issues/7211
Refs https://github.com/1Panel-dev/1Panel/issues/7386
2025-09-30 09:34:22 +00:00
蘭 5d83f21a26 feat: File management supports file format conversion (#10541)
#10028
2025-09-29 09:26:21 +00:00
CityFun 56936aa6cd feat: support uploading local certificate files (#10540)
Refs https://github.com/1Panel-dev/1Panel/issues/8606
2025-09-29 09:24:20 +00:00
CityFun f4a649dc2f feat: support custom extension sources for PHP runtime (#10539) 2025-09-29 14:23:01 +08:00
CityFun 464ca8d272 feat: Delete associated database when deleting website (#10535)
Refs https://github.com/1Panel-dev/1Panel/issues/9168
2025-09-29 06:22:19 +00:00
ssongliu 7897bdabc4 style: Optimize the restriction prompt for quick access on the dashboard (#10534) 2025-09-29 06:20:20 +00:00
ssongliu 3675c2f3f3 fix: Fix the issue of abnormal 1pctl language display after upgrade (#10533) 2025-09-29 14:18:50 +08:00
ssongliu 4eb264bea4 feat: Support batch image pulling (#10532)
Refs #10521
2025-09-29 06:18:20 +00:00
ssongliu 129e864975 style: Adjust the maximum height of network card options (#10529)
Refs #10526
2025-09-28 10:24:16 +00:00
ssongliu 7f05c1ef33 fix: Fix the issue of abnormal container network creation (#10527)
Refs #10511
2025-09-28 10:22:16 +00:00
ssongliu 97d6d9c69f fix: Fix the issue of abnormal default network card display (#10525)
Refs #10520
2025-09-28 10:20:16 +00:00
ssongliu 8937acee13 feat: Support manual stopping of shell-type tasks (#10524)
Refs #10067
2025-09-28 10:18:17 +00:00
CityFun 7518c5fe95 feat: Update the local certificate and remember the directory address. (#10523)
Refs https://github.com/1Panel-dev/1Panel/issues/8478
2025-09-28 10:16:18 +00:00
CityFun 45fbb9f010 feat: Certificate Add ID Display (#10522)
Refs https://github.com/1Panel-dev/1Panel/issues/10513
2025-09-28 10:14:16 +00:00
ssongliu eff55da020 fix: Fix the issue of double quote escaping in cronjob (#10519) 2025-09-28 15:51:53 +08:00
ssongliu e87bf0b3fc fix: Modify the style of the file exclusion control (#10504) 2025-09-26 10:08:11 +00:00
ssongliu 3f141346fa feat: Support checking proxy server connection availability (#10502)
Refs #9843
2025-09-26 09:24:12 +00:00
CityFun 34b39f6eb8 feat: Node Runtime support for pm2 (#10501)
Refs https://github.com/1Panel-dev/1Panel/issues/3176
2025-09-26 09:22:12 +00:00
CityFun b8fd8ee0d9 feat: handle for install openresty (#10500) 2025-09-26 15:15:33 +08:00
ssongliu b8895f215c fix: Fix the issue where cache cleanup fails to exclude logs of curre… (#10497)
…ntly used

Refs #10484
2025-09-26 07:14:11 +00:00
ssongliu c4b3efeb9d fix: Fix the issue where cronjob expressions do not support commas (#10493)
Refs #9748
2025-09-26 07:12:13 +00:00
CityFun 8f0c84c2a5 feat: The runtime supports offline mode (#10499) 2025-09-26 07:10:12 +00:00
CityFun 90b9c53420 feat: Cache the last created website type. (#10496)
Refs https://github.com/1Panel-dev/1Panel/issues/9475
2025-09-26 07:08:12 +00:00
CityFun 46f9f4f561 feat: Optimize the Web Access URL field. (#10492)
Refs https://github.com/1Panel-dev/1Panel/issues/10475
2025-09-26 07:06:10 +00:00
双向极端andlive de265a19b2 修复https防窜站关闭时修改默认站点报错的问题 (#10490)
Co-authored-by: live <Anxys@outlook.com>
2025-09-26 10:21:30 +08:00
ssongliu 8af6868694 feat: Support customizing whether scripts are automatically synchronized (#10487)
Refs #10057
2025-09-25 10:44:08 +00:00
ssongliu b1dde54e38 feat: Fix the issue of abnormal node name display in the Community Ed… (#10485)
…ition
2025-09-25 10:16:09 +00:00
ssongliu 0a6163c89b feat: Support customizing the number of upgrade backup copies (#10483)
Refs #7361
2025-09-25 10:14:09 +00:00
CityFun 07d0b37692 feat: Nginx configuration supports shortcut commenting. (#10482)
Refs https://github.com/1Panel-dev/1Panel/issues/3606
2025-09-25 07:30:08 +00:00
CityFun ac9881dd4c feat: The runtime supports offline mode. (#10481) 2025-09-25 15:26:59 +08:00
ssongliu 280c1b4fea feat: Support authkeys management (#10480)
Refs #5247
2025-09-25 07:26:10 +00:00
CityFun 6b24ee23e6 feat: Support for Quick Website Name Editing (#10479) 2025-09-25 15:25:23 +08:00
CityFun d93f8c5e6e feat: Adapt to Offline Mode (#10473) 2025-09-24 10:56:07 +00:00
CityFun 384f065040 feat: Adapt to Offline Mode (#10472) 2025-09-24 17:40:44 +08:00
CityFun 672fedc7e5 feat: support config environment for supervisor (#10471) 2025-09-24 17:40:32 +08:00
CityFun 0433a26a07 feat: Optimize App Update Log Display (#10469) 2025-09-24 17:40:20 +08:00
ssongliu 4ebd4bddee feat: Support adding IPv6 nodes (#10470)
Refs #9074
2025-09-24 09:40:08 +00:00
ssongliu 030ab743f3 feat: Support customizing the master node name (#10468)
Refs #8964
2025-09-24 09:38:08 +00:00
CityFun d4623a68dd fix: Solve the issue of missing styles in WordPress after enabling HTTPS (#10467) 2025-09-24 09:36:08 +00:00
CityFun a38d1c96d8 feat: add acme-dns provider for ssl dns (#10464)
Refs https://github.com/1Panel-dev/1Panel/issues/10048
2025-09-24 09:34:07 +00:00
CityFun 61ddf89fdd feat: add Ovh provider for ssl dns (#10458) 2025-09-23 19:08:48 +08:00
ssongliu 5c7d9b4ffe feat: Container mounted volumes support shared customization (#10457)
Refs  #8443
2025-09-23 10:44:05 +00:00
CityFun 563ea985a4 perf: Optimize Process-Network Page Performance (#10456)
Refs https://github.com/1Panel-dev/1Panel/issues/6900
2025-09-23 10:32:05 +00:00
CityFun 657b2ddc8a feat: Optimizing the process management page frontend is feasible. (#10455)
Refs https://github.com/1Panel-dev/1Panel/issues/4634
2025-09-23 09:20:06 +00:00
ssongliu 50221c2ace fix: Optimize the input description for tamper prevention (#10454)
Refs #9203
2025-09-23 09:18:07 +00:00
CityFun b0bf3be707 feat: change website rate limit prompt (#10450)
Refs https://github.com/1Panel-dev/1Panel/issues/10441
2025-09-23 09:16:07 +00:00
ssongliu caaf05897a fix: Optimize error messages for failed extraction with passwords (#10453) 2025-09-23 17:15:55 +08:00
CityFun 0354b11ddb feat: handle mcp for offline mode (#10451) 2025-09-23 07:52:05 +00:00
CityFun bdcf9b075e feat: Fixed issue with Edit App - Resource limit prompt message is missing (#10449)
Refs https://github.com/1Panel-dev/1Panel/issues/10431
2025-09-23 05:54:04 +00:00
CityFun ab77aa2b5b feat: Adapt version handle for offline mode. (#10448) 2025-09-23 04:00:05 +00:00
ssongliu 1d54dcc95c fix: Increase Docker restart timeout duration (#10447)
Refs #10440
2025-09-23 03:58:04 +00:00
ssongliu 0138d481a6 fix: Add timeout settings for clam scanning (#10445)
Refs #10426
2025-09-23 03:56:04 +00:00
ssongliu ba6cdc6e10 fix: Optimize the display of database synchronization passwords (#10437)
Refs #10410
2025-09-22 15:18:08 +00:00
CityFun 389d9c652f feat: Adapt script library for offline mode. (#10436) 2025-09-22 10:00:05 +00:00
ssongliu 4942c6c206 feat: Support import and export operations for quick commands (#10434)
Refs #7384
2025-09-22 09:20:04 +00:00
CityFun 40e264e0d4 fix: Fixed issue with backup mysql-cluster failed (#10433) 2025-09-22 08:02:03 +00:00
CityFun b73b851245 feat: Remove unused front-end components (#10432) 2025-09-22 08:00:07 +00:00
CityFun 49a777e2d8 feat: Optimize local application installation name settings. (#10430)
Refs https://github.com/1Panel-dev/1Panel/issues/10406
2025-09-22 07:58:04 +00:00
wanghe-fit2cloud 7df43aeb59 feat: Add offline switch 2025-09-22 14:23:09 +08:00
CityFun 86047c48e4 feat: add timeout limit function Config for PHP (#10429) 2025-09-22 11:31:33 +08:00
ssongliu 6000a206a8 feat: Supports task logs for tamper prevention (#10428) 2025-09-22 03:30:03 +00:00
CityFun da75f050e2 feat: Create load balancer with health check. (#10419)
Refs https://github.com/1Panel-dev/1Panel/issues/10412
2025-09-19 10:18:04 +00:00
CityFun 7ed81996b8 feat: Optimize log file support for IPv6. (#10418)
Refs https://github.com/1Panel-dev/1Panel/issues/10411
2025-09-19 10:12:01 +00:00
CityFun 2040f8f3b0 feat: support batch operate website (#10415)
Refs https://github.com/1Panel-dev/1Panel/issues/5142
2025-09-19 10:10:01 +00:00
ssongliu c216726449 fix: Optimize the style of quick command options (#10408) 2025-09-19 10:08:01 +00:00
ssongliu a33b17d5da feat: Support database terminal operations (#10407)
Refs #9538
2025-09-18 10:08:03 +00:00
ssongliu 58c994a3e2 feat: Support quick commands in the file terminal (#10405)
Refs #9541
2025-09-18 07:06:37 +00:00
ssongliu 98d01556c2 fix: Fix the issue of SSH port failure on some machines (#10404)
Refs #4801
2025-09-18 07:04:38 +00:00
CityFun fd21e8de14 feat: optimization install php extensions (#10403) 2025-09-18 07:02:36 +00:00
蘭 37ef2059f1 feat: Improve Español language (#10400) 2025-09-17 14:29:59 +00:00
ssongliu 41c49ca95f fix: Fix the issue where SSH port changes are not synchronized to the… (#10398)
… local terminal

Refs #10358
2025-09-17 10:43:58 +00:00
CEO DaXyS 1999f102bf feat: Dev v2 spanish (#10352) 2025-09-17 18:02:29 +08:00
CityFun aa91bb88bc feat: Update element-plus version (#10397) 2025-09-17 09:53:59 +00:00
ssongliu 7410a41331 feat: Add alias settings for quick jump (#10396)
Refs #10390
2025-09-17 09:51:59 +00:00
CityFun a987370657 feat: Add support for offline version. (#10395) 2025-09-17 09:49:59 +00:00
ssongliu 1883b05f26 feat: Add panel carousel on the overview page (#10394) 2025-09-17 09:47:58 +00:00
CityFun c408619042 fix: Fixed issue with sync ssl to node failed (#10393)
Refs https://github.com/1Panel-dev/1Panel/issues/10237
2025-09-17 07:11:59 +00:00
ssongliu 1181b13c83 feat: Support adding panels (#10388) 2025-09-17 10:21:03 +08:00
CityFun c1aef03f09 perf: change php runtime website config (#10387)
Refs https://github.com/1Panel-dev/1Panel/issues/10334
2025-09-16 07:48:10 +00:00
CityFun 6609871b1e fix: Resolve the issue of application edit parameters becoming mandatory. (#10386)
Refs https://github.com/1Panel-dev/1Panel/issues/10381
2025-09-16 07:46:10 +00:00
CityFun 813f1718fb style: change app edit button style (#10385) 2025-09-16 07:44:09 +00:00
蘭 49bcc14df7 ref: Code formatting (#10383) 2025-09-16 02:35:57 +00:00
david082321 28108a1c02 feat: Update zh-Hant translation (#10380) 2025-09-15 18:27:09 +08:00
蘭 c169dfa395 fix: Fix the issue of generating Swagger documents with actual parameters (#10379) 2025-09-15 16:35:46 +08:00
ssongliu 3b26a692b6 feat: Support watermark (#10378) 2025-09-15 16:35:24 +08:00
ssongliu 9a6b51b092 feat: Add backup status for manual backups (#10377) 2025-09-15 16:27:11 +08:00
ssongliu c3121fcd38 feat: Add force stop functionality to tamper prevention (#10376)
Refs #6866
2025-09-15 08:24:45 +00:00
531 changed files with 34914 additions and 9169 deletions
+3 -2
View File
@@ -70,11 +70,12 @@ archives:
wrap_in_directory: true
files:
- 1pctl
- install.sh
- 1panel-core.service
- 1panel-agent.service
- install.sh
- GeoIP.mmdb
- initscript/*
- lang/*
- GeoIP.mmdb
checksum:
name_template: 'checksums.txt'
+4 -4
View File
@@ -30,19 +30,19 @@ build_frontend:
build_core_on_linux:
cd $(CORE_PATH) \
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
&& CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
build_agent_on_linux:
cd $(AGENT_PATH) \
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)
&& CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)
build_core_on_darwin:
cd $(CORE_PATH) \
&& GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
&& CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
build_agent_on_darwin:
cd $(AGENT_PATH) \
&& GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)
&& CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)
build_all: build_frontend build_core_on_linux build_agent_on_linux
+22
View File
@@ -28,6 +28,28 @@ func (b *BaseApi) CreateComposeTemplate(c *gin.Context) {
helper.Success(c)
}
// @Tags Container Compose-template
// @Summary Bacth compose template
// @Accept json
// @Param request body dto.ComposeTemplateBatch true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/template/batch [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"批量导入编排模版","formatEN":"batch import compose templates"}
func (b *BaseApi) BatchComposeTemplate(c *gin.Context) {
var req dto.ComposeTemplateBatch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := composeTemplateService.Batch(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Container Compose-template
// @Summary Page compose templates
// @Accept json
+1 -1
View File
@@ -314,7 +314,7 @@ func (b *BaseApi) ContainerCreateByCommand(c *gin.Context) {
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/upgrade [post]
// @x-panel-log {"bodyKeys":["name","image"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新容器镜像 [name][image]","formatEN":"upgrade container image [name][image]"}
// @x-panel-log {"bodyKeys":["names","image"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新容器镜像 [names][image]","formatEN":"upgrade container image [names][image]"}
func (b *BaseApi) ContainerUpgrade(c *gin.Context) {
var req dto.ContainerUpgrade
if err := helper.CheckBindAndValidate(&req, c); err != nil {
+23 -1
View File
@@ -211,7 +211,7 @@ func (b *BaseApi) LoadRecordLog(c *gin.Context) {
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /cronjobs/records/clean [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":"clean cronjob [name] records"}
// @x-panel-log {"bodyKeys":["cronjobID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"cronjobID","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"清空计划任务记录 [name]","formatEN":"clean cronjob [name] records"}
func (b *BaseApi) CleanRecord(c *gin.Context) {
var req dto.CronjobClean
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -226,6 +226,28 @@ func (b *BaseApi) CleanRecord(c *gin.Context) {
helper.Success(c)
}
// @Tags Cronjob
// @Summary Handle stop job
// @Accept json
// @Param request body dto.OperateByID true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /cronjobs/stop [post]
func (b *BaseApi) StopCronJob(c *gin.Context) {
var req dto.OperateByID
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := cronjobService.HandleStop(req.ID); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Cronjob
// @Summary Delete cronjob
// @Accept json
+4 -2
View File
@@ -17,8 +17,9 @@ var (
appInstallService = service.NewIAppInstalledService()
appIgnoreUpgradeService = service.NewIAppIgnoreUpgradeService()
aiToolService = service.NewIAIToolService()
mcpServerService = service.NewIMcpServerService()
aiToolService = service.NewIAIToolService()
mcpServerService = service.NewIMcpServerService()
tensorrtLLMService = service.NewITensorRTLLMService()
containerService = service.NewIContainerService()
composeTemplateService = service.NewIComposeTemplateService()
@@ -37,6 +38,7 @@ var (
fileService = service.NewIFileService()
sshService = service.NewISSHService()
firewallService = service.NewIFirewallService()
iptablesService = service.NewIIptablesService()
monitorService = service.NewIMonitorService()
systemService = service.NewISystemService()
+41
View File
@@ -901,3 +901,44 @@ func (b *BaseApi) GetUsersAndGroups(c *gin.Context) {
}
helper.SuccessWithData(c, res)
}
// @Tags File
// @Summary Convert file
// @Accept json
// @Param request body request.FileConvert true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/convert [post]
func (b *BaseApi) ConvertFile(c *gin.Context) {
var req request.FileConvertRequest
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
fileService.Convert(req)
helper.SuccessWithData(c, nil)
}
// @Tags File
// @Summary Convert file
// @Accept json
// @Param request body dto.PageInfo true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/convert/log [post]
func (b *BaseApi) ConvertLog(c *gin.Context) {
var req dto.PageInfo
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, logs, err := fileService.ConvertLog(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: logs,
Total: total,
})
}
+124 -8
View File
@@ -8,12 +8,19 @@ import (
// @Tags Firewall
// @Summary Load firewall base info
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200 {object} dto.FirewallBaseInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/base [get]
// @Router /hosts/firewall/base [post]
func (b *BaseApi) LoadFirewallBaseInfo(c *gin.Context) {
data, err := firewallService.LoadBaseInfo()
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := firewallService.LoadBaseInfo(req.Name)
if err != nil {
helper.InternalServer(c, err)
return
@@ -95,14 +102,14 @@ func (b *BaseApi) OperatePortRule(c *gin.Context) {
// OperateForwardRule
// @Tags Firewall
// @Summary Create group
// @Summary Operate forward rule
// @Accept json
// @Param request body dto.ForwardRuleOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/forward [post]
// @x-panel-log {"bodyKeys":["source_port"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新端口转发规则 [source_port]","formatEN":"update port forward rules [source_port]"}
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新端口转发规则","formatEN":"update port forward rules"}
func (b *BaseApi) OperateForwardRule(c *gin.Context) {
var req dto.ForwardRuleOperate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -117,7 +124,7 @@ func (b *BaseApi) OperateForwardRule(c *gin.Context) {
}
// @Tags Firewall
// @Summary Create group
// @Summary Operate Ip rule
// @Accept json
// @Param request body dto.AddrRuleOperate true "request"
// @Success 200
@@ -139,7 +146,7 @@ func (b *BaseApi) OperateIPRule(c *gin.Context) {
}
// @Tags Firewall
// @Summary Create group
// @Summary Batch operate rule
// @Accept json
// @Param request body dto.BatchRuleOperate true "request"
// @Success 200
@@ -181,7 +188,7 @@ func (b *BaseApi) UpdateFirewallDescription(c *gin.Context) {
}
// @Tags Firewall
// @Summary Create group
// @Summary Update port rule
// @Accept json
// @Param request body dto.PortRuleUpdate true "request"
// @Success 200
@@ -202,7 +209,7 @@ func (b *BaseApi) UpdatePortRule(c *gin.Context) {
}
// @Tags Firewall
// @Summary Create group
// @Summary Update Ip rule
// @Accept json
// @Param request body dto.AddrRuleUpdate true "request"
// @Success 200
@@ -221,3 +228,112 @@ func (b *BaseApi) UpdateAddrRule(c *gin.Context) {
}
helper.Success(c)
}
// @Tags Firewall
// @Summary search iptables filter rules
// @Accept json
// @Param request body dto.SearchPageWithType true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/filter/search [post]
func (b *BaseApi) SearchFilterRules(c *gin.Context) {
var req dto.SearchPageWithType
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := iptablesService.Search(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags Firewall
// @Summary Operate iptables filter rule
// @Accept json
// @Param request body dto.IptablesRuleOp true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/filter/rule/operate [post]
// @x-panel-log {"bodyKeys":["operation","chain"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] filter规则到 [chain]","formatEN":"[operation] filter rule to [chain]"}
func (b *BaseApi) OperateFilterRule(c *gin.Context) {
var req dto.IptablesRuleOp
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := iptablesService.OperateRule(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Firewall
// @Summary Batch operate iptables filter rules
// @Accept json
// @Param request body dto.IptablesBatchOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/filter/rule/batch [post]
func (b *BaseApi) BatchOperateFilterRule(c *gin.Context) {
var req dto.IptablesBatchOperate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := iptablesService.BatchOperate(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Firewall
// @Summary Apply/Unload/Init iptables filter
// @Accept json
// @Param request body dto.IptablesOp true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/filter/operate [post]
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] iptables filter 防火墙","formatEN":"[operate] iptables filter firewall"}
func (b *BaseApi) OperateFilterChain(c *gin.Context) {
var req dto.IptablesOp
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := iptablesService.Operate(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Firewall
// @Summary load chain status with name
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/firewall/filter/chain/status [post]
func (b *BaseApi) LoadChainStatus(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
helper.SuccessWithData(c, iptablesService.LoadChainStatus(req))
}
-22
View File
@@ -95,28 +95,6 @@ func (b *BaseApi) OperateToolConfig(c *gin.Context) {
helper.SuccessWithData(c, config)
}
// @Tags Host tool
// @Summary Get tool logs
// @Accept json
// @Param request body request.HostToolLogReq true "request"
// @Success 200 {string} logContent
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/tool/log [post]
func (b *BaseApi) GetToolLog(c *gin.Context) {
var req request.HostToolLogReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
logContent, err := hostToolService.GetToolLog(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, logContent)
}
// @Tags Host tool
// @Summary Create Supervisor process
// @Accept json
+2 -2
View File
@@ -161,13 +161,13 @@ func (b *BaseApi) GetNginxModules(c *gin.Context) {
// @Tags OpenResty
// @Summary Operate default HTTPs
// @Accept json
// @Param request body request.NginxOperateReq true "request"
// @Param request body request.NginxDefaultHTTPSUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /openresty/https [post]
func (b *BaseApi) OperateDefaultHTTPs(c *gin.Context) {
var req request.NginxOperateReq
var req request.NginxDefaultHTTPSUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
+1 -1
View File
@@ -40,7 +40,7 @@ func (b *BaseApi) StopProcess(c *gin.Context) {
// @Tags Process
// @Summary Get Process Info By PID
// @Param pid path int true "PID"
// @Success 200 {object} websocket.PsProcessData
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /process/{pid} [get]
+1 -21
View File
@@ -1,7 +1,6 @@
package v2
import (
"encoding/base64"
"encoding/json"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
@@ -77,26 +76,7 @@ func (b *BaseApi) LoadBaseDir(c *gin.Context) {
// @Security Timestamp
// @Router /settings/ssh/conn [get]
func (b *BaseApi) LoadLocalConn(c *gin.Context) {
connInfoInDB := settingService.GetSettingByKey("LocalSSHConn")
if len(connInfoInDB) == 0 {
helper.Success(c)
return
}
var data dto.SSHConnData
if err := json.Unmarshal([]byte(connInfoInDB), &data); err != nil {
helper.Success(c)
return
}
if len(data.Password) != 0 {
data.Password = base64.StdEncoding.EncodeToString([]byte(data.Password))
}
if len(data.PrivateKey) != 0 {
data.PrivateKey = base64.StdEncoding.EncodeToString([]byte(data.PrivateKey))
}
if len(data.PassPhrase) != 0 {
data.PassPhrase = base64.StdEncoding.EncodeToString([]byte(data.PassPhrase))
}
helper.SuccessWithData(c, data)
helper.SuccessWithData(c, settingService.GetLocalConn())
}
func (b *BaseApi) CheckLocalConn(c *gin.Context) {
+76 -42
View File
@@ -53,7 +53,7 @@ func (b *BaseApi) OperateSSH(c *gin.Context) {
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/update [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置 [key] => [value]","formatEN":"update SSH setting [key] => [value]"}
// @x-panel-log {"bodyKeys":["key","newValue"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置 [key] => [newValue]","formatEN":"update SSH setting [key] => [newValue]"}
func (b *BaseApi) UpdateSSH(c *gin.Context) {
var req dto.SSHUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -68,21 +68,23 @@ func (b *BaseApi) UpdateSSH(c *gin.Context) {
}
// @Tags SSH
// @Summary Update host SSH setting by file
// @Summary Generate host SSH secret
// @Accept json
// @Param request body dto.SSHConf true "request"
// @Param request body dto.RootCertOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/conffile/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置文件","formatEN":"update SSH conf"}
func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
var req dto.SSHConf
// @Router /hosts/ssh/cert [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
func (b *BaseApi) CreateRootCert(c *gin.Context) {
var req dto.RootCertOperate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := sshService.UpdateByFile(req.File); err != nil {
if err := loadCertAfterDecrypt(&req); err != nil {
helper.BadRequest(c, err)
}
if err := sshService.CreateRootCert(req); err != nil {
helper.InternalServer(c, err)
return
}
@@ -90,45 +92,23 @@ func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
}
// @Tags SSH
// @Summary Generate host SSH secret
// @Summary Update host SSH secret
// @Accept json
// @Param request body dto.CreateRootCert true "request"
// @Param request body dto.RootCertOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/cert [post]
// @Router /hosts/ssh/cert/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
func (b *BaseApi) CreateRootCert(c *gin.Context) {
var req dto.CreateRootCert
func (b *BaseApi) EditRootCert(c *gin.Context) {
var req dto.RootCertOperate
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 err := loadCertAfterDecrypt(&req); err != nil {
helper.BadRequest(c, err)
}
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.CreateRootCert(req); err != nil {
if err := sshService.EditRootCert(req); err != nil {
helper.InternalServer(c, err)
return
}
@@ -246,15 +226,69 @@ func (b *BaseApi) ExportSSHLogs(c *gin.Context) {
// @Tags SSH
// @Summary Load host SSH conf
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200 {string} conf
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/conf [get]
func (b *BaseApi) LoadSSHConf(c *gin.Context) {
data, err := sshService.LoadSSHConf()
// @Router /hosts/ssh/file [post]
func (b *BaseApi) LoadSSHFile(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := sshService.LoadSSHFile(req.Name)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags SSH
// @Summary Update host SSH setting by file
// @Accept json
// @Param request body dto.SSHConf true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/ssh/file/update [post]
// @x-panel-log {"bodyKeys":["key"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置文件 [key]","formatEN":"update SSH conf [key]"}
func (b *BaseApi) UpdateSSHByFile(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := sshService.UpdateByFile(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
func loadCertAfterDecrypt(req *dto.RootCertOperate) error {
if len(req.PassPhrase) != 0 {
passPhrase, err := base64.StdEncoding.DecodeString(req.PassPhrase)
if err != nil {
return err
}
req.PassPhrase = string(passPhrase)
}
if len(req.PrivateKey) != 0 {
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
if err != nil {
return err
}
req.PrivateKey = string(privateKey)
}
if len(req.PublicKey) != 0 {
publicKey, err := base64.StdEncoding.DecodeString(req.PublicKey)
if err != nil {
return err
}
req.PublicKey = string(publicKey)
}
return nil
}
+68
View File
@@ -0,0 +1,68 @@
package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/gin-gonic/gin"
)
func (b *BaseApi) PageTensorRTLLMs(c *gin.Context) {
var req request.TensorRTLLMSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list := tensorrtLLMService.Page(req)
helper.SuccessWithData(c, list)
}
func (b *BaseApi) CreateTensorRTLLM(c *gin.Context) {
var req request.TensorRTLLMCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := tensorrtLLMService.Create(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
func (b *BaseApi) UpdateTensorRTLLM(c *gin.Context) {
var req request.TensorRTLLMUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := tensorrtLLMService.Update(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
func (b *BaseApi) DeleteTensorRTLLM(c *gin.Context) {
var req request.TensorRTLLMDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := tensorrtLLMService.Delete(req.ID)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
func (b *BaseApi) OperateTensorRTLLM(c *gin.Context) {
var req request.TensorRTLLMOperate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := tensorrtLLMService.Operate(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
+40 -55
View File
@@ -6,7 +6,6 @@ import (
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
@@ -86,15 +85,16 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
return
}
source := c.Query("source")
var containerID string
var initCmd []string
switch source {
case "redis", "redis-cluster":
containerID, initCmd, err = loadRedisInitCmd(c, source)
initCmd, err = loadRedisInitCmd(c, source)
case "ollama":
containerID, initCmd, err = loadOllamaInitCmd(c)
initCmd, err = loadOllamaInitCmd(c)
case "container":
containerID, initCmd, err = loadContainerInitCmd(c)
initCmd, err = loadContainerInitCmd(c)
case "database":
initCmd, err = loadDatabaseInitCmd(c)
default:
if wshandleError(wsConn, fmt.Errorf("not support such source %s", source)) {
return
@@ -103,12 +103,10 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
if wshandleError(wsConn, err) {
return
}
pidMap := loadMapFromDockerTop(containerID)
slave, err := terminal.NewCommand("docker", initCmd...)
if wshandleError(wsConn, err) {
return
}
defer killBash(containerID, strings.ReplaceAll(strings.Join(initCmd, " "), fmt.Sprintf("exec -it %s ", containerID), ""), pidMap)
defer slave.Close()
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
@@ -127,18 +125,18 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
}
func loadRedisInitCmd(c *gin.Context, redisType string) (string, []string, error) {
func loadRedisInitCmd(c *gin.Context, redisType string) ([]string, error) {
name := c.Query("name")
from := c.Query("from")
commands := []string{"exec", "-it"}
database, err := databaseService.Get(name)
if err != nil {
return "", nil, fmt.Errorf("no such database in db, err: %v", err)
return nil, fmt.Errorf("no such database in db, err: %v", err)
}
if from == "local" {
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: redisType})
if err != nil {
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
return nil, fmt.Errorf("no such app in db, err: %v", err)
}
name = redisInfo.ContainerName
commands = append(commands, []string{name, "redis-cli"}...)
@@ -152,38 +150,61 @@ func loadRedisInitCmd(c *gin.Context, redisType string) (string, []string, error
commands = append(commands, []string{"-a", database.Password, "--no-auth-warning"}...)
}
}
return name, commands, nil
return commands, nil
}
func loadOllamaInitCmd(c *gin.Context) (string, []string, error) {
func loadOllamaInitCmd(c *gin.Context) ([]string, error) {
name := c.Query("name")
if cmd.CheckIllegal(name) {
return "", nil, fmt.Errorf("ollama model %s contains illegal characters", name)
return nil, fmt.Errorf("ollama model %s contains illegal characters", name)
}
ollamaInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: "", Type: "ollama"})
if err != nil {
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
return nil, fmt.Errorf("no such app in db, err: %v", err)
}
containerName := ollamaInfo.ContainerName
return containerName, []string{"exec", "-it", containerName, "ollama", "run", name}, nil
return []string{"exec", "-it", containerName, "ollama", "run", name}, nil
}
func loadContainerInitCmd(c *gin.Context) (string, []string, error) {
func loadContainerInitCmd(c *gin.Context) ([]string, error) {
containerID := c.Query("containerid")
command := c.Query("command")
user := c.Query("user")
if cmd.CheckIllegal(user, containerID, command) {
return "", nil, fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
return nil, fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
}
if len(command) == 0 || len(containerID) == 0 {
return "", nil, fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
return nil, fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
}
commands := []string{"exec", "-it", containerID, command}
if len(user) != 0 {
commands = []string{"exec", "-it", "-u", user, containerID, command}
}
return containerID, commands, nil
return commands, nil
}
func loadDatabaseInitCmd(c *gin.Context) ([]string, error) {
database := c.Query("database")
databaseType := c.Query("databaseType")
if len(database) == 0 || len(databaseType) == 0 {
return nil, fmt.Errorf("error param of database: %s or database type: %s", database, databaseType)
}
databaseConn, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Type: databaseType, Name: database})
if err != nil {
return nil, fmt.Errorf("no such database in db, err: %v", err)
}
commands := []string{"exec", "-it", databaseConn.ContainerName}
switch databaseType {
case "mysql", "mysql-cluster":
commands = append(commands, []string{"mysql", "-uroot", "-p" + databaseConn.Password}...)
case "mariadb":
commands = append(commands, []string{"mariadb", "-uroot", "-p" + databaseConn.Password}...)
case "postgresql", "postgresql-cluster":
commands = []string{"exec", "-e", fmt.Sprintf("PGPASSWORD=%s", databaseConn.Password), "-it", databaseConn.ContainerName, "psql", "-t", "-U", databaseConn.Username}
}
return commands, nil
}
func wshandleError(ws *websocket.Conn, err error) bool {
@@ -206,42 +227,6 @@ func wshandleError(ws *websocket.Conn, err error) bool {
return false
}
func loadMapFromDockerTop(containerID string) map[string]string {
pidMap := make(map[string]string)
sudo := cmd.SudoHandleCmd()
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s docker top %s -eo pid,command ", sudo, containerID)
if err != nil {
return pidMap
}
lines := strings.Split(stdout, "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 2 {
continue
}
pidMap[parts[0]] = strings.Join(parts[1:], " ")
}
return pidMap
}
func killBash(containerID, comm string, pidMap map[string]string) {
sudo := cmd.SudoHandleCmd()
newPidMap := loadMapFromDockerTop(containerID)
for pid, command := range newPidMap {
isOld := false
for pid2 := range pidMap {
if pid == pid2 {
isOld = true
break
}
}
if !isOld && command == comm {
_, _ = cmd.RunDefaultWithStdoutBashCf("%s kill -9 %s", sudo, pid)
}
}
}
var upGrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024 * 1024 * 10,
+83 -1
View File
@@ -672,7 +672,7 @@ func (b *BaseApi) UpdateAntiLeech(c *gin.Context) {
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/redirect/update [post]
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 重定向理配置 ","formatEN":"Update domain [domain] redirect config"}
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 重定向配置 ","formatEN":"Update domain [domain] redirect config"}
func (b *BaseApi) UpdateRedirectConfig(c *gin.Context) {
var req request.NginxRedirectReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -1141,3 +1141,85 @@ func (b *BaseApi) ExecComposer(c *gin.Context) {
}
helper.Success(c)
}
// @Tags Website
// @Summary Batch operate websites
// @Accept json
// @Param request body request.BatchWebsiteOp true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/batch/operate [post]
func (b *BaseApi) BatchOpWebsites(c *gin.Context) {
var req request.BatchWebsiteOp
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.BatchOpWebsite(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Website
// @Summary Batch set website group
// @Accept json
// @Param request body request.BatchWebsiteGroup true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/batch/group [post]
func (b *BaseApi) BatchSetWebsiteGroup(c *gin.Context) {
var req request.BatchWebsiteGroup
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.BatchSetGroup(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Website
// @Summary Get CORS Config
// @Accept json
// @Param id path int true "id"
// @Success 200 {object} request.CorsConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/cors/{id} [get]
func (b *BaseApi) GetCORSConfig(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.BadRequest(c, err)
return
}
res, err := websiteService.GetCors(id)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Website
// @Summary Update CORS Config
// @Accept json
// @Param request body request.CorsConfigReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/cors/update [post]
func (b *BaseApi) UpdateCORSConfig(c *gin.Context) {
var req request.CorsConfigReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.UpdateCors(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
+74
View File
@@ -2,6 +2,8 @@ package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"io"
"mime/multipart"
"net/http"
"net/url"
"reflect"
@@ -217,6 +219,78 @@ func (b *BaseApi) UploadWebsiteSSL(c *gin.Context) {
helper.Success(c)
}
// @Tags Website SSL
// @Summary Upload SSL file
// @Accept multipart/form-data
// @Param type formData string true "type"
// @Param description formData string false "description"
// @Param sslID formData string false "sslID"
// @Param privateKeyFile formData file true "privateKeyFile"
// @Param certificateFile formData file true "certificateFile"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /websites/ssl/upload/file [post]
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传 ssl 文件 [type]","formatEN":"Upload ssl file [type]"}
func (b *BaseApi) UploadSSLFile(c *gin.Context) {
var req request.WebsiteSSLFileUpload
req.Description = c.PostForm("description")
sslID := c.PostForm("sslID")
if sslID != "" {
req.SSLID, _ = strconv.ParseUint(sslID, 10, 64)
}
privateKeyFile, err := c.FormFile("privateKeyFile")
if err != nil {
helper.InternalServer(c, err)
return
}
certificateFile, err := c.FormFile("certificateFile")
if err != nil {
helper.InternalServer(c, err)
return
}
privateKeyContent, err := readUploadedFile(privateKeyFile)
if err != nil {
helper.InternalServer(c, err)
return
}
certificateContent, err := readUploadedFile(certificateFile)
if err != nil {
helper.InternalServer(c, err)
return
}
uploadReq := request.WebsiteSSLUpload{
Type: "paste",
PrivateKey: string(privateKeyContent),
Certificate: string(certificateContent),
Description: req.Description,
SSLID: uint(req.SSLID),
}
if err := websiteSSLService.Upload(uploadReq); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
func readUploadedFile(fileHeader *multipart.FileHeader) ([]byte, error) {
file, err := fileHeader.Open()
if err != nil {
return nil, err
}
defer file.Close()
return io.ReadAll(file)
}
// @Tags Website SSL
// @Summary Download SSL file
// @Accept json
+3 -1
View File
@@ -292,6 +292,7 @@ type AlertConfigTest struct {
Host string `json:"host"`
Port int `json:"port"`
Sender string `json:"sender"`
UserName string `json:"userName"`
Password string `json:"password"`
DisplayName string `json:"displayName"`
Encryption string `json:"encryption"` // "ssl" / "tls" / "none"
@@ -316,13 +317,14 @@ type AlertCommonConfig struct {
type AlertSmsConfig struct {
Phone string `json:"phone"`
AlertDailyNum uint `json:"alertDailyNum"`
AlertDailyNum string `json:"alertDailyNum"`
}
type AlertEmailConfig struct {
Host string `json:"host"`
Port int `json:"port"`
Sender string `json:"sender"`
UserName string `json:"userName"`
Password string `json:"password"`
DisplayName string `json:"displayName"`
Encryption string `json:"encryption"` // "ssl" / "tls" / "none"
+1
View File
@@ -128,6 +128,7 @@ type Locale struct {
Zh string `json:"zh"`
Ko string `json:"ko"`
Tr string `json:"tr"`
Es string `json:"es-es" yaml:"es-es"`
}
type AppForm struct {
+3
View File
@@ -99,6 +99,9 @@ type BackupRecords struct {
DownloadAccountID uint `json:"downloadAccountID"`
FileDir string `json:"fileDir"`
FileName string `json:"fileName"`
TaskID string `json:"taskID"`
Status string `json:"status"`
Message string `json:"message"`
Description string `json:"description"`
}
+3
View File
@@ -33,6 +33,7 @@ type ClamInfo struct {
LastRecordStatus string `json:"lastRecordStatus"`
LastRecordTime string `json:"lastRecordTime"`
Spec string `json:"spec"`
Timeout uint `json:"timeout"`
Description string `json:"description"`
AlertCount uint `json:"alertCount"`
AlertMethod string `json:"alertMethod"`
@@ -77,6 +78,7 @@ type ClamCreate struct {
InfectedStrategy string `json:"infectedStrategy"`
InfectedDir string `json:"infectedDir"`
Spec string `json:"spec"`
Timeout uint `json:"timeout"`
Description string `json:"description"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
@@ -91,6 +93,7 @@ type ClamUpdate struct {
InfectedStrategy string `json:"infectedStrategy"`
InfectedDir string `json:"infectedDir"`
Spec string `json:"spec"`
Timeout uint `json:"timeout"`
Description string `json:"description"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
+4
View File
@@ -8,6 +8,10 @@ type ComposeTemplateCreate struct {
Content string `json:"content"`
}
type ComposeTemplateBatch struct {
Templates []ComposeTemplateCreate `json:"templates" validate:"required"`
}
type ComposeTemplateUpdate struct {
ID uint `json:"id"`
Description string `json:"description"`
+20 -10
View File
@@ -31,6 +31,9 @@ type ContainerInfo struct {
Network []string `json:"network"`
Ports []string `json:"ports"`
SizeRw int64 `json:"sizeRw"`
SizeRootFs int64 `json:"sizeRootFs"`
IsFromApp bool `json:"isFromApp"`
IsFromCompose bool `json:"isFromCompose"`
@@ -45,14 +48,13 @@ type ContainerOptions struct {
}
type ContainerStatus struct {
All uint `json:"all"`
Created uint `json:"created"`
Running uint `json:"running"`
Paused uint `json:"paused"`
Restarting uint `json:"restarting"`
Removing uint `json:"removing"`
Exited uint `json:"exited"`
Dead uint `json:"dead"`
Created int `json:"created"`
Running int `json:"running"`
Paused int `json:"paused"`
Restarting int `json:"restarting"`
Removing int `json:"removing"`
Exited int `json:"exited"`
Dead int `json:"dead"`
ContainerCount int `json:"containerCount"`
ComposeCount int `json:"composeCount"`
@@ -62,7 +64,14 @@ type ContainerStatus struct {
VolumeCount int `json:"volumeCount"`
RepoCount int `json:"repoCount"`
ImageSize uint64 `json:"imageSize"`
ContainerUsage int64 `json:"containerUsage"`
ContainerReclaimable int64 `json:"containerReclaimable"`
ImageUsage int64 `json:"imageUsage"`
ImageReclaimable int64 `json:"imageReclaimable"`
VolumeUsage int64 `json:"volumeUsage"`
VolumeReclaimable int64 `json:"volumeReclaimable"`
BuildCacheUsage int64 `json:"buildCacheUsage"`
BuildCacheReclaimable int64 `json:"buildCacheReclaimable"`
}
type ResourceLimit struct {
CPU int `json:"cpu"`
@@ -71,7 +80,6 @@ type ResourceLimit struct {
type ContainerOperate struct {
TaskID string `json:"taskID"`
ContainerID string `json:"containerID"`
ForcePull bool `json:"forcePull"`
Name string `json:"name" validate:"required"`
Image string `json:"image" validate:"required"`
@@ -144,6 +152,7 @@ type VolumeHelper struct {
SourceDir string `json:"sourceDir"`
ContainerDir string `json:"containerDir"`
Mode string `json:"mode"`
Shared string `json:"shared"`
}
type PortHelper struct {
HostIP string `json:"hostIP"`
@@ -153,6 +162,7 @@ type PortHelper struct {
}
type ContainerOperation struct {
TaskID string `json:"taskID"`
Names []string `json:"names" validate:"required"`
Operation string `json:"operation" validate:"required,oneof=up start stop restart kill pause unpause remove"`
}
+6
View File
@@ -53,6 +53,12 @@ type CronjobOperate struct {
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
AlertMethod string `json:"alertMethod"`
CleanLogConfig
}
type CleanLogConfig struct {
Scopes []string `json:"scopes"`
}
type SnapshotRule struct {
+4
View File
@@ -34,6 +34,7 @@ type ChangeQuicks struct {
type QuickJump struct {
ID uint `json:"id"`
Name string `json:"name"`
Alias string `json:"alias"`
Title string `json:"title"`
Detail string `json:"detail"`
Recommend int `json:"recommend"`
@@ -115,6 +116,9 @@ type DashboardCurrent struct {
GPUData []GPUInfo `json:"gpuData"`
XPUData []XPUInfo `json:"xpuData"`
TopCPUItems []Process `json:"topCPUItems"`
TopMemItems []Process `json:"topMemItems"`
ShotTime time.Time `json:"shotTime"`
}
+9 -8
View File
@@ -281,14 +281,15 @@ type DatabaseItem struct {
}
type DatabaseCreate struct {
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password"`
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
InitialDB string `json:"initialDB"`
Username string `json:"username" validate:"required"`
Password string `json:"password"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
+2 -1
View File
@@ -27,7 +27,8 @@ type SwapHelper struct {
Size uint64 `json:"size"`
Used string `json:"used"`
IsNew bool `json:"isNew"`
IsNew bool `json:"isNew"`
TaskID string `json:"taskID"`
}
type TimeZoneOptions struct {
+38 -2
View File
@@ -4,6 +4,8 @@ type FirewallBaseInfo struct {
Name string `json:"name"`
IsExist bool `json:"isExist"`
IsActive bool `json:"isActive"`
IsInit bool `json:"isInit"`
IsBind bool `json:"isBind"`
Version string `json:"version"`
PingStatus string `json:"pingStatus"`
}
@@ -22,7 +24,9 @@ type FirewallOperation struct {
}
type PortRuleOperate struct {
ID uint `json:"id"`
Operation string `json:"operation" validate:"required,oneof=add remove"`
Chain string `json:"chain"`
Address string `json:"address"`
Port string `json:"port" validate:"required"`
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
@@ -37,6 +41,7 @@ type ForwardRuleOperate struct {
Operation string `json:"operation" validate:"required,oneof=add remove"`
Num string `json:"num"`
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
Interface string `json:"interface"`
Port string `json:"port" validate:"required"`
TargetIP string `json:"targetIP"`
TargetPort string `json:"targetPort" validate:"required"`
@@ -45,8 +50,11 @@ type ForwardRuleOperate struct {
type UpdateFirewallDescription struct {
Type string `json:"type"`
Address string `json:"address"`
Port string `json:"port"`
Chain string `json:"chain"`
SrcIP string `json:"srcIP"`
DstIP string `json:"dstIP"`
SrcPort string `json:"srcPort"`
DstPort string `json:"dstPort"`
Protocol string `json:"protocol"`
Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
@@ -54,6 +62,7 @@ type UpdateFirewallDescription struct {
}
type AddrRuleOperate struct {
ID uint `json:"id"`
Operation string `json:"operation" validate:"required,oneof=add remove"`
Address string `json:"address" validate:"required"`
Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
@@ -75,3 +84,30 @@ type BatchRuleOperate struct {
Type string `json:"type" validate:"required"`
Rules []PortRuleOperate `json:"rules"`
}
type IptablesOp struct {
Name string `json:"name" validate:"required,oneof=1PANEL_INPUT 1PANEL_OUTPUT 1PANEL_BASIC"`
Operate string `json:"operate" validate:"required,oneof=init-base init-forward init-advance bind-base unbind-base bind unbind"`
}
type IptablesRuleOp struct {
Operation string `json:"operation" validate:"required,oneof=add remove"`
ID uint `json:"id"`
Chain string `json:"chain" validate:"required,oneof=1PANEL_BASIC 1PANEL_BASIC_BEFORE 1PANEL_INPUT 1PANEL_OUTPUT"`
Protocol string `json:"protocol"`
SrcIP string `json:"srcIP"`
SrcPort uint `json:"srcPort"`
DstIP string `json:"dstIP"`
DstPort uint `json:"dstPort"`
Strategy string `json:"strategy" validate:"required,oneof=accept drop reject"`
Description string `json:"description"`
}
type IptablesBatchOperate struct {
Rules []IptablesRuleOp `json:"rules"`
}
type IptablesChainStatus struct {
IsBind bool `json:"isBind"`
DefaultStrategy string `json:"defaultStrategy"`
}
+9 -6
View File
@@ -18,7 +18,8 @@ type ImageInfo struct {
}
type ImageLoad struct {
Path string `json:"path" validate:"required"`
TaskID string `json:"taskID"`
Path string `json:"path" validate:"required"`
}
type ImageBuild struct {
@@ -27,17 +28,18 @@ type ImageBuild struct {
Name string `json:"name" validate:"required"`
Dockerfile string `json:"dockerfile" validate:"required"`
Tags []string `json:"tags"`
Args []string `json:"args"`
}
type ImagePull struct {
TaskID string `json:"taskID"`
RepoID uint `json:"repoID"`
ImageName string `json:"imageName" validate:"required"`
TaskID string `json:"taskID"`
RepoID uint `json:"repoID"`
ImageName []string `json:"imageName" validate:"required"`
}
type ImageTag struct {
SourceID string `json:"sourceID" validate:"required"`
TargetName string `json:"targetName" validate:"required"`
SourceID string `json:"sourceID" validate:"required"`
Tags []string `json:"tags" validate:"required"`
}
type ImagePush struct {
@@ -48,6 +50,7 @@ type ImagePush struct {
}
type ImageSave struct {
TaskID string `json:"taskID"`
TagName string `json:"tagName" validate:"required"`
Path string `json:"path" validate:"required"`
Name string `json:"name" validate:"required"`
+13 -2
View File
@@ -4,7 +4,8 @@ import "time"
type MonitorSearch struct {
Param string `json:"param" validate:"required,oneof=all cpu memory load io network"`
Info string `json:"info"`
IO string `json:"io"`
Network string `json:"network"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
}
@@ -15,14 +16,24 @@ type MonitorData struct {
Value []interface{} `json:"value"`
}
type Process struct {
Name string `json:"name"`
Pid int32 `json:"pid"`
Percent float64 `json:"percent"`
Memory uint64 `json:"memory"`
Cmd string `json:"cmd"`
User string `json:"user"`
}
type MonitorSetting struct {
MonitorStatus string `json:"monitorStatus"`
MonitorStoreDays string `json:"monitorStoreDays"`
MonitorInterval string `json:"monitorInterval"`
DefaultNetwork string `json:"defaultNetwork"`
DefaultIO string `json:"defaultIO"`
}
type MonitorSettingUpdate struct {
Key string `json:"key" validate:"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval DefaultNetwork"`
Key string `json:"key" validate:"required,oneof=MonitorStatus MonitorStoreDays MonitorInterval DefaultNetwork DefaultIO"`
Value string `json:"value"`
}
+7 -6
View File
@@ -73,12 +73,13 @@ type NginxUpstream struct {
}
type NginxUpstreamServer struct {
Server string `json:"server"`
Weight int `json:"weight"`
FailTimeout string `json:"failTimeout"`
MaxFails int `json:"maxFails"`
MaxConns int `json:"maxConns"`
Flag string `json:"flag"`
Server string `json:"server"`
Weight int `json:"weight"`
FailTimeout int `json:"failTimeout"`
FailTimeoutUnit string `json:"failTimeoutUnit"`
MaxFails int `json:"maxFails"`
MaxConns int `json:"maxConns"`
Flag string `json:"flag"`
}
var LBAlgorithms = map[string]struct{}{"ip_hash": {}, "least_conn": {}}
+16
View File
@@ -148,3 +148,19 @@ type FileExistReq struct {
Name string `json:"name" validate:"required"`
Dir string `json:"dir" validate:"required"`
}
type FileConvert struct {
Path string `json:"path" validate:"required"`
Type string `json:"type" validate:"required"`
InputFile string `json:"inputFile" validate:"required"`
Extension string `json:"extension" validate:"required"`
OutputFormat string `json:"outputFormat" validate:"required"`
Status string `json:"status"`
}
type FileConvertRequest struct {
Files []FileConvert `json:"files" validate:"required"`
OutputPath string `json:"outputPath" validate:"required"`
DeleteSource bool `json:"deleteSource"`
TaskID string `json:"taskID"`
}
+1
View File
@@ -34,6 +34,7 @@ type SupervisorProcessConfig struct {
Numprocs string `json:"numprocs"`
AutoRestart string `json:"autoRestart"`
AutoStart string `json:"autoStart"`
Environment string `json:"environment"`
}
type SupervisorProcessFileReq struct {
+5
View File
@@ -130,3 +130,8 @@ type NginxModuleUpdate struct {
type NginxOperateReq struct {
Operate string `json:"operate" validate:"required,oneof=enable disable"`
}
type NginxDefaultHTTPSUpdate struct {
Operate string `json:"operate" validate:"required,oneof=enable disable"`
SSLRejectHandshake bool `json:"sslRejectHandshake"`
}
+8
View File
@@ -32,6 +32,7 @@ type NodeConfig struct {
ExposedPorts []ExposedPort `json:"exposedPorts"`
Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"`
ExtraHosts []ExtraHost `json:"extraHosts"`
}
type Environment struct {
@@ -49,6 +50,11 @@ type ExposedPort struct {
HostIP string `json:"hostIP"`
}
type ExtraHost struct {
Hostname string `json:"hostname"`
IP string `json:"ip"`
}
type RuntimeDelete struct {
ID uint `json:"id"`
ForceDelete bool `json:"forceDelete"`
@@ -100,6 +106,7 @@ type PHPConfigUpdate struct {
Scope string `json:"scope" validate:"required"`
DisableFunctions []string `json:"disableFunctions"`
UploadMaxSize string `json:"uploadMaxSize"`
MaxExecutionTime string `json:"maxExecutionTime"`
}
type PHPFileUpdate struct {
@@ -134,6 +141,7 @@ type PHPContainerConfig struct {
ExposedPorts []ExposedPort `json:"exposedPorts"`
Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"`
ExtraHosts []ExtraHost `json:"extraHosts"`
}
type RuntimeRemark struct {
+40
View File
@@ -0,0 +1,40 @@
package request
import "github.com/1Panel-dev/1Panel/agent/app/dto"
type TensorRTLLMSearch struct {
dto.PageInfo
Name string `json:"name"`
}
type TensorRTLLMCreate struct {
Name string `json:"name" validate:"required"`
ContainerName string `json:"containerName" validate:"required"`
Version string `json:"version" validate:"required"`
ModelDir string `json:"modelDir" validate:"required"`
Image string `json:"image" validate:"required"`
Command string `json:"command" validate:"required"`
ModelType string `json:"modelType"`
ModelSpeedup bool `json:"modelSpeedup"`
DockerConfig
}
type TensorRTLLMUpdate struct {
ID uint `json:"id" validate:"required"`
TensorRTLLMCreate
}
type TensorRTLLMDelete struct {
ID uint `json:"id" validate:"required"`
}
type TensorRTLLMOperate struct {
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
}
type DockerConfig struct {
ExposedPorts []ExposedPort `json:"exposedPorts"`
Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"`
}
+27
View File
@@ -95,6 +95,7 @@ type WebsiteDelete struct {
DeleteApp bool `json:"deleteApp"`
DeleteBackup bool `json:"deleteBackup"`
ForceDelete bool `json:"forceDelete"`
DeleteDB bool `json:"deleteDB"`
}
type WebsiteOp struct {
@@ -102,6 +103,17 @@ type WebsiteOp struct {
Operate string `json:"operate"`
}
type BatchWebsiteOp struct {
IDs []uint `json:"ids" validate:"required"`
Operate string `json:"operate" validate:"required"`
TaskID string `json:"taskID" validate:"required"`
}
type BatchWebsiteGroup struct {
IDs []uint `json:"ids" validate:"required"`
GroupID uint `json:"groupID" validate:"required"`
}
type WebsiteRedirectUpdate struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Key string `json:"key" validate:"required"`
@@ -222,6 +234,21 @@ type WebsiteProxyConfig struct {
Replaces map[string]string `json:"replaces"`
SNI bool `json:"sni"`
ProxySSLName string `json:"proxySSLName"`
CorsConfig
}
type CorsConfig struct {
Cors bool `json:"cors"`
AllowOrigins string `json:"allowOrigins"`
AllowMethods string `json:"allowMethods"`
AllowHeaders string `json:"allowHeaders"`
AllowCredentials bool `json:"allowCredentials"`
Preflight bool `json:"preflight"`
}
type CorsConfigReq struct {
WebsiteID uint `json:"websiteID" validate:"required"`
CorsConfig
}
type WebsiteProxyReq struct {
+6
View File
@@ -157,3 +157,9 @@ type WebsiteCAObtain struct {
type WebsiteCARenew struct {
SSLID uint `json:"SSLID" validate:"required"`
}
type WebsiteSSLFileUpload struct {
Type string `json:"type"`
Description string `json:"description"`
SSLID uint64 `json:"sslID"`
}
+7 -1
View File
@@ -128,6 +128,9 @@ type AppInstallDTO struct {
Favorite bool `json:"favorite"`
App AppDetail `json:"app"`
Container string `json:"container"`
IsEdit bool `json:"isEdit"`
LinkDB bool `json:"linkDB"`
ServiceName string `json:"serviceName"`
}
type AppInstallInfo struct {
@@ -139,6 +142,8 @@ type AppInstallInfo struct {
HttpPort int `json:"HttpPort"`
Container string `json:"container"`
ComposePath string `json:"composePath"`
AppKey string `json:"appKey"`
AppPorts []int `json:"appPorts"`
Env map[string]interface{} `json:"env"`
}
@@ -176,6 +181,7 @@ type AppParam struct {
}
type AppConfig struct {
Params []AppParam `json:"params"`
Params []AppParam `json:"params"`
RawCompose string `json:"rawCompose"`
request.AppContainerConfig
}
+1 -1
View File
@@ -24,7 +24,7 @@ type DiskBasicInfo struct {
type CompleteDiskInfo struct {
Disks []DiskInfo `json:"disks"`
UnpartitionedDisks []DiskBasicInfo `json:"unpartitionedDisks"`
SystemDisk *DiskInfo `json:"systemDisk"`
SystemDisks []DiskInfo `json:"systemDisks"`
TotalDisks int `json:"totalDisks"`
TotalCapacity int64 `json:"totalCapacity"`
}
+8
View File
@@ -73,3 +73,11 @@ type DepthDirSizeRes struct {
Path string `json:"path"`
Size int64 `json:"size"`
}
type FileConvertLog struct {
Date string `json:"date"`
Type string `json:"type"`
Log string `json:"log"`
Status string `json:"status"`
Message string `json:"message"`
}
+1
View File
@@ -32,6 +32,7 @@ type SupervisorProcessConfig struct {
Status []ProcessStatus `json:"status"`
AutoRestart string `json:"autoRestart"`
AutoStart string `json:"autoStart"`
Environment string `json:"environment"`
}
type ProcessStatus struct {
+2 -1
View File
@@ -82,5 +82,6 @@ type NginxBuildConfig struct {
}
type NginxConfigRes struct {
Https bool `json:"https"`
Https bool `json:"https"`
SSLRejectHandshake bool `json:"sslRejectHandshake"`
}
+1
View File
@@ -28,6 +28,7 @@ type RuntimeDTO struct {
ExposedPorts []request.ExposedPort `json:"exposedPorts"`
Environments []request.Environment `json:"environments"`
Volumes []request.Volume `json:"volumes"`
ExtraHosts []request.ExtraHost `json:"extraHosts"`
ContainerStatus string `json:"containerStatus"`
Container string `json:"container"`
Remark string `json:"remark"`
+25
View File
@@ -0,0 +1,25 @@
package response
import (
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/model"
)
type TensorRTLLMsRes struct {
Items []TensorRTLLMDTO `json:"items"`
Total int64 `json:"total"`
}
type TensorRTLLMDTO struct {
model.TensorRTLLM
Version string `json:"version"`
Model string `json:"model"`
Dir string `json:"dir"`
ModelDir string `json:"modelDir"`
Image string `json:"image"`
Command string `json:"command"`
ExposedPorts []request.ExposedPort `json:"exposedPorts"`
Environments []request.Environment `json:"environments"`
Volumes []request.Volume `json:"volumes"`
}
+11 -10
View File
@@ -59,16 +59,16 @@ type WebsiteNginxConfig struct {
}
type WebsiteHTTPS struct {
Enable bool `json:"enable"`
HttpConfig string `json:"httpConfig"`
SSL model.WebsiteSSL `json:"SSL"`
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"`
Enable bool `json:"enable"`
HttpConfig string `json:"httpConfig"`
SSL model.WebsiteSSL `json:"SSL"`
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"`
}
type WebsiteLog struct {
@@ -82,6 +82,7 @@ type PHPConfig struct {
Params map[string]string `json:"params"`
DisableFunctions []string `json:"disableFunctions"`
UploadMaxSize string `json:"uploadMaxSize"`
MaxExecutionTime string `json:"maxExecutionTime"`
}
type NginxRewriteRes struct {
+3
View File
@@ -10,6 +10,7 @@ type SettingInfo struct {
NtpSite string `json:"ntpSite"`
DefaultNetwork string `json:"defaultNetwork"`
DefaultIO string `json:"defaultIO"`
LastCleanTime string `json:"lastCleanTime"`
LastCleanSize string `json:"lastCleanSize"`
LastCleanData string `json:"lastCleanData"`
@@ -69,6 +70,8 @@ type SSHConnData struct {
Password string `json:"password"`
PrivateKey string `json:"privateKey"`
PassPhrase string `json:"passPhrase"`
LocalSSHConnShow string `json:"localSSHConnShow"`
}
type SystemProxy struct {
+2 -1
View File
@@ -22,7 +22,8 @@ type SSHInfo struct {
CurrentUser string `json:"currentUser"`
}
type CreateRootCert struct {
type RootCertOperate struct {
ID uint `json:"id"`
Name string `json:"name"`
Mode string `json:"mode"`
EncryptionMode string `json:"encryptionMode" validate:"required,oneof=rsa ed25519 ecdsa dsa"`
+7 -1
View File
@@ -43,11 +43,17 @@ type App struct {
func (i *App) IsLocalApp() bool {
return i.Resource == constant.ResourceLocal
}
func (i *App) IsCustomApp() bool {
return i.Resource == constant.AppResourceCustom
}
func (i *App) GetAppResourcePath() string {
if i.IsLocalApp() {
//这里要去掉本地应用的local前缀
return filepath.Join(global.Dir.LocalAppResourceDir, strings.TrimPrefix(i.Key, "local"))
}
if i.IsCustomApp() {
return filepath.Join(global.Dir.CustomAppResourceDir, i.Key)
}
return filepath.Join(global.Dir.RemoteAppResourceDir, i.Key)
}
+1
View File
@@ -8,6 +8,7 @@ type AppLauncher struct {
type QuickJump struct {
BaseModel
Name string `json:"name"`
Alias string `json:"alias"`
Title string `json:"title"`
Detail string `json:"detail"`
Recommend int `json:"recommend"`
+3
View File
@@ -27,5 +27,8 @@ type BackupRecord struct {
FileDir string `json:"fileDir"`
FileName string `json:"fileName"`
TaskID string `json:"taskID"`
Status string `json:"status"`
Message string `json:"message"`
Description string `json:"description"`
}
+2
View File
@@ -10,6 +10,8 @@ type Clam struct {
InfectedStrategy string `json:"infectedStrategy"`
InfectedDir string `json:"infectedDir"`
Spec string `json:"spec"`
RetryTimes uint `json:"retryTimes"`
Timeout uint `json:"timeout"`
EntryID int `json:"entryID"`
Description string `json:"description"`
+2
View File
@@ -43,6 +43,8 @@ type Cronjob struct {
EntryIDs string `json:"entryIDs"`
Records []JobRecords `json:"records"`
Secret string `json:"secret"`
Config string `json:"config"`
}
type JobRecords struct {
+1
View File
@@ -9,6 +9,7 @@ type Database struct {
From string `json:"from" gorm:"not null"`
Address string `json:"address" gorm:"not null"`
Port uint `json:"port" gorm:"not null"`
InitialDB string `json:"initialDB"`
Username string `json:"username"`
Password string `json:"password"`
+11 -14
View File
@@ -3,19 +3,16 @@ package model
type Firewall struct {
BaseModel
Type string `gorm:"not null" json:"type"`
Port string `gorm:"not null" json:"port"`
Protocol string `gorm:"not null" json:"protocol"`
Address string `gorm:"not null" json:"address"`
Type string `json:"type"`
Port string `json:"port"` // Deprecated
Address string `json:"address"` // Deprecated
Chain string `json:"chain"`
Protocol string `json:"protocol"`
SrcIP string `json:"srcIP"`
SrcPort string `json:"srcPort"`
DstIP string `json:"dstIP"`
DstPort string `json:"dstPort"`
Strategy string `gorm:"not null" json:"strategy"`
Description string `gorm:"not null" json:"description"`
}
type Forward struct {
BaseModel
Protocol string `gorm:"not null" json:"protocol"`
Port string `gorm:"not null" json:"port"`
TargetIP string `gorm:"not null" json:"targetIP"`
TargetPort string `gorm:"not null" json:"targetPort"`
Description string `json:"description"`
}
+7 -3
View File
@@ -2,14 +2,18 @@ package model
type MonitorBase struct {
BaseModel
Cpu float64 `json:"cpu"`
Cpu float64 `json:"cpu"`
TopCPU string `json:"topCPU"`
TopCPUItems interface{} `gorm:"-" json:"topCPUItems"`
Memory float64 `json:"memory"`
TopMem string `json:"topMem"`
TopMemItems interface{} `gorm:"-" json:"topMemItems"`
LoadUsage float64 `json:"loadUsage"`
CpuLoad1 float64 `json:"cpuLoad1"`
CpuLoad5 float64 `json:"cpuLoad5"`
CpuLoad15 float64 `json:"cpuLoad15"`
Memory float64 `json:"memory"`
}
type MonitorIO struct {
+4
View File
@@ -49,3 +49,7 @@ func (r *Runtime) GetPHPPath() string {
func (r *Runtime) GetLogPath() string {
return path.Join(r.GetPath(), "build.log")
}
func (r *Runtime) GetSlowLogPath() string {
return path.Join(r.GetPath(), "log", "fpm.slow.log")
}
+14
View File
@@ -0,0 +1,14 @@
package model
type TensorRTLLM struct {
BaseModel
Name string `json:"name"`
DockerCompose string `json:"dockerCompose"`
ContainerName string `json:"containerName"`
Message string `json:"message"`
Status string `json:"status"`
Env string `json:"env"`
TaskID string `json:"taskID"`
ModelType string `json:"modelType"`
ModelSpeedup bool `json:"modelSpeedup"`
}
+31 -29
View File
@@ -10,35 +10,37 @@ import (
type WebsiteSSL struct {
BaseModel
PrimaryDomain string `json:"primaryDomain"`
PrivateKey string `json:"privateKey"`
Pem string `json:"pem"`
Domains string `json:"domains"`
CertURL string `json:"certURL"`
Type string `json:"type"`
Provider string `json:"provider"`
Organization string `json:"organization"`
DnsAccountID uint `json:"dnsAccountId"`
AcmeAccountID uint `gorm:"column:acme_account_id" json:"acmeAccountId" `
CaID uint `json:"caId"`
AutoRenew bool `json:"autoRenew"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
Status string `json:"status"`
Message string `json:"message"`
KeyType string `json:"keyType"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
Description string `json:"description"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
DisableCNAME bool `json:"disableCNAME"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
MasterSSLID uint `json:"masterSslId"`
Nodes string `json:"nodes"`
PushNode bool `json:"pushNode"`
PrimaryDomain string `json:"primaryDomain"`
PrivateKey string `json:"privateKey"`
Pem string `json:"pem"`
Domains string `json:"domains"`
CertURL string `json:"certURL"`
Type string `json:"type"`
Provider string `json:"provider"`
Organization string `json:"organization"`
DnsAccountID uint `json:"dnsAccountId"`
AcmeAccountID uint `gorm:"column:acme_account_id" json:"acmeAccountId" `
CaID uint `json:"caId"`
AutoRenew bool `json:"autoRenew"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
Status string `json:"status"`
Message string `json:"message"`
KeyType string `json:"keyType"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
Description string `json:"description"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
DisableCNAME bool `json:"disableCNAME"`
ExecShell bool `json:"execShell"`
Shell string `json:"shell"`
MasterSSLID uint `json:"masterSslId"`
Nodes string `json:"nodes"`
PushNode bool `json:"pushNode"`
PrivateKeyPath string `json:"privateKeyPath"`
CertPath string `json:"certPath"`
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
-9
View File
@@ -27,7 +27,6 @@ type IAppRepo interface {
GetFirst(opts ...DBOption) (model.App, error)
GetBy(opts ...DBOption) ([]model.App, error)
BatchCreate(ctx context.Context, apps []model.App) error
GetByKey(ctx context.Context, key string) (model.App, error)
Create(ctx context.Context, app *model.App) error
Save(ctx context.Context, app *model.App) error
BatchDelete(ctx context.Context, apps []model.App) error
@@ -144,14 +143,6 @@ func (a AppRepo) BatchCreate(ctx context.Context, apps []model.App) error {
return getTx(ctx).Omit(clause.Associations).Create(&apps).Error
}
func (a AppRepo) GetByKey(ctx context.Context, key string) (model.App, error) {
var app model.App
if err := getTx(ctx).Where("key = ?", key).First(&app).Error; err != nil {
return app, err
}
return app, nil
}
func (a AppRepo) Create(ctx context.Context, app *model.App) error {
return getTx(ctx).Omit(clause.Associations).Create(app).Error
}
+1
View File
@@ -95,6 +95,7 @@ func (u *LauncherRepo) UpdateQuicks(quicks []model.QuickJump) error {
if err := tx.Model(&model.QuickJump{}).Where("id = ?", item.ID).Updates(map[string]interface{}{
"is_show": item.IsShow,
"detail": item.Detail,
"alias": item.Alias,
}).Error; err != nil {
tx.Rollback()
return err
+2 -1
View File
@@ -20,6 +20,7 @@ type IBackupRepo interface {
WithByPublic(isPublic bool) DBOption
ListRecord(opts ...DBOption) ([]model.BackupRecord, error)
GetRecord(opts ...DBOption) (*model.BackupRecord, error)
PageRecord(page, size int, opts ...DBOption) (int64, []model.BackupRecord, error)
CreateRecord(record *model.BackupRecord) error
DeleteRecord(ctx context.Context, opts ...DBOption) error
@@ -160,7 +161,7 @@ func (u *BackupRepo) WithByCronID(cronjobID uint) DBOption {
}
func (u *BackupRepo) GetRecord(opts ...DBOption) (*model.BackupRecord, error) {
var record *model.BackupRecord
record := &model.BackupRecord{}
db := global.DB.Model(&model.BackupRecord{})
for _, opt := range opts {
db = opt(db)
+1 -1
View File
@@ -173,7 +173,7 @@ func (u *CronjobRepo) AddFailedRecord(cronjobID uint, message string) {
var record model.JobRecords
record.StartTime = time.Now()
record.CronjobID = cronjobID
record.Status = constant.StatusFailed
record.Status = constant.StatusUnexecuted
record.Message = message
if err := global.DB.Create(&record).Error; err != nil {
global.LOG.Errorf("create record status failed, err: %v", err)
+43 -24
View File
@@ -4,24 +4,26 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"gorm.io/gorm"
)
type HostRepo struct{}
type IHostRepo interface {
GetFirewallRecord(opts ...DBOption) (model.Firewall, error)
ListFirewallRecord() ([]model.Firewall, error)
ListFirewallRecord(opts ...DBOption) ([]model.Firewall, error)
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
SaveCert(cert *model.RootCert) error
UpdateCert(id uint, vars map[string]interface{}) error
DeleteCert(opts ...DBOption) error
WithByChain(chain string) DBOption
}
func NewIHostRepo() IHostRepo {
@@ -38,12 +40,16 @@ func (h *HostRepo) GetFirewallRecord(opts ...DBOption) (model.Firewall, error) {
return firewall, err
}
func (h *HostRepo) ListFirewallRecord() ([]model.Firewall, error) {
var datas []model.Firewall
if err := global.DB.Find(&datas).Error; err != nil {
return datas, nil
func (h *HostRepo) ListFirewallRecord(opts ...DBOption) ([]model.Firewall, error) {
var firewalls []model.Firewall
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return datas, nil
if err := global.DB.Find(&firewalls).Error; err != nil {
return firewalls, nil
}
return firewalls, nil
}
func (h *HostRepo) SaveFirewallRecord(firewall *model.Firewall) error {
@@ -51,16 +57,27 @@ func (h *HostRepo) SaveFirewallRecord(firewall *model.Firewall) error {
return global.DB.Save(firewall).Error
}
var data model.Firewall
if firewall.Type == "port" {
_ = global.DB.Where("type = ? AND port = ? AND protocol = ? AND address = ? AND strategy = ?", "port", firewall.Port, firewall.Protocol, firewall.Address, firewall.Strategy).First(&data)
if data.ID != 0 {
firewall.ID = data.ID
}
} else {
_ = global.DB.Where("type = ? AND address = ? AND strategy = ?", "address", firewall.Address, firewall.Strategy).First(&data)
if data.ID != 0 {
firewall.ID = data.ID
}
switch firewall.Type {
case "port":
_ = global.DB.Where("type = ? AND dst_port = ? AND protocol = ? AND src_ip = ? AND strategy = ?", "port",
firewall.DstPort,
firewall.Protocol,
firewall.SrcIP,
firewall.Strategy,
).First(&data).Error
case "ip":
_ = global.DB.Where("type = ? AND src_ip = ? AND strategy = ?", "address", firewall.SrcIP, firewall.Strategy).First(&data)
default:
_ = global.DB.Where("type = ? AND chain = ? AND src_port = ? AND dst_port = ? AND protocol = ? AND src_ip = ? AND dst_ip = ? AND strategy = ?",
firewall.Type,
firewall.Chain,
firewall.SrcPort,
firewall.DstPort,
firewall.Protocol,
firewall.SrcIP,
firewall.DstIP,
firewall.Strategy,
).First(&data).Error
}
return global.DB.Save(firewall).Error
}
@@ -69,10 +86,6 @@ func (h *HostRepo) DeleteFirewallRecordByID(id uint) error {
return global.DB.Where("id = ?", id).Delete(&model.Firewall{}).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
@@ -107,8 +120,8 @@ func (u *HostRepo) ListCert(opts ...DBOption) ([]model.RootCert, error) {
return ops, err
}
func (u *HostRepo) CreateCert(cert *model.RootCert) error {
return global.DB.Create(cert).Error
func (u *HostRepo) SaveCert(cert *model.RootCert) error {
return global.DB.Save(cert).Error
}
func (u *HostRepo) UpdateCert(id uint, vars map[string]interface{}) error {
@@ -151,3 +164,9 @@ func (u *HostRepo) SyncCert(data []model.RootCert) error {
tx.Commit()
return nil
}
func (u *HostRepo) WithByChain(chain string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("chain = ?", chain)
}
}
+7
View File
@@ -24,6 +24,7 @@ type ITaskRepo interface {
DeleteAll() error
WithByID(id string) DBOption
WithByIDNotIn(ids []string) DBOption
WithResourceID(id uint) DBOption
WithOperate(taskOperate string) DBOption
WithByStatus(status string) DBOption
@@ -58,6 +59,12 @@ func (t TaskRepo) WithByID(id string) DBOption {
}
}
func (t TaskRepo) WithByIDNotIn(ids []string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id not in (?)", ids)
}
}
func (t TaskRepo) WithOperate(taskOperate string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("operate = ?", taskOperate)
+56
View File
@@ -0,0 +1,56 @@
package repo
import "github.com/1Panel-dev/1Panel/agent/app/model"
type TensorRTLLMRepo struct {
}
type ITensorRTLLMRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.TensorRTLLM, error)
GetFirst(opts ...DBOption) (*model.TensorRTLLM, error)
Create(tensorrtLLM *model.TensorRTLLM) error
Save(tensorrtLLM *model.TensorRTLLM) error
DeleteBy(opts ...DBOption) error
List(opts ...DBOption) ([]model.TensorRTLLM, error)
}
func NewITensorRTLLMRepo() ITensorRTLLMRepo {
return &TensorRTLLMRepo{}
}
func (t TensorRTLLMRepo) Page(page, size int, opts ...DBOption) (int64, []model.TensorRTLLM, error) {
var servers []model.TensorRTLLM
db := getDb(opts...).Model(&model.TensorRTLLM{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&servers).Error
return count, servers, err
}
func (t TensorRTLLMRepo) GetFirst(opts ...DBOption) (*model.TensorRTLLM, error) {
var tensorrtLLM model.TensorRTLLM
if err := getDb(opts...).First(&tensorrtLLM).Error; err != nil {
return nil, err
}
return &tensorrtLLM, nil
}
func (t TensorRTLLMRepo) List(opts ...DBOption) ([]model.TensorRTLLM, error) {
var tensorrtLLMs []model.TensorRTLLM
if err := getDb(opts...).Find(&tensorrtLLMs).Error; err != nil {
return nil, err
}
return tensorrtLLMs, nil
}
func (t TensorRTLLMRepo) Create(tensorrtLLM *model.TensorRTLLM) error {
return getDb().Create(tensorrtLLM).Error
}
func (t TensorRTLLMRepo) Save(tensorrtLLM *model.TensorRTLLM) error {
return getDb().Save(tensorrtLLM).Error
}
func (t TensorRTLLMRepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.TensorRTLLM{}).Error
}
+4 -6
View File
@@ -134,9 +134,8 @@ func (u *AIToolService) Close(name string) error {
if err != nil {
return err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama stop %s", containerName, name)
if err != nil {
return fmt.Errorf("handle ollama stop %s failed, stdout: %s, err: %v", name, stdout, err)
if err := cmd.RunDefaultBashCf("docker exec %s ollama stop %s", containerName, name); err != nil {
return fmt.Errorf("handle ollama stop %s failed, %v", name, err)
}
return nil
}
@@ -193,9 +192,8 @@ func (u *AIToolService) Delete(req dto.ForceDelete) error {
}
for _, item := range ollamaList {
if item.Status != constant.StatusDeleted {
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama rm %s", containerName, item.Name)
if err != nil && !req.ForceDelete {
return fmt.Errorf("handle ollama rm %s failed, stdout: %s, err: %v", item.Name, stdout, err)
if err := cmd.RunDefaultBashCf("docker exec %s ollama rm %s", containerName, item.Name); err != nil && !req.ForceDelete {
return fmt.Errorf("handle ollama rm %s failed, %v", item.Name, err)
}
}
_ = aiRepo.Delete(repo.WithByID(item.ID))
+7 -2
View File
@@ -479,12 +479,17 @@ func (a AlertService) DeleteAlertConfig(id uint) error {
}
func (a AlertService) TestAlertConfig(req dto.AlertConfigTest) (bool, error) {
username := req.UserName
if username == "" {
username = req.Sender
}
cfg := email.SMTPConfig{
Host: req.Host,
Port: req.Port,
Username: req.Sender,
Sender: req.Sender,
Username: username,
Password: req.Password,
From: fmt.Sprintf("%s <%s>", req.DisplayName, req.Sender),
From: fmt.Sprintf(`"%s" <%s>`, req.DisplayName, req.Sender),
Encryption: req.Encryption,
Recipient: req.Recipient,
}
+20 -5
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"net/http"
"os"
"path"
"path/filepath"
"reflect"
"strconv"
@@ -72,7 +73,11 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
if err != nil {
return nil, err
}
opts = append(opts, appRepo.WithArch(info.KernelArch))
kernelArch := info.KernelArch
if kernelArch == "aarch64" {
kernelArch = "arm64"
}
opts = append(opts, appRepo.WithArch(kernelArch))
}
if len(req.Tags) != 0 {
tags, err := tagRepo.GetByKeys(req.Tags)
@@ -107,7 +112,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
lang := strings.ToLower(common.GetLang(ctx))
for _, ap := range apps {
if req.Type == "php" {
if ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion)) {
if !global.CONF.Base.IsOffLine && (ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion))) {
continue
}
}
@@ -445,10 +450,11 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
}
index++
}
if app.Limit == 0 && appInstall.Name != serviceName && len(servicesMap) == 1 {
servicesMap[appInstall.Name] = servicesMap[serviceName]
newServiceName := strings.ToLower(appInstall.Name)
if app.Limit == 0 && newServiceName != serviceName && len(servicesMap) == 1 {
servicesMap[newServiceName] = servicesMap[serviceName]
delete(servicesMap, serviceName)
serviceName = appInstall.Name
serviceName = newServiceName
}
appInstall.ServiceName = serviceName
@@ -480,6 +486,15 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
req.Params["RESTY_ADD_PACKAGE_BUILDDEPS"] = ""
req.Params["RESTY_CONFIG_OPTIONS_MORE"] = ""
}
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
}
}
paramByte, err = json.Marshal(req.Params)
if err != nil {
return
+21
View File
@@ -796,6 +796,9 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
res.RestartPolicy = getRestartPolicy(install.DockerCompose)
res.WebUI = install.WebUI
res.Type = install.App.Type
if rawCompose, err := getUpgradeCompose(install, detail); err == nil {
res.RawCompose = rawCompose
}
return &res, nil
}
@@ -940,8 +943,26 @@ func (a *AppInstallService) GetAppInstallInfo(installID uint) (*response.AppInst
HttpPort: appInstall.HttpPort,
Status: appInstall.Status,
Message: appInstall.Message,
AppKey: appInstall.App.Key,
Env: envMap,
ComposePath: appInstall.GetComposePath(),
}
for k, v := range envMap {
if strings.Contains(strings.ToUpper(k), "PANEL_APP_PORT") {
var port int
switch val := v.(type) {
case int:
port = val
case string:
port, err = strconv.Atoi(val)
if err != nil {
continue
}
default:
continue
}
res.AppPorts = append(res.AppPorts, port)
}
}
return res, nil
}
+181 -145
View File
@@ -15,7 +15,6 @@ import (
"path"
"path/filepath"
"reflect"
"regexp"
"strconv"
"strings"
"time"
@@ -40,6 +39,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/compose-spec/compose-go/v2/types"
@@ -134,6 +134,76 @@ var ToolKeys = map[string]uint{
"minio": 9001,
}
func CreateDB(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
database := &model.Database{
AppInstallID: appInstall.ID,
Name: appInstall.Name,
Type: app.Key,
Version: appInstall.Version,
From: "local",
Address: appInstall.ServiceName,
Port: DatabaseKeys[app.Key],
}
detail, err := appDetailRepo.GetFirst(repo.WithByID(appInstall.AppDetailId))
if err != nil {
return err
}
formFields := &dto.AppForm{}
if err := json.Unmarshal([]byte(detail.Params), formFields); err != nil {
return err
}
for _, form := range formFields.FormFields {
if form.EnvKey == "PANEL_APP_PORT_HTTP" {
portFloat, ok := form.Default.(float64)
if ok {
database.Port = uint(int(portFloat))
}
break
}
}
switch app.Key {
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb, constant.AppMysqlCluster, constant.AppPostgresqlCluster:
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
if password != "" {
database.Password = password.(string)
if app.Key == "mysql" || app.Key == "mariadb" || app.Key == constant.AppMysqlCluster {
database.Username = "root"
}
if rootUser, ok := params["PANEL_DB_ROOT_USER"]; ok {
database.Username = rootUser.(string)
}
authParam := dto.AuthParam{
RootPassword: password.(string),
RootUser: database.Username,
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
case constant.AppRedis, constant.AppRedisCluster:
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
authParam := dto.RedisAuthParam{
RootPassword: "",
}
if password != "" {
authParam.RootPassword = password.(string)
database.Password = password.(string)
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
return databaseRepo.Create(ctx, database)
}
func createLink(ctx context.Context, installTask *task.Task, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
deleteAppLink := func(t *task.Task) {
del := dto.DelAppLink{
@@ -145,74 +215,7 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
}
if DatabaseKeys[app.Key] > 0 {
handleDataBaseApp := func(task *task.Task) error {
database := &model.Database{
AppInstallID: appInstall.ID,
Name: appInstall.Name,
Type: app.Key,
Version: appInstall.Version,
From: "local",
Address: appInstall.ServiceName,
Port: DatabaseKeys[app.Key],
}
detail, err := appDetailRepo.GetFirst(repo.WithByID(appInstall.AppDetailId))
if err != nil {
return err
}
formFields := &dto.AppForm{}
if err := json.Unmarshal([]byte(detail.Params), formFields); err != nil {
return err
}
for _, form := range formFields.FormFields {
if form.EnvKey == "PANEL_APP_PORT_HTTP" {
portFloat, ok := form.Default.(float64)
if ok {
database.Port = uint(int(portFloat))
}
break
}
}
switch app.Key {
case constant.AppMysql, constant.AppMariaDB, constant.AppPostgresql, constant.AppMongodb, constant.AppMysqlCluster, constant.AppPostgresqlCluster:
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
if password != "" {
database.Password = password.(string)
if app.Key == "mysql" || app.Key == "mariadb" || app.Key == constant.AppMysqlCluster {
database.Username = "root"
}
if rootUser, ok := params["PANEL_DB_ROOT_USER"]; ok {
database.Username = rootUser.(string)
}
authParam := dto.AuthParam{
RootPassword: password.(string),
RootUser: database.Username,
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
case constant.AppRedis, constant.AppRedisCluster:
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
authParam := dto.RedisAuthParam{
RootPassword: "",
}
if password != "" {
authParam.RootPassword = password.(string)
database.Password = password.(string)
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
return databaseRepo.Create(ctx, database)
return CreateDB(ctx, app, appInstall, params)
}
installTask.AddSubTask(i18n.GetMsgByKey("HandleDatabaseApp"), handleDataBaseApp, deleteAppLink)
}
@@ -677,29 +680,29 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
backupFile string
)
backUpApp := func(t *task.Task) error {
if req.Backup {
backupService := NewIBackupService()
backupRecordService := NewIBackupRecordService()
fileName := fmt.Sprintf("upgrade_backup_%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
backupRecord, err := backupService.AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name, FileName: fileName})
if err == nil {
backups, _ := backupRecordService.ListAppRecords(install.App.Key, install.Name, "upgrade_backup")
if len(backups) > 3 {
backupsToDelete := backups[:len(backups)-3]
var deleteIDs []uint
for _, backup := range backupsToDelete {
deleteIDs = append(deleteIDs, backup.ID)
}
_ = backupRecordService.BatchDeleteRecord(deleteIDs)
backupService := NewIBackupService()
backupRecordService := NewIBackupRecordService()
fileName := fmt.Sprintf("upgrade_backup_%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
backupRecord, err := backupService.AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name, FileName: fileName})
if err == nil {
backups, _ := backupRecordService.ListAppRecords(install.App.Key, install.Name, "upgrade_backup")
if len(backups) > 3 {
backupsToDelete := backups[:len(backups)-3]
var deleteIDs []uint
for _, backup := range backupsToDelete {
deleteIDs = append(deleteIDs, backup.ID)
}
backupFile = path.Join(global.Dir.LocalBackupDir, backupRecord.FileDir, backupRecord.FileName)
} else {
return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
_ = backupRecordService.BatchDeleteRecord(deleteIDs)
}
backupFile = path.Join(global.Dir.LocalBackupDir, backupRecord.FileDir, backupRecord.FileName)
} else {
return buserr.WithNameAndErr("ErrAppBackup", install.Name, err)
}
return nil
}
upgradeTask.AddSubTask(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeApp), backUpApp, nil)
if req.Backup {
upgradeTask.AddSubTask(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeApp), backUpApp, nil)
}
upgradeApp := func(t *task.Task) error {
fileOp := files.NewFileOp()
@@ -946,7 +949,7 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
}
func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.AppInstall, logger *log.Logger) (err error) {
if app.IsLocalApp() {
if app.IsLocalApp() || app.IsCustomApp() {
return nil
}
appResourceDir := path.Join(global.Dir.AppResourceDir, app.Resource)
@@ -1082,11 +1085,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.RunWithStdoutBashC(scriptPath)
if err != nil {
if out != "" {
err = errors.New(out)
}
if err := cmdMgr.RunBashC(scriptPath); err != nil {
task.LogFailedWithErr(logStr, err)
return err
}
@@ -1163,7 +1162,11 @@ func upApp(task *task.Task, appInstall *model.AppInstall, pullImages bool) error
appInstall.Message = errMsg + errOur
installErr := errors.New(appInstall.Message)
task.LogFailedWithErr(i18n.GetMsgByKey("PullImage"), installErr)
return installErr
if exist, _ := dockerCLi.ImageExists(image); !exist {
return installErr
} else {
task.Log(i18n.GetMsgByKey("UseExistImage"))
}
} else {
task.Log(i18n.GetMsgByKey("PullImageSuccess"))
}
@@ -1409,6 +1412,8 @@ func handleLocalApp(appDir string) (app *model.App, err error) {
desc, _ := json.Marshal(appDefine.Description)
app.Description = string(desc)
app.Key = "local" + appDefine.Key
app.Architectures = strings.Join(appDefine.Architectures, ",")
app.GpuSupport = appDefine.GpuSupport
app.Resource = constant.AppResourceLocal
app.Status = constant.AppNormal
@@ -1562,41 +1567,41 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
Website: installed.App.Website,
Document: installed.App.Document,
},
Favorite: installed.Favorite,
Container: installed.ContainerName,
Favorite: installed.Favorite,
Container: installed.ContainerName,
ServiceName: strings.ToLower(installed.ServiceName),
}
if updated {
installDTO.DockerCompose = installed.DockerCompose
if !updated {
installDTO.LinkDB = hasLinkDB(installed.ID)
res = append(res, installDTO)
continue
}
app, err := appRepo.GetFirst(repo.WithByID(installed.AppId))
if err != nil {
return nil, err
if installed.Version == "latest" {
continue
}
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(app.ID))
installDTO.DockerCompose = installed.DockerCompose
installDTO.IsEdit = isEditCompose(installed)
details, err := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID))
if err != nil {
return nil, err
}
var versions []string
for _, detail := range details {
ignores, _ := appIgnoreUpgradeRepo.List(runtimeRepo.WithDetailId(detail.ID), appIgnoreUpgradeRepo.WithScope("version"))
if len(ignores) > 0 || installed.Version == "latest" {
for _, appDetail := range details {
ignores, _ := appIgnoreUpgradeRepo.List(runtimeRepo.WithDetailId(appDetail.ID), appIgnoreUpgradeRepo.WithScope("version"))
if len(ignores) > 0 {
continue
}
if common.IsCrossVersion(installed.Version, detail.Version) && !app.CrossVersionUpdate {
if common.IsCrossVersion(installed.Version, appDetail.Version) && !installed.App.CrossVersionUpdate {
continue
}
versions = append(versions, detail.Version)
versions = append(versions, appDetail.Version)
}
versions = common.GetSortedVersions(versions)
if len(versions) == 0 {
if !updated {
installDTO.CanUpdate = false
res = append(res, installDTO)
}
continue
}
versions = common.GetSortedVersions(versions)
lastVersion := versions[0]
if app.Key == constant.AppMysql || app.Key == constant.AppMysqlCluster {
if installed.App.Key == constant.AppMysql || installed.App.Key == constant.AppMysqlCluster {
for _, version := range versions {
majorVersion := getMajorVersion(installed.Version)
if !strings.HasPrefix(version, majorVersion) {
@@ -1608,15 +1613,11 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
}
}
if common.IsCrossVersion(installed.Version, lastVersion) {
installDTO.CanUpdate = app.CrossVersionUpdate
installDTO.CanUpdate = installed.App.CrossVersionUpdate
} else {
installDTO.CanUpdate = common.CompareVersion(lastVersion, installed.Version)
}
if updated {
if installDTO.CanUpdate {
res = append(res, installDTO)
}
} else {
if installDTO.CanUpdate {
res = append(res, installDTO)
}
}
@@ -1820,8 +1821,7 @@ func getAppCommonConfig(envs map[string]interface{}) request.AppContainerConfig
config.CpuQuota = 0
}
if memLimit, ok := envs[constant.MemoryLimit]; ok {
re := regexp.MustCompile(`(\d+)([A-Za-z]+)`)
matches := re.FindStringSubmatch(memLimit.(string))
matches := re.GetRegex(re.NumberAlphaPattern).FindStringSubmatch(memLimit.(string))
if len(matches) == 3 {
num, err := strconv.ParseFloat(matches[1], 64)
if err == nil {
@@ -1995,7 +1995,7 @@ func handleOpenrestyFile(appInstall *model.AppInstall) error {
break
}
}
if err := handleSSLConfig(appInstall, hasDefaultWebsite); err != nil {
if err := handleSSLConfig(appInstall, hasDefaultWebsite, true); err != nil {
return err
}
if len(websites) == 0 {
@@ -2024,34 +2024,37 @@ func handleDefaultServer(appInstall *model.AppInstall) error {
return nil
}
func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool) error {
func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool, sslRejectHandshake bool) error {
sslDir := path.Join(appInstall.GetPath(), "conf", "ssl")
fileOp := files.NewFileOp()
if !fileOp.Stat(sslDir) {
return errors.New("ssl dir not found")
}
ca, _ := websiteCARepo.GetFirst(repo.WithByName("1Panel"))
if ca.ID == 0 {
global.LOG.Errorf("create openresty default ssl failed ca not found")
return nil
hasDefaultSSL := fileOp.Stat(path.Join(sslDir, "fullchain.pem")) && fileOp.Stat(path.Join(sslDir, "privkey.pem")) && fileOp.Stat(path.Join(sslDir, "root_ssl.conf"))
if !hasDefaultSSL {
ca, _ := websiteCARepo.GetFirst(repo.WithByName("1Panel"))
if ca.ID == 0 {
global.LOG.Errorf("create openresty default ssl failed ca not found")
return nil
}
caService := NewIWebsiteCAService()
caRequest := request.WebsiteCAObtain{
ID: ca.ID,
Domains: "localhost",
KeyType: "4096",
Time: 99,
Unit: "year",
Dir: sslDir,
PushDir: true,
}
websiteSSL, err := caService.ObtainSSL(caRequest)
if err != nil {
return err
}
defer func() {
_ = NewIWebsiteSSLService().Delete([]uint{websiteSSL.ID})
}()
}
caService := NewIWebsiteCAService()
caRequest := request.WebsiteCAObtain{
ID: ca.ID,
Domains: "localhost",
KeyType: "4096",
Time: 99,
Unit: "year",
Dir: sslDir,
PushDir: true,
}
websiteSSL, err := caService.ObtainSSL(caRequest)
if err != nil {
return err
}
defer func() {
_ = NewIWebsiteSSLService().Delete([]uint{websiteSSL.ID})
}()
defaultConfigPath := path.Join(appInstall.GetPath(), "conf", "default", "00.default.conf")
content, err := os.ReadFile(defaultConfigPath)
if err != nil {
@@ -2063,9 +2066,14 @@ func handleSSLConfig(appInstall *model.AppInstall, hasDefaultWebsite bool) error
}
defaultConfig.FilePath = defaultConfigPath
defaultServer := defaultConfig.FindServers()[0]
updateDefaultServer(defaultServer, appInstall.HttpPort, appInstall.HttpsPort, !hasDefaultWebsite)
updateDefaultServer(defaultServer, appInstall.HttpPort, appInstall.HttpsPort, !hasDefaultWebsite, true)
defaultServer.UpdateDirective("include", []string{"/usr/local/openresty/nginx/conf/ssl/root_ssl.conf"})
defaultServer.UpdateDirective("http2", []string{"on"})
if sslRejectHandshake {
defaultServer.UpdateDirective("ssl_reject_handshake", []string{"on"})
} else {
defaultServer.RemoveDirective("ssl_reject_handshake", []string{})
}
if err = nginx.WriteConfig(defaultConfig, nginx.IndentedStyle); err != nil {
return err
}
@@ -2127,3 +2135,31 @@ func needsUpdate(localTag *model.Tag, remoteTag dto.Tag, translations string) bo
localTag.Sort != remoteTag.Sort ||
localTag.Translations != translations
}
func hasLinkDB(installID uint) bool {
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(installID))
hasDB := false
if len(resources) > 0 {
for _, resource := range resources {
if resource.Key == constant.AppPostgres || resource.Key == constant.AppMysql ||
resource.Key == constant.AppMariaDB || resource.Key == constant.AppMysqlCluster ||
resource.Key == constant.AppPostgresql || resource.Key == constant.AppPostgresqlCluster {
hasDB = true
break
}
}
}
return hasDB
}
func isEditCompose(installed model.AppInstall) bool {
detail, _ := appDetailRepo.GetFirst(repo.WithByID(installed.AppDetailId))
rawCompose, err := getUpgradeCompose(installed, detail)
if rawCompose == "" || err != nil {
return false
}
if rawCompose != installed.DockerCompose {
return true
}
return false
}
+51 -47
View File
@@ -45,36 +45,29 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, er
fileName = fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
}
backupApp := func() (*model.BackupRecord, error) {
if err = handleAppBackup(&install, nil, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
global.LOG.Errorf("backup app %s failed, err: %v", req.DetailName, err)
return nil, err
}
record := &model.BackupRecord{
Type: "app",
Name: req.Name,
DetailName: req.DetailName,
SourceAccountIDs: "1",
DownloadAccountID: 1,
FileDir: itemDir,
FileName: fileName,
Description: req.Description,
}
if err := backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return nil, err
}
return record, nil
record := &model.BackupRecord{
Type: "app",
Name: req.Name,
DetailName: req.DetailName,
SourceAccountIDs: "1",
DownloadAccountID: 1,
FileDir: itemDir,
FileName: fileName,
TaskID: req.TaskID,
Status: constant.StatusWaiting,
Description: req.Description,
}
if err := backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return nil, err
}
if req.TaskID != "" {
go func() {
_, _ = backupApp()
}()
} else {
return backupApp()
if err = handleAppBackup(&install, nil, record.ID, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
global.LOG.Errorf("backup app %s failed, err: %v", req.DetailName, err)
return nil, err
}
return nil, nil
return record, nil
}
func (u *BackupService) AppRecover(req dto.CommonRecover) error {
@@ -94,11 +87,9 @@ func (u *BackupService) AppRecover(req dto.CommonRecover) error {
if _, err := compose.Down(install.GetComposePath()); err != nil {
return err
}
go func() {
if err := handleAppRecover(&install, nil, req.File, false, req.Secret, req.TaskID); err != nil {
global.LOG.Errorf("recover app %s failed, err: %v", req.DetailName, err)
}
}()
if err := handleAppRecover(&install, nil, req.File, false, req.Secret, req.TaskID); err != nil {
global.LOG.Errorf("recover app %s failed, err: %v", req.DetailName, err)
}
return nil
}
@@ -111,7 +102,7 @@ func backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name str
}
parentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
databaseHelper := DatabaseHelper{Database: db.MysqlName, DBType: resourceKey, Name: db.Name}
if err := handleMysqlBackup(databaseHelper, parentTask, tmpDir, fmt.Sprintf("%s.sql.gz", name), ""); err != nil {
if err := handleMysqlBackup(databaseHelper, parentTask, 0, tmpDir, fmt.Sprintf("%s.sql.gz", name), "", ""); err != nil {
return err
}
parentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
@@ -122,7 +113,7 @@ func backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name str
}
parentTask.LogStart(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
databaseHelper := DatabaseHelper{Database: db.PostgresqlName, DBType: resourceKey, Name: db.Name}
if err := handlePostgresqlBackup(databaseHelper, parentTask, tmpDir, fmt.Sprintf("%s.sql.gz", name), ""); err != nil {
if err := handlePostgresqlBackup(databaseHelper, parentTask, 0, tmpDir, fmt.Sprintf("%s.sql.gz", name), "", ""); err != nil {
return err
}
parentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
@@ -130,25 +121,34 @@ func backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name str
return nil
}
func handleAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fileName, excludes, secret, taskID string) error {
func handleAppBackup(install *model.AppInstall, parentTask *task.Task, recordID uint, backupDir, fileName, excludes, secret, taskID string) error {
var (
err error
backupTask *task.Task
)
backupTask = parentTask
if parentTask == nil {
backupTask, err = task.NewTaskWithOps(install.Name, task.TaskBackup, task.TaskScopeApp, taskID, install.ID)
backupTask, err = task.NewTaskWithOps(install.Name, task.TaskBackup, task.TaskScopeBackup, taskID, install.ID)
if err != nil {
return err
}
}
itemHandler := func() error { return doAppBackup(install, backupTask, backupDir, fileName, excludes, secret) }
backupTask.AddSubTask(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeApp), func(t *task.Task) error { return itemHandler() }, nil)
if parentTask != nil {
return itemHandler()
}
return backupTask.Execute()
backupTask.AddSubTaskWithOps(task.GetTaskName(install.Name, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
go func() {
if err := backupTask.Execute(); err != nil {
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
return
}
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
}()
return nil
}
func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverFile string, isRollback bool, secret, taskID string) error {
@@ -160,7 +160,7 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
)
recoverTask = parentTask
if parentTask == nil {
recoverTask, err = task.NewTaskWithOps(install.Name, task.TaskRecover, task.TaskScopeApp, taskID, install.ID)
recoverTask, err = task.NewTaskWithOps(install.Name, task.TaskRecover, task.TaskScopeBackup, taskID, install.ID)
if err != nil {
return err
}
@@ -170,7 +170,7 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
fileOp := files.NewFileOp()
if !isRollback {
rollbackFile = path.Join(global.Dir.TmpDir, fmt.Sprintf("app/%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)))
if err := handleAppBackup(install, recoverTask, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", taskID); err != nil {
if err := handleAppBackup(install, recoverTask, 0, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", taskID); err != nil {
t.Log(fmt.Sprintf("backup app %s for rollback before recover failed, err: %v", install.Name, err))
}
}
@@ -260,7 +260,7 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
Name: newDB.MysqlName,
DetailName: newDB.Name,
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
}, parentTask, true, ""); err != nil {
}, parentTask, true); err != nil {
t.LogFailedWithErr(taskName, err)
return err
}
@@ -308,6 +308,10 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
return nil
}
if parentTask != nil {
return recoverApp(parentTask)
}
rollBackApp := func(t *task.Task) {
if isRollback {
return
@@ -324,12 +328,11 @@ func handleAppRecover(install *model.AppInstall, parentTask *task.Task, recoverF
_ = os.RemoveAll(rollbackFile)
}
}
recoverTask.AddSubTask(task.GetTaskName(install.Name, task.TaskRecover, task.TaskScopeApp), recoverApp, rollBackApp)
if parentTask != nil {
return recoverApp(parentTask)
}
return recoverTask.Execute()
recoverTask.AddSubTask(task.GetTaskName(install.Name, task.TaskRecover, task.TaskScopeBackup), recoverApp, rollBackApp)
go func() {
_ = recoverTask.Execute()
}()
return nil
}
func doAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fileName, excludes, secret string) error {
@@ -351,6 +354,7 @@ func doAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fi
}
appPath := install.GetPath()
parentTask.LogStart(i18n.GetMsgByKey("TaskBackup"))
if err := fileOp.TarGzCompressPro(true, appPath, path.Join(tmpDir, "app.tar.gz"), "", excludes); err != nil {
return err
}
+48 -30
View File
@@ -30,11 +30,6 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
targetDir := path.Join(global.Dir.LocalBackupDir, itemDir)
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
if err := handleMysqlBackup(databaseHelper, nil, targetDir, fileName, req.TaskID); err != nil {
return err
}
record := &model.BackupRecord{
Type: req.Type,
Name: req.Name,
@@ -43,19 +38,25 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
DownloadAccountID: 1,
FileDir: itemDir,
FileName: fileName,
TaskID: req.TaskID,
Status: constant.StatusWaiting,
Description: req.Description,
}
if err := backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
if err := handleMysqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID, req.Secret); err != nil {
backupRepo.UpdateRecordByMap(record.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
return err
}
return nil
}
func (u *BackupService) MysqlRecover(req dto.CommonRecover) error {
if err := handleMysqlRecover(req, nil, false, req.TaskID); err != nil {
return err
}
return nil
return handleMysqlRecover(req, nil, false)
}
func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
@@ -64,14 +65,14 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
return err
}
req.File = recoveFile
if err := handleMysqlRecover(req, nil, false, req.TaskID); err != nil {
if err := handleMysqlRecover(req, nil, false); err != nil {
return err
}
global.LOG.Info("recover from uploads successful!")
return nil
}
func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir, fileName, taskID string) error {
func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID, secret string) error {
var (
err error
backupTask *task.Task
@@ -83,21 +84,28 @@ func handleMysqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir, file
}
itemName := fmt.Sprintf("%s[%s] - %s", db.Database, db.DBType, db.Name)
if parentTask == nil {
backupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeDatabase, taskID, dbInfo.ID)
backupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeBackup, taskID, dbInfo.ID)
if err != nil {
return err
}
}
itemHandler := func() error { return doMysqlBackup(db, targetDir, fileName) }
backupTask.AddSubTask(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeDatabase), func(t *task.Task) error { return itemHandler() }, nil)
itemHandler := func() error { return doMysqlBackup(db, targetDir, fileName, secret) }
if parentTask != nil {
return itemHandler()
}
return backupTask.Execute()
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
go func() {
if err := backupTask.Execute(); err != nil {
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
return
}
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
}()
return nil
}
func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool, taskID string) error {
func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool) error {
var (
err error
itemTask *task.Task
@@ -109,7 +117,7 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
}
itemName := fmt.Sprintf("%s[%s] - %s", req.Name, req.Type, req.DetailName)
if parentTask == nil {
itemTask, err = task.NewTaskWithOps(itemName, task.TaskRecover, task.TaskScopeDatabase, taskID, dbInfo.ID)
itemTask, err = task.NewTaskWithOps(itemName, task.TaskRecover, task.TaskScopeBackup, req.TaskID, dbInfo.ID)
if err != nil {
return err
}
@@ -139,8 +147,6 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
Format: dbInfo.Format,
TargetDir: path.Dir(rollbackFile),
FileName: path.Base(rollbackFile),
Timeout: 300,
}); err != nil {
return fmt.Errorf("backup mysql db %s for rollback before recover failed, err: %v", req.DetailName, err)
}
@@ -153,8 +159,6 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
Version: version,
Format: dbInfo.Format,
SourceFile: rollbackFile,
Timeout: 300,
}); err != nil {
global.LOG.Errorf("rollback mysql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
} else {
@@ -166,14 +170,21 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
}
}()
}
if len(req.Secret) != 0 {
err = files.OpensslDecrypt(req.File, req.Secret)
if err != nil {
return err
}
req.File = path.Join(path.Dir(req.File), "tmp_"+path.Base(req.File))
defer os.Remove(req.File)
t.LogWithStatus(i18n.GetMsgByKey("Decrypt"), err)
}
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
Type: req.Type,
Version: version,
Format: dbInfo.Format,
SourceFile: req.File,
Timeout: 300,
}); err != nil {
global.LOG.Errorf("recover mysql db %s from %s failed, err: %v", req.DetailName, req.File, err)
return err
@@ -181,15 +192,18 @@ func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback
isOk = true
return nil
}
itemTask.AddSubTask(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil)
if parentTask != nil {
return recoverDatabase(parentTask)
}
return itemTask.Execute()
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 3, time.Hour)
go func() {
_ = itemTask.Execute()
}()
return nil
}
func doMysqlBackup(db DatabaseHelper, targetDir, fileName string) error {
func doMysqlBackup(db DatabaseHelper, targetDir, fileName, secret string) error {
dbInfo, err := mysqlRepo.Get(repo.WithByName(db.Name), mysqlRepo.WithByMysqlName(db.Database))
if err != nil {
return err
@@ -205,10 +219,14 @@ func doMysqlBackup(db DatabaseHelper, targetDir, fileName string) error {
Format: dbInfo.Format,
TargetDir: targetDir,
FileName: fileName,
Timeout: 300,
}
return cli.Backup(backupInfo)
if err := cli.Backup(backupInfo); err != nil {
return err
}
if len(secret) != 0 {
return files.OpensslEncrypt(path.Join(targetDir, fileName), secret)
}
return nil
}
func loadSqlFile(file string) (string, error) {
+48 -24
View File
@@ -6,21 +6,18 @@ import (
"path"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/utils/common"
pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
"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/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
pgclient "github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
)
func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
@@ -29,11 +26,6 @@ func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
targetDir := path.Join(global.Dir.LocalBackupDir, itemDir)
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
if err := handlePostgresqlBackup(databaseHelper, nil, targetDir, fileName, req.TaskID); err != nil {
return err
}
record := &model.BackupRecord{
Type: req.Type,
Name: req.Name,
@@ -42,11 +34,18 @@ func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
DownloadAccountID: 1,
FileDir: itemDir,
FileName: fileName,
TaskID: req.TaskID,
Status: constant.StatusWaiting,
Description: req.Description,
}
if err := backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
}
databaseHelper := DatabaseHelper{Database: req.Name, DBType: req.Type, Name: req.DetailName}
if err := handlePostgresqlBackup(databaseHelper, nil, record.ID, targetDir, fileName, req.TaskID, req.Secret); err != nil {
return err
}
return nil
}
func (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {
@@ -69,7 +68,7 @@ func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
return nil
}
func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir, fileName, taskID string) error {
func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, recordID uint, targetDir, fileName, taskID, secret string) error {
var (
err error
backupTask *task.Task
@@ -77,18 +76,25 @@ func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir,
backupTask = parentTask
itemName := fmt.Sprintf("%s - %s", db.Database, db.Name)
if parentTask == nil {
backupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeDatabase, taskID, db.ID)
backupTask, err = task.NewTaskWithOps(itemName, task.TaskBackup, task.TaskScopeBackup, taskID, db.ID)
if err != nil {
return err
}
}
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName) }
backupTask.AddSubTask(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeDatabase), func(task *task.Task) error { return itemHandler() }, nil)
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName, secret) }
if parentTask != nil {
return itemHandler()
}
return backupTask.Execute()
backupTask.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
go func() {
if err := backupTask.Execute(); err != nil {
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
return
}
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
}()
return nil
}
func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool) error {
@@ -102,7 +108,7 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
}
itemTask = parentTask
if parentTask == nil {
itemTask, err = task.NewTaskWithOps(req.Name, task.TaskRecover, task.TaskScopeDatabase, req.TaskID, dbInfo.ID)
itemTask, err = task.NewTaskWithOps(req.Name, task.TaskRecover, task.TaskScopeBackup, req.TaskID, dbInfo.ID)
if err != nil {
return err
}
@@ -151,6 +157,15 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
}
}()
}
if len(req.Secret) != 0 {
err = files.OpensslDecrypt(req.File, req.Secret)
if err != nil {
return err
}
req.File = path.Join(path.Dir(req.File), "tmp_"+path.Base(req.File))
defer os.Remove(req.File)
t.LogWithStatus(i18n.GetMsgByKey("Decrypt"), err)
}
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
SourceFile: req.File,
@@ -163,15 +178,18 @@ func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRol
isOk = true
return nil
}
itemTask.AddSubTask(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil)
if parentTask != nil {
return recoverDatabase(parentTask)
}
return itemTask.Execute()
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil, 3, time.Hour)
go func() {
_ = itemTask.Execute()
}()
return nil
}
func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName string) error {
func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName, secret string) error {
cli, err := LoadPostgresqlClientByFrom(db.Database)
if err != nil {
return err
@@ -184,5 +202,11 @@ func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName string) error {
Timeout: 300,
}
return cli.Backup(backupInfo)
if err := cli.Backup(backupInfo); err != nil {
return err
}
if len(secret) != 0 {
return files.OpensslEncrypt(path.Join(targetDir, fileName), secret)
}
return nil
}
+4
View File
@@ -11,6 +11,7 @@ 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/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/jinzhu/copier"
)
@@ -239,6 +240,9 @@ func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.Recor
return nil, err
}
for _, item := range records {
if item.Status == constant.StatusWaiting {
continue
}
list = append(list, backupSizeHelper{ID: item.ID, DownloadID: item.DownloadAccountID, FilePath: path.Join(item.FileDir, item.FileName)})
}
}
+28 -20
View File
@@ -19,7 +19,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/compose"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/pkg/errors"
)
func (u *BackupService) RedisBackup(req dto.CommonBackup) error {
@@ -44,22 +43,24 @@ func (u *BackupService) RedisBackup(req dto.CommonBackup) error {
}
itemDir := fmt.Sprintf("database/redis/%s", redisInfo.Name)
backupDir := path.Join(global.Dir.LocalBackupDir, itemDir)
if err := handleRedisBackup(redisInfo, nil, backupDir, fileName, req.Secret, req.TaskID); err != nil {
return err
}
record := &model.BackupRecord{
Type: "redis",
Type: req.Type,
Name: req.Name,
SourceAccountIDs: "1",
DownloadAccountID: 1,
FileDir: itemDir,
FileName: fileName,
TaskID: req.TaskID,
Status: constant.StatusWaiting,
Description: req.Description,
}
if err := backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
}
if err := handleRedisBackup(redisInfo, nil, record.ID, backupDir, fileName, req.Secret, req.TaskID); err != nil {
return err
}
return nil
}
@@ -75,15 +76,14 @@ func (u *BackupService) RedisRecover(req dto.CommonRecover) error {
return nil
}
func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDir, fileName, secret, taskID string) error {
func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, recordID uint, backupDir, fileName, secret, taskID string) error {
var (
err error
itemTask *task.Task
)
itemTask = parentTask
if parentTask == nil {
itemTask, err = task.NewTaskWithOps("Redis", task.TaskBackup, task.TaskScopeDatabase, taskID, redisInfo.ID)
itemTask, err = task.NewTaskWithOps("Redis", task.TaskBackup, task.TaskScopeBackup, taskID, redisInfo.ID)
if err != nil {
return err
}
@@ -97,9 +97,8 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDi
}
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s redis-cli -a %s --no-auth-warning save", redisInfo.ContainerName, redisInfo.Password)
if err != nil {
return errors.New(string(stdout))
if err := cmd.RunDefaultBashCf("docker exec %s redis-cli -a %s --no-auth-warning save", redisInfo.ContainerName, redisInfo.Password); err != nil {
return err
}
if strings.HasSuffix(fileName, ".tar.gz") {
@@ -110,16 +109,14 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDi
return nil
}
if strings.HasSuffix(fileName, ".aof") {
stdout1, err := cmd.RunDefaultWithStdoutBashCf("docker cp %s:/data/appendonly.aof %s/%s", redisInfo.ContainerName, backupDir, fileName)
if err != nil {
return errors.New(stdout1)
if err := cmd.RunDefaultBashCf("docker cp %s:/data/appendonly.aof %s/%s", redisInfo.ContainerName, backupDir, fileName); err != nil {
return err
}
return nil
}
stdout1, err1 := cmd.RunDefaultWithStdoutBashCf("docker cp %s:/data/dump.rdb %s/%s", redisInfo.ContainerName, backupDir, fileName)
if err1 != nil {
return errors.New(stdout1)
if err := cmd.RunDefaultBashCf("docker cp %s:/data/dump.rdb %s/%s", redisInfo.ContainerName, backupDir, fileName); err != nil {
return err
}
return nil
}
@@ -128,6 +125,14 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDi
return backupDatabase(parentTask)
}
itemTask.AddSubTaskWithOps(i18n.GetMsgByKey("TaskBackup"), backupDatabase, nil, 3, time.Hour)
go func() {
if err := itemTask.Execute(); err != nil {
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
return
}
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
}()
return itemTask.Execute()
}
@@ -138,7 +143,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recover
)
itemTask = parentTask
if parentTask == nil {
itemTask, err = task.NewTaskWithOps("Redis", task.TaskRecover, task.TaskScopeDatabase, taskID, redisInfo.ID)
itemTask, err = task.NewTaskWithOps("Redis", task.TaskRecover, task.TaskScopeBackup, taskID, redisInfo.ID)
if err != nil {
return err
}
@@ -180,7 +185,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recover
}
}
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/redis/%s_%s.%s", redisInfo.Name, time.Now().Format(constant.DateTimeSlimLayout), suffix))
if err := handleRedisBackup(redisInfo, nil, path.Dir(rollbackFile), path.Base(rollbackFile), secret, ""); err != nil {
if err := handleRedisBackup(redisInfo, nil, 0, path.Dir(rollbackFile), path.Base(rollbackFile), secret, ""); err != nil {
return fmt.Errorf("backup database %s for rollback before recover failed, err: %v", redisInfo.Name, err)
}
defer func() {
@@ -230,5 +235,8 @@ func handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recover
return recoverDatabase(parentTask)
}
return itemTask.Execute()
go func() {
_ = itemTask.Execute()
}()
return nil
}
+82 -62
View File
@@ -16,7 +16,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/compose"
"github.com/pkg/errors"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
@@ -38,26 +37,26 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
backupDir := path.Join(global.Dir.LocalBackupDir, itemDir)
fileName := fmt.Sprintf("%s_%s.tar.gz", website.Alias, timeNow+common.RandStrAndNum(5))
go func() {
if err = handleWebsiteBackup(&website, nil, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
global.LOG.Errorf("backup website %s failed, err: %v", website.Alias, err)
return
}
record := &model.BackupRecord{
Type: "website",
Name: website.Alias,
DetailName: website.Alias,
SourceAccountIDs: "1",
DownloadAccountID: 1,
FileDir: itemDir,
FileName: fileName,
Description: req.Description,
}
if err = backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return
}
}()
record := &model.BackupRecord{
Type: "website",
Name: website.Alias,
DetailName: website.Alias,
SourceAccountIDs: "1",
DownloadAccountID: 1,
FileDir: itemDir,
FileName: fileName,
TaskID: req.TaskID,
Status: constant.StatusWaiting,
Description: req.Description,
}
if err = backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
if err = handleWebsiteBackup(&website, nil, record.ID, backupDir, fileName, "", req.Secret, req.TaskID); err != nil {
global.LOG.Errorf("backup website %s failed, err: %v", website.Alias, err)
return err
}
return nil
}
@@ -66,40 +65,34 @@ func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
if err != nil {
return err
}
go func() {
if err := handleWebsiteRecover(&website, req.File, false, req.Secret, req.TaskID); err != nil {
global.LOG.Errorf("recover website %s failed, err: %v", website.Alias, err)
}
}()
if err := handleWebsiteRecover(&website, nil, req.File, false, req.Secret, req.TaskID); err != nil {
global.LOG.Errorf("recover website %s failed, err: %v", website.Alias, err)
}
return nil
}
func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback bool, secret, taskID string) error {
recoverTask, err := task.NewTaskWithOps(website.PrimaryDomain, task.TaskRecover, task.TaskScopeWebsite, taskID, website.ID)
if err != nil {
return err
func handleWebsiteRecover(website *model.Website, parentTask *task.Task, recoverFile string, isRollback bool, secret, taskID string) error {
var (
err error
recoverTask *task.Task
isOk = false
rollbackFile string
)
recoverTask = parentTask
if parentTask == nil {
recoverTask, err = task.NewTaskWithOps(website.PrimaryDomain, task.TaskRecover, task.TaskScopeBackup, taskID, website.ID)
if err != nil {
return err
}
}
recoverTask.AddSubTask(task.GetTaskName(website.PrimaryDomain, task.TaskRecover, task.TaskScopeWebsite), func(t *task.Task) error {
isOk := false
recoverWebsite := func(t *task.Task) error {
if !isRollback {
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))
if err := handleWebsiteBackup(website, recoverTask, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", taskID); err != nil {
rollbackFile = path.Join(global.Dir.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format(constant.DateTimeSlimLayout)))
if err := handleWebsiteBackup(website, recoverTask, 0, path.Dir(rollbackFile), path.Base(rollbackFile), "", "", taskID); err != nil {
return fmt.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
}
defer func() {
if !isOk {
t.LogStart(i18n.GetMsgByKey("Rollback"))
if err := handleWebsiteRecover(website, rollbackFile, true, "", taskID); err != nil {
t.LogFailedWithErr(i18n.GetMsgByKey("Rollback"), err)
return
}
t.LogSuccess(i18n.GetMsgByKey("Rollback"))
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
fileOp := files.NewFileOp()
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
t.Log(i18n.GetWithName("DeCompressFile", recoverFile))
@@ -151,12 +144,10 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
taskName := task.GetTaskName(app.Name, task.TaskRecover, task.TaskScopeApp)
t.LogStart(taskName)
if err := handleAppRecover(&app, recoverTask, fmt.Sprintf("%s/%s.app.tar.gz", tmpPath, website.Alias), true, "", ""); err != nil {
t.LogFailedWithErr(taskName, err)
return err
}
t.LogSuccess(taskName)
if _, err = compose.DownAndUp(fmt.Sprintf("%s/%s/%s/docker-compose.yml", global.Dir.AppInstallDir, app.App.Key, app.Name)); err != nil {
t.LogFailedWithErr("Run", err)
return err
}
case constant.Runtime:
@@ -167,7 +158,6 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
taskName := task.GetTaskName(runtime.Name, task.TaskRecover, task.TaskScopeRuntime)
t.LogStart(taskName)
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
t.LogFailedWithErr(taskName, err)
return err
}
t.LogSuccess(taskName)
@@ -186,12 +176,10 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
taskName := i18n.GetMsgByKey("TaskRecover") + i18n.GetMsgByKey("websiteDir")
t.Log(taskName)
if err = fileOp.TarGzExtractPro(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), GetOpenrestyDir(SitesRootDir), ""); err != nil {
t.LogFailedWithErr(taskName, err)
return err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec -i %s nginx -s reload", nginxInfo.ContainerName)
if err != nil {
return errors.New(stdout)
if err := cmd.RunDefaultBashCf("docker exec -i %s nginx -s reload", nginxInfo.ContainerName); err != nil {
return err
}
oldWebsite.ID = website.ID
if err := websiteRepo.SaveWithoutCtx(&oldWebsite); err != nil {
@@ -199,28 +187,60 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
}
isOk = true
return nil
}, nil)
return recoverTask.Execute()
}
if parentTask != nil {
return recoverWebsite(parentTask)
}
rollBackWebsite := func(t *task.Task) {
if isRollback {
return
}
if !isOk {
t.Log(i18n.GetMsgByKey("RecoverFailedStartRollBack"))
if err := handleWebsiteRecover(website, t, rollbackFile, true, "", ""); err != nil {
t.LogFailedWithErr(i18n.GetMsgByKey("Rollback"), err)
return
}
t.LogSuccess(i18n.GetMsgByKey("Rollback"))
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}
recoverTask.AddSubTask(task.GetTaskName(website.PrimaryDomain, task.TaskRecover, task.TaskScopeBackup), recoverWebsite, rollBackWebsite)
go func() {
_ = recoverTask.Execute()
}()
return nil
}
func handleWebsiteBackup(website *model.Website, parentTask *task.Task, backupDir, fileName, excludes, secret, taskID string) error {
func handleWebsiteBackup(website *model.Website, parentTask *task.Task, recordID uint, backupDir, fileName, excludes, secret, taskID string) error {
var (
err error
backupTask *task.Task
)
backupTask = parentTask
if parentTask == nil {
backupTask, err = task.NewTaskWithOps(website.Alias, task.TaskBackup, task.TaskScopeWebsite, taskID, website.ID)
backupTask, err = task.NewTaskWithOps(website.Alias, task.TaskBackup, task.TaskScopeBackup, taskID, website.ID)
if err != nil {
return err
}
}
itemHandler := func() error { return doWebsiteBackup(website, backupTask, backupDir, fileName, excludes, secret) }
backupTask.AddSubTask(task.GetTaskName(website.Alias, task.TaskBackup, task.TaskScopeWebsite), func(t *task.Task) error { return itemHandler() }, nil)
if parentTask != nil {
return itemHandler()
}
return backupTask.Execute()
backupTask.AddSubTaskWithOps(task.GetTaskName(website.Alias, task.TaskBackup, task.TaskScopeBackup), func(t *task.Task) error { return itemHandler() }, nil, 3, time.Hour)
go func() {
if err := backupTask.Execute(); err != nil {
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
return
}
backupRepo.UpdateRecordByMap(recordID, map[string]interface{}{"status": constant.StatusSuccess})
}()
return nil
}
func doWebsiteBackup(website *model.Website, parentTask *task.Task, backupDir, fileName, excludes, secret string) error {
@@ -252,7 +272,7 @@ func doWebsiteBackup(website *model.Website, parentTask *task.Task, backupDir, f
return err
}
parentTask.LogStart(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeApp))
if err = handleAppBackup(&app, parentTask, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias), excludes, "", ""); err != nil {
if err = handleAppBackup(&app, parentTask, 0, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias), excludes, "", ""); err != nil {
return err
}
parentTask.LogSuccess(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeApp))
@@ -343,7 +363,7 @@ func recoverWebsiteDatabase(t *task.Task, dbID uint, dbType, tmpPath, websiteKey
Name: db.MysqlName,
DetailName: db.Name,
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, websiteKey),
}, t, true, ""); err != nil {
}, t, true); err != nil {
t.LogFailedWithErr(taskName, err)
return err
}
+82 -90
View File
@@ -8,6 +8,7 @@ import (
"path"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
@@ -19,14 +20,16 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/alert_push"
"github.com/1Panel-dev/1Panel/agent/utils/clam"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/systemctl"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/jinzhu/copier"
"github.com/robfig/cron/v3"
)
type ClamService struct {
serviceName string
serviceName string
freshClamService string
}
type IClamService interface {
@@ -54,23 +57,31 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
var baseInfo dto.ClamBaseInfo
baseInfo.Version = "-"
baseInfo.FreshVersion = "-"
exist1, _ := systemctl.IsExist(constant.ClamServiceNameCentOs)
if exist1 {
c.serviceName = constant.ClamServiceNameCentOs
baseInfo.IsExist = true
baseInfo.IsActive, _ = systemctl.IsActive(constant.ClamServiceNameCentOs)
clamSvc, err := controller.LoadServiceName("clam")
if err != nil {
baseInfo.IsExist = false
return baseInfo, nil
}
exist2, _ := systemctl.IsExist(constant.ClamServiceNameUbuntu)
if exist2 {
c.serviceName = constant.ClamServiceNameUbuntu
c.serviceName = clamSvc
exist, _ := controller.CheckExist(clamSvc)
if exist {
baseInfo.IsExist = true
baseInfo.IsActive, _ = systemctl.IsActive(constant.ClamServiceNameUbuntu)
baseInfo.IsActive, _ = controller.CheckActive(clamSvc)
}
freshExist, _ := systemctl.IsExist(constant.FreshClamService)
freshSvc, err := controller.LoadServiceName("freshclam")
if err != nil {
baseInfo.FreshIsExist = false
return baseInfo, nil
}
c.freshClamService = freshSvc
freshExist, _ := controller.CheckExist(freshSvc)
if freshExist {
baseInfo.FreshIsExist = true
baseInfo.FreshIsActive, _ = systemctl.IsActive(constant.FreshClamService)
baseInfo.FreshIsActive, _ = controller.CheckActive(freshSvc)
}
if !cmd.Which("clamdscan") {
baseInfo.IsActive = false
}
@@ -85,7 +96,7 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
}
}
} else {
_ = clam.CheckClamIsActive(false, clamRepo)
_ = clam.StopAllClamJob(false, clamRepo)
}
if baseInfo.FreshIsActive {
version, err := cmd.RunDefaultWithStdoutBashC("freshclam --version")
@@ -103,15 +114,13 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
func (c *ClamService) Operate(operate string) error {
switch operate {
case "start", "restart", "stop":
stdout, err := cmd.RunDefaultWithStdoutBashCf("systemctl %s %s", operate, c.serviceName)
if err != nil {
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
if err := controller.Handle(operate, c.serviceName); err != nil {
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, err)
}
return nil
case "fresh-start", "fresh-restart", "fresh-stop":
stdout, err := cmd.RunDefaultWithStdoutBashCf("systemctl %s %s", strings.TrimPrefix(operate, "fresh-"), constant.FreshClamService)
if err != nil {
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
if err := controller.Handle(strings.TrimPrefix(operate, "fresh-"), c.freshClamService); err != nil {
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, err)
}
return nil
default:
@@ -238,6 +247,7 @@ func (c *ClamService) Update(req dto.ClamUpdate) error {
upMap["infected_dir"] = req.InfectedDir
upMap["infected_strategy"] = req.InfectedStrategy
upMap["spec"] = req.Spec
upMap["timeout"] = req.Timeout
upMap["description"] = req.Description
if err := clamRepo.Update(req.ID, upMap); err != nil {
return err
@@ -303,7 +313,7 @@ func (c *ClamService) Delete(req dto.ClamDelete) error {
}
func (c *ClamService) HandleOnce(id uint) error {
if active := clam.CheckClamIsActive(true, clamRepo); !active {
if active := clam.StopAllClamJob(true, clamRepo); !active {
return buserr.New("ErrClamdscanNotFound")
}
clamItem, _ := clamRepo.Get(repo.WithByID(id))
@@ -339,6 +349,9 @@ func (c *ClamService) SearchRecords(req dto.ClamLogSearch) (int64, interface{},
if clam.ID == 0 {
return 0, nil, buserr.New("ErrRecordNotFound")
}
loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
req.StartTime = req.StartTime.In(loc)
req.EndTime = req.EndTime.In(loc)
total, records, err := clamRepo.PageRecords(req.Page, req.PageSize, clamRepo.WithByClamID(req.ClamID), repo.WithByStatus(req.Status), repo.WithByCreatedAt(req.StartTime, req.EndTime))
if err != nil {
@@ -373,37 +386,13 @@ func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
filePath := ""
switch req.Name {
case "clamd":
if c.serviceName == constant.ClamServiceNameUbuntu {
filePath = "/etc/clamav/clamd.conf"
} else {
filePath = "/etc/clamd.d/scan.conf"
}
filePath = c.loadConfigPath("clamd")
case "clamd-log":
filePath = c.loadLogPath("clamd-log")
if len(filePath) != 0 {
break
}
if c.serviceName == constant.ClamServiceNameUbuntu {
filePath = "/var/log/clamav/clamav.log"
} else {
filePath = "/var/log/clamd.scan"
}
case "freshclam":
if c.serviceName == constant.ClamServiceNameUbuntu {
filePath = "/etc/clamav/freshclam.conf"
} else {
filePath = "/etc/freshclam.conf"
}
filePath = c.loadConfigPath("freshclam")
case "freshclam-log":
filePath = c.loadLogPath("freshclam-log")
if len(filePath) != 0 {
break
}
if c.serviceName == constant.ClamServiceNameUbuntu {
filePath = "/var/log/clamav/freshclam.log"
} else {
filePath = "/var/log/freshclam.log"
}
default:
return "", fmt.Errorf("not support such type")
}
@@ -426,23 +415,11 @@ func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
filePath := ""
service := ""
switch req.Name {
case "clamd":
if c.serviceName == constant.ClamServiceNameUbuntu {
service = constant.ClamServiceNameUbuntu
filePath = "/etc/clamav/clamd.conf"
} else {
service = constant.ClamServiceNameCentOs
filePath = "/etc/clamd.d/scan.conf"
}
filePath = c.loadConfigPath("clamd")
case "freshclam":
if c.serviceName == constant.ClamServiceNameUbuntu {
filePath = "/etc/clamav/freshclam.conf"
} else {
filePath = "/etc/freshclam.conf"
}
service = "clamav-freshclam.service"
filePath = c.loadConfigPath("freshclam")
default:
return fmt.Errorf("not support such type")
}
@@ -455,50 +432,65 @@ func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
_, _ = write.WriteString(req.File)
write.Flush()
_ = systemctl.Restart(service)
_ = controller.HandleRestart(c.serviceName)
return nil
}
func (c *ClamService) loadLogPath(name string) string {
confPath := ""
if name == "clamd-log" {
if c.serviceName == constant.ClamServiceNameUbuntu {
confPath = "/etc/clamav/clamd.conf"
} else {
confPath = "/etc/clamd.d/scan.conf"
}
} else {
if c.serviceName == constant.ClamServiceNameUbuntu {
confPath = "/etc/clamav/freshclam.conf"
} else {
confPath = "/etc/freshclam.conf"
}
}
if _, err := os.Stat(confPath); err != nil {
return ""
configKey := "clamd"
searchPrefix := "LogFile "
if name != "clamd-log" {
configKey = "freshclam"
searchPrefix = "UpdateLogFile "
}
confPath := c.loadConfigPath(configKey)
content, err := os.ReadFile(confPath)
if err != nil {
global.LOG.Debugf("read config of %s failed, err: %v", configKey, err)
return ""
}
lines := strings.Split(string(content), "\n")
if name == "clamd-log" {
for _, line := range lines {
if strings.HasPrefix(line, "LogFile ") {
return strings.Trim(strings.ReplaceAll(line, "LogFile ", ""), " ")
}
}
} else {
for _, line := range lines {
if strings.HasPrefix(line, "UpdateLogFile ") {
return strings.Trim(strings.ReplaceAll(line, "UpdateLogFile ", ""), " ")
}
for _, line := range lines {
if strings.HasPrefix(line, searchPrefix) {
return strings.Trim(strings.ReplaceAll(line, searchPrefix, ""), " ")
}
}
if configKey == "clamd" {
if _, err := os.Stat("/var/log/clamav/clamav.log"); err == nil {
return "/var/log/clamav/clamav.log"
}
if _, err := os.Stat("/var/log/clamd.scan"); err == nil {
return "/var/log/clamd.scan"
}
}
if configKey == "freshclam" {
if _, err := os.Stat("/var/log/clamav/freshclam.log"); err == nil {
return "/var/log/clamav/freshclam.log"
}
if _, err := os.Stat("/var/log/freshclam.log"); err == nil {
return "/var/log/freshclam.log"
}
}
return ""
}
func (c *ClamService) loadConfigPath(confType string) string {
switch confType {
case "clamd":
if _, err := os.Stat("/etc/clamav/clamd.conf"); err == nil {
return "/etc/clamav/clamd.conf"
}
return "/etc/clamd.d/scan.conf"
case "freshclam":
if _, err := os.Stat("/etc/clamav/freshclam.conf"); err == nil {
return "/etc/clamav/freshclam.conf"
}
return "/etc/freshclam.conf"
default:
return ""
}
}
func handleAlert(infectedFiles, clamName string, clamId uint) {
itemInfected, _ := strconv.Atoi(strings.TrimSpace(infectedFiles))
if itemInfected < 0 {
+19 -1
View File
@@ -2,6 +2,7 @@ package service
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/buserr"
"github.com/jinzhu/copier"
@@ -12,8 +13,9 @@ type ComposeTemplateService struct{}
type IComposeTemplateService interface {
List() ([]dto.ComposeTemplateInfo, error)
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
Create(composeDto dto.ComposeTemplateCreate) error
Create(req dto.ComposeTemplateCreate) error
Update(id uint, upMap map[string]interface{}) error
Batch(req dto.ComposeTemplateBatch) error
Delete(ids []uint) error
}
@@ -64,6 +66,22 @@ func (u *ComposeTemplateService) Create(composeDto dto.ComposeTemplateCreate) er
return nil
}
func (u *ComposeTemplateService) Batch(req dto.ComposeTemplateBatch) error {
for _, item := range req.Templates {
template, _ := composeRepo.Get(repo.WithByName(item.Name))
if template.ID == 0 {
if err := composeRepo.Create(&model.ComposeTemplate{Name: item.Name, Content: item.Content, Description: item.Description}); err != nil {
return err
}
} else {
if err := composeRepo.Update(template.ID, map[string]interface{}{"content": item.Content, "description": item.Description}); err != nil {
return err
}
}
}
return nil
}
func (u *ComposeTemplateService) Delete(ids []uint) error {
if len(ids) == 1 {
compose, _ := composeRepo.Get(repo.WithByID(ids[0]))
+92 -49
View File
@@ -12,7 +12,6 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
@@ -34,6 +33,8 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
@@ -105,7 +106,8 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
}
defer client.Close()
options := container.ListOptions{
All: true,
All: true,
Size: true,
}
if len(req.Filters) != 0 {
options.Filters = filters.NewArgs()
@@ -199,6 +201,8 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
Ports: exposePorts,
IsFromApp: IsFromApp,
IsFromCompose: IsFromCompose,
SizeRw: item.SizeRw,
SizeRootFs: item.SizeRootFs,
}
install, _ := appInstallRepo.GetFirst(appInstallRepo.WithContainerName(info.Name))
if install.ID > 0 {
@@ -282,11 +286,35 @@ func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
}
defer client.Close()
c := context.Background()
images, _ := client.ImageList(c, image.ListOptions{})
for _, image := range images {
data.ImageSize += uint64(image.Size)
usage, err := client.DiskUsage(c, types.DiskUsageOptions{})
if err != nil {
return data, err
}
data.ImageCount = len(usage.Images)
data.VolumeCount = len(usage.Volumes)
for _, item := range usage.Images {
data.ImageUsage += item.Size
if item.Containers < 1 {
data.ImageReclaimable += item.Size
}
}
for _, item := range usage.Containers {
data.ContainerUsage += item.SizeRw
if item.State != "running" {
data.ContainerReclaimable += item.SizeRw
}
}
for _, item := range usage.Volumes {
data.VolumeUsage += item.UsageData.Size
if item.UsageData.RefCount == 0 {
data.VolumeReclaimable += item.UsageData.Size
}
}
for _, item := range usage.BuildCache {
data.BuildCacheUsage += item.Size
}
data.ImageCount = len(images)
repo, _ := imageRepoRepo.List()
data.RepoCount = len(repo)
templates, _ := composeRepo.List()
@@ -296,12 +324,8 @@ func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
volumes, _ := client.VolumeList(c, volume.ListOptions{})
data.VolumeCount = len(volumes.Volumes)
data.ComposeCount = loadComposeCount(client)
containers, err := client.ContainerList(c, container.ListOptions{All: true})
if err != nil {
return data, err
}
data.All = uint(len(containers))
for _, item := range containers {
data.ContainerCount = len(usage.Containers)
for _, item := range usage.Containers {
switch item.State {
case "created":
data.Created++
@@ -319,7 +343,6 @@ func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
data.Removing++
}
}
data.ContainerCount = int(data.All)
return data, nil
}
func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error) {
@@ -478,6 +501,7 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) error {
pruneFilters.Add("until", "24h")
}
}
taskItem.Log(i18n.GetMsgByKey("PruneStart"))
DeletedNumber := 0
SpaceReclaimed := 0
switch req.PruneType {
@@ -640,7 +664,6 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
}
var data dto.ContainerOperate
data.ContainerID = oldContainer.ID
data.Name = strings.ReplaceAll(oldContainer.Name, "/", "")
data.Image = oldContainer.Config.Image
if oldContainer.NetworkSettings != nil {
@@ -702,11 +725,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
}
defer client.Close()
ctx := context.Background()
newContainer, _ := client.ContainerInspect(ctx, req.Name)
if newContainer.ContainerJSONBase != nil && newContainer.ID != req.ContainerID {
return buserr.New("ErrContainerName")
}
oldContainer, err := client.ContainerInspect(ctx, req.ContainerID)
oldContainer, err := client.ContainerInspect(ctx, req.Name)
if err != nil {
return err
}
@@ -730,7 +749,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
}, nil)
taskItem.AddSubTask(i18n.GetWithName("ContainerCreate", req.Name), func(t *task.Task) error {
err := client.ContainerRemove(ctx, req.ContainerID, container.RemoveOptions{Force: true})
err := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true})
taskItem.LogWithStatus(i18n.GetWithName("ContainerRemoveOld", req.Name), err)
if err != nil {
return err
@@ -900,25 +919,36 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
return err
}
defer client.Close()
for _, item := range req.Names {
global.LOG.Infof("start container %s operation %s", item, req.Operation)
switch req.Operation {
case constant.ContainerOpStart:
err = client.ContainerStart(ctx, item, container.StartOptions{})
case constant.ContainerOpStop:
err = client.ContainerStop(ctx, item, container.StopOptions{})
case constant.ContainerOpRestart:
err = client.ContainerRestart(ctx, item, container.StopOptions{})
case constant.ContainerOpKill:
err = client.ContainerKill(ctx, item, "SIGKILL")
case constant.ContainerOpPause:
err = client.ContainerPause(ctx, item)
case constant.ContainerOpUnpause:
err = client.ContainerUnpause(ctx, item)
case constant.ContainerOpRemove:
err = client.ContainerRemove(ctx, item, container.RemoveOptions{RemoveVolumes: true, Force: true})
}
taskItem, err := task.NewTaskWithOps(strings.Join(req.Names, " "), req.Operation, task.TaskScopeContainer, req.TaskID, 1)
if err != nil {
return fmt.Errorf("new task for container commit failed, err: %v", err)
}
for _, item := range req.Names {
taskItem.AddSubTask(item, func(t *task.Task) error {
switch req.Operation {
case constant.ContainerOpStart:
err = client.ContainerStart(ctx, item, container.StartOptions{})
case constant.ContainerOpStop:
err = client.ContainerStop(ctx, item, container.StopOptions{})
case constant.ContainerOpRestart:
err = client.ContainerRestart(ctx, item, container.StopOptions{})
case constant.ContainerOpKill:
err = client.ContainerKill(ctx, item, "SIGKILL")
case constant.ContainerOpPause:
err = client.ContainerPause(ctx, item)
case constant.ContainerOpUnpause:
err = client.ContainerUnpause(ctx, item)
case constant.ContainerOpRemove:
err = client.ContainerRemove(ctx, item, container.RemoveOptions{RemoveVolumes: true, Force: true})
}
return err
}, nil)
}
go func() {
_ = taskItem.Execute()
}()
return err
}
@@ -1137,7 +1167,7 @@ func (u *ContainerService) DownloadContainerLogs(containerType, container, since
errCh := make(chan error)
go func() {
scanner := bufio.NewScanner(stdout)
var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;?]*[A-Za-z]|\x1b=|\x1b>`)
var ansiRegex = re.GetRegex(re.AnsiEscapePattern)
for scanner.Scan() {
line := scanner.Text()
cleanLine := ansiRegex.ReplaceAllString(line, "")
@@ -1227,6 +1257,16 @@ func stringsToMap(list []string) map[string]string {
}
return labelMap
}
func stringsToMap2(list []string) map[string]*string {
var labelMap = make(map[string]*string)
for _, label := range list {
if strings.Contains(label, "=") {
sps := strings.SplitN(label, "=", 2)
labelMap[sps[0]] = &sps[1]
}
}
return labelMap
}
func calculateCPUPercentUnix(stats *container.StatsResponse) float64 {
cpuPercent := 0.0
@@ -1503,17 +1543,19 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *conta
hostConf.DNS = req.DNS
config.Volumes = make(map[string]struct{})
for _, volume := range req.Volumes {
if volume.Type == "volume" {
hostConf.Mounts = append(hostConf.Mounts, mount.Mount{
Type: mount.Type(volume.Type),
Source: volume.SourceDir,
Target: volume.ContainerDir,
ReadOnly: volume.Mode == "ro",
})
config.Volumes[volume.ContainerDir] = struct{}{}
} else {
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
item := mount.Mount{
Type: mount.Type(volume.Type),
Source: volume.SourceDir,
Target: volume.ContainerDir,
ReadOnly: volume.Mode == "ro",
}
if volume.Type == "bind" {
item.BindOptions = &mount.BindOptions{
Propagation: mount.Propagation(volume.Shared),
}
}
hostConf.Mounts = append(hostConf.Mounts, item)
config.Volumes[volume.ContainerDir] = struct{}{}
}
return &config, &hostConf, &networkConf, nil
}
@@ -1555,6 +1597,7 @@ func loadVolumeBinds(binds []container.MountPoint) []dto.VolumeHelper {
if bind.RW {
volumeItem.Mode = "rw"
}
volumeItem.Shared = string(bind.Propagation)
datas = append(datas, volumeItem)
}
return datas
+25 -3
View File
@@ -36,6 +36,7 @@ type ICronjobService interface {
Delete(req dto.CronjobBatchDelete) error
StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)
CleanRecord(req dto.CronjobClean) error
HandleStop(id uint) error
Export(req dto.OperateByIDs) (string, error)
Import(req []dto.CronjobTrans) error
@@ -107,6 +108,9 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
if cronjob.Type == "snapshot" && len(cronjob.SnapshotRule) != 0 {
_ = json.Unmarshal([]byte(cronjob.SnapshotRule), &item.SnapshotRule)
}
if cronjob.Type == "cleanLog" {
_ = json.Unmarshal([]byte(cronjob.Config), &item.CleanLogConfig)
}
return &item, err
}
@@ -499,8 +503,8 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
if hasBackup(cronjob.Type) {
accountMap := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if !req.CleanRemoteData {
for key := range accountMap {
if key != constant.Local {
for key, val := range accountMap {
if val.accountType != constant.Local {
delete(accountMap, key)
}
}
@@ -576,6 +580,10 @@ func (u *CronjobService) Create(req dto.CronjobOperate) error {
}
cronjob.Spec = spec
cronjob.EntryIDs = entryIDs
if req.Type == "cleanLog" {
config, _ := json.Marshal(req.CleanLogConfig)
cronjob.Config = string(config)
}
if err := cronjobRepo.Create(&cronjob); err != nil {
return err
}
@@ -604,7 +612,7 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob, isUpdate bool) (string
global.Cron.Remove(cron.EntryID(idItem))
}
}
specs := strings.Split(cronjob.Spec, ",")
specs := strings.Split(cronjob.Spec, "&&")
var ids []string
for _, spec := range specs {
cronjob.Spec = spec
@@ -617,6 +625,20 @@ func (u *CronjobService) StartJob(cronjob *model.Cronjob, isUpdate bool) (string
return strings.Join(ids, ","), nil
}
func (u *CronjobService) HandleStop(id uint) error {
record, _ := cronjobRepo.GetRecord(repo.WithByID(id))
if record.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
if len(record.TaskID) == 0 {
return nil
}
if cancle, ok := global.TaskCtxMap[record.TaskID]; ok {
cancle()
}
return nil
}
func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
for _, id := range req.IDs {
cronjob, _ := cronjobRepo.Get(repo.WithByID(id))
+8 -2
View File
@@ -38,6 +38,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
retry := 0
taskItem.AddSubTaskWithOps(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
var record model.BackupRecord
record.Status = constant.StatusSuccess
record.From = "cronjob"
record.Type = "app"
record.CronjobID = cronjob.ID
@@ -95,6 +96,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
retry := 0
taskItem.AddSubTaskWithOps(task.GetTaskName(web.Alias, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
var record model.BackupRecord
record.Status = constant.StatusSuccess
record.From = "cronjob"
record.Type = "website"
record.CronjobID = cronjob.ID
@@ -154,6 +156,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
itemName := fmt.Sprintf("%s[%s] - %s", dbInfo.Database, dbInfo.DBType, dbInfo.Name)
taskItem.AddSubTaskWithOps(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
var record model.BackupRecord
record.Status = constant.StatusSuccess
record.From = "cronjob"
record.Type = dbInfo.DBType
record.CronjobID = cronjob.ID
@@ -164,7 +167,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
backupDir := path.Join(global.Dir.LocalBackupDir, fmt.Sprintf("tmp/database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
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 err := doMysqlBackup(dbInfo, backupDir, record.FileName, cronjob.Secret); err != nil {
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
@@ -175,7 +178,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
}
}
} else {
if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName); err != nil {
if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName, cronjob.Secret); err != nil {
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
@@ -238,6 +241,7 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
}
}
var record model.BackupRecord
record.Status = constant.StatusSuccess
record.From = "cronjob"
record.Type = "directory"
record.CronjobID = cronjob.ID
@@ -273,6 +277,7 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
return err
}
var record model.BackupRecord
record.Status = constant.StatusSuccess
record.From = "cronjob"
record.Type = "log"
record.CronjobID = cronjob.ID
@@ -301,6 +306,7 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
return errors.New(i18n.GetMsgWithDetail("LoadBackupFailed", accountMap[fmt.Sprintf("%d", cronjob.DownloadAccountID)].message))
}
var record model.BackupRecord
record.Status = constant.StatusSuccess
record.From = "cronjob"
record.Type = "snapshot"
record.CronjobID = cronjob.ID
+137 -6
View File
@@ -1,11 +1,16 @@
package service
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path"
pathUtils "path"
"path/filepath"
"strings"
"time"
@@ -127,12 +132,17 @@ 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 "syncIpGroup":
u.handleSyncIpGroup(*cronjob, taskItem)
case "cleanLog":
u.handleCleanLog(*cronjob, taskItem)
}
return err
}
func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task) {
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithContext(taskItem.TaskCtx))
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error {
if len(cronjob.ContainerName) != 0 {
scriptItem := cronjob.Script
@@ -147,17 +157,21 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task)
command = cronjob.Command
}
if len(cronjob.User) != 0 {
return cmdMgr.Run("docker", "exec", "-u", cronjob.User, cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
return cmdMgr.Run("docker", "exec", "-u", cronjob.User, cronjob.ContainerName, command, "-c", scriptItem)
}
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", scriptItem)
}
if len(cronjob.Executor) == 0 {
cronjob.Executor = "bash"
}
if cronjob.ScriptMode == "input" {
fileItem := pathUtils.Join(global.Dir.DataDir, "task", "shell", cronjob.Name, cronjob.Name+".sh")
suffix := ".sh"
if strings.HasPrefix(cronjob.Executor, "python") {
suffix = ".py"
}
fileItem := pathUtils.Join(global.Dir.DataDir, "task", "shell", cronjob.Name, cronjob.Name+suffix)
_ = os.MkdirAll(pathUtils.Dir(fileItem), os.ModePerm)
shellFile, err := os.OpenFile(fileItem, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)
shellFile, err := os.OpenFile(fileItem, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.DirPerm)
if err != nil {
return err
}
@@ -205,6 +219,116 @@ func (u *CronjobService) handleNtpSync(cronjob model.Cronjob, taskItem *task.Tas
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
func (u *CronjobService) handleCleanLog(cronjob model.Cronjob, taskItem *task.Task) {
taskItem.AddSubTaskWithOps(i18n.GetWithName("CleanLog", cronjob.Name), func(t *task.Task) error {
config := GetCleanLogConfig(cronjob)
for _, scope := range config.Scopes {
switch scope {
case "website":
websites, _ := websiteRepo.List()
for _, website := range websites {
curStr := i18n.GetWithName("CleanLogByName", website.PrimaryDomain)
t.LogStart(curStr)
accessLogPath := GetSitePath(website, SiteAccessLog)
if err := os.Truncate(accessLogPath, 0); err != nil {
t.LogFailedWithErr(curStr, err)
continue
}
errLogPath := GetSitePath(website, SiteErrorLog)
if err := os.Truncate(errLogPath, 0); err != nil {
t.LogFailedWithErr(curStr, err)
continue
}
t.LogSuccess(curStr)
}
appInstall, _ := getAppInstallByKey(constant.AppOpenresty)
if appInstall.ID > 0 {
curStr := i18n.GetWithName("CleanLogByName", "OpenResty")
t.LogStart(curStr)
accessLogPath := path.Join(appInstall.GetPath(), "log", "access.log")
if err := os.Truncate(accessLogPath, 0); err != nil {
t.LogFailedWithErr(curStr, err)
}
errLogPath := path.Join(appInstall.GetPath(), "log", "error.log")
if err := os.Truncate(errLogPath, 0); err != nil {
t.LogFailedWithErr(curStr, err)
}
t.LogSuccess(curStr)
}
}
}
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
func (u *CronjobService) handleSyncIpGroup(cronjob model.Cronjob, taskItem *task.Task) {
taskItem.AddSubTaskWithOps(i18n.GetWithName("SyncIpGroup", cronjob.Name), func(t *task.Task) error {
appInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
ipGroupDir := path.Join(appInstall.GetPath(), "1pwaf", "data", "rules", "ip_group")
urlDir := path.Join(ipGroupDir, "ip_group_url")
urlsFiles, err := os.ReadDir(urlDir)
if err != nil {
return err
}
for _, file := range urlsFiles {
if file.IsDir() || !strings.HasSuffix(file.Name(), "_url") {
continue
}
urlFilePath := filepath.Join(urlDir, file.Name())
urlContent, err := os.ReadFile(urlFilePath)
if err != nil {
continue
}
remoteURL := strings.TrimSpace(string(urlContent))
if remoteURL == "" {
continue
}
resp, err := http.Get(remoteURL)
if err != nil {
continue
}
ipGroupFile := strings.TrimSuffix(file.Name(), "_url")
ipGroupPath := filepath.Join(ipGroupDir, ipGroupFile)
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
taskItem.Logf("get remote ip group %s failed %s status code %d", ipGroupFile, remoteURL, resp.StatusCode)
continue
}
outFile, err := os.OpenFile(ipGroupPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
resp.Body.Close()
taskItem.Logf("sync %s failed %v", ipGroupFile, err)
continue
}
writer := bufio.NewWriter(outFile)
_, err = io.Copy(writer, resp.Body)
if err != nil {
outFile.Close()
resp.Body.Close()
taskItem.Logf("sync %s failed , write file failed %v", ipGroupFile, err)
continue
}
writer.Flush()
outFile.Close()
resp.Body.Close()
taskItem.LogSuccess(i18n.Get("TaskSync") + " " + ipGroupFile)
}
if err := opNginx(appInstall.ContainerName, constant.NginxReload); err != nil {
return err
}
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
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 {
@@ -217,6 +341,7 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t
for _, website := range websites {
taskItem.Log(website.Alias)
var record model.BackupRecord
record.Status = constant.StatusSuccess
record.From = "cronjob"
record.Type = "cut-website-log"
record.CronjobID = cronjob.ID
@@ -337,7 +462,7 @@ func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {
_ = os.Remove(file)
}
}
_ = cronjobRepo.DeleteRecord(repo.WithByID(uint(records[i].ID)))
_ = cronjobRepo.DeleteRecord(repo.WithByID(records[i].ID))
_ = taskRepo.Delete(taskRepo.WithByID(records[i].TaskID))
_ = os.Remove(path.Join(global.CONF.Base.InstallDir, "1panel/log/task/Cronjob", records[i].TaskID+".log"))
}
@@ -356,3 +481,9 @@ func handleCronJobAlert(cronjob *model.Cronjob) {
}
_ = alert_push.PushAlert(pushAlert)
}
func GetCleanLogConfig(cronJob model.Cronjob) dto.CleanLogConfig {
config := &dto.CleanLogConfig{}
_ = json.Unmarshal([]byte(cronJob.Config), config)
return *config
}
+27 -20
View File
@@ -21,6 +21,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
"github.com/1Panel-dev/1Panel/agent/utils/copier"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/v4/cpu"
@@ -53,23 +54,25 @@ func NewIDashboardService() IDashboardService {
}
func (u *DashboardService) Restart(operation string) error {
if operation != "1panel" && operation != "system" && operation != "1panel-agent" {
switch operation {
case "system":
{
go func() {
if err := cmd.RunDefaultBashCf("%s reboot", cmd.SudoHandleCmd()); err != nil {
global.LOG.Errorf("handle reboot failed, %v", err)
}
}()
return nil
}
case "1panel-agent":
controller.RestartPanel(false, true, false)
return nil
case "1panel":
controller.RestartPanel(true, true, false)
return nil
default:
return fmt.Errorf("handle restart operation %s failed, err: nonsupport such operation", operation)
}
itemCmd := fmt.Sprintf("%s systemctl restart 1panel-agent.service && %s systemctl restart 1panel-core.service", cmd.SudoHandleCmd(), cmd.SudoHandleCmd())
if operation == "system" {
itemCmd = fmt.Sprintf("%s reboot", cmd.SudoHandleCmd())
}
if operation == "1panel-agent" {
itemCmd = fmt.Sprintf("%s systemctl restart 1panel-agent.service", cmd.SudoHandleCmd())
}
go func() {
stdout, err := cmd.RunDefaultWithStdoutBashC(itemCmd)
if err != nil {
global.LOG.Errorf("handle %s failed, err: %v", itemCmd, stdout)
}
}()
return nil
}
func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {
@@ -117,7 +120,7 @@ func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryAvailable = memoryInfo.Available
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory()
@@ -191,7 +194,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryFree = memoryInfo.Free
currentInfo.MemoryCache = memoryInfo.Cached + memoryInfo.Buffers
currentInfo.MemoryShard = memoryInfo.Shared
@@ -208,6 +211,9 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.GPUData = loadGPUInfo()
currentInfo.XPUData = loadXpuInfo()
currentInfo.TopCPUItems = loadTopCPU()
currentInfo.TopMemItems = loadTopMem()
if ioOption == "all" {
diskInfo, _ := disk.IOCounters()
for _, state := range diskInfo {
@@ -401,10 +407,11 @@ type diskInfo struct {
func loadDiskInfo() []dto.DiskInfo {
var datas []dto.DiskInfo
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
stdout, err := cmdMgr.RunWithStdoutBashC("timeout 2 df -hT -P | awk 'NR>1 && !/tmpfs|snap\\/core|udev/ {print}'")
format := `awk 'NR>1 && !/tmpfs|snap\/core|udev/ {printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", $1, $2, $3, $4, $5, $6, $7}'`
stdout, err := cmdMgr.RunWithStdout("bash", "-c", `timeout 2 df -hT -P | `+format)
if err != nil {
cmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
stdout, err = cmdMgr2.RunWithStdoutBashC("timeout 1 df -lhT -P | awk 'NR>1 && !/tmpfs|snap\\/core|udev/ {print}'")
stdout, err = cmdMgr2.RunWithStdout("bash", "-c", `timeout 1 df -lhT -P | `+format)
if err != nil {
return datas
}
@@ -414,7 +421,7 @@ func loadDiskInfo() []dto.DiskInfo {
var mounts []diskInfo
var excludes = []string{"/mnt/cdrom", "/boot", "/boot/efi", "/dev", "/dev/shm", "/run/lock", "/run", "/run/shm", "/run/user"}
for _, line := range lines {
fields := strings.Fields(line)
fields := strings.Split(line, "\t")
if len(fields) < 7 {
continue
}
+7 -6
View File
@@ -122,12 +122,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
switch req.Type {
case constant.AppPostgresql:
_, err := postgresql.NewPostgresqlClient(pgclient.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
From: "remote",
Address: req.Address,
Port: req.Port,
InitialDB: req.InitialDB,
Username: req.Username,
Password: req.Password,
Timeout: req.Timeout,
})
return err == nil
case constant.AppRedis:
+2 -2
View File
@@ -7,7 +7,6 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
@@ -24,6 +23,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/compose"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/mysql"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"github.com/1Panel-dev/1Panel/agent/utils/mysql/client"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/copier"
@@ -623,7 +623,7 @@ func updateMyCnf(oldFiles []string, group string, param string, value interface{
isOn := false
hasGroup := false
hasKey := false
regItem, _ := regexp.Compile(`\[*\]`)
regItem := re.GetRegex(re.MysqlGroupPattern)
var newFiles []string
i := 0
for _, line := range oldFiles {
+52 -39
View File
@@ -2,7 +2,6 @@ package service
import (
"bufio"
"errors"
"fmt"
"net"
"os"
@@ -12,11 +11,14 @@ import (
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"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/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
"github.com/shirou/gopsutil/v4/mem"
)
@@ -120,7 +122,7 @@ func (u *DeviceService) Update(key, value string) error {
if err := ntp.UpdateSystemTimeZone(value); err != nil {
return err
}
go common.RestartService(global.IsMaster, true, false)
go controller.RestartPanel(global.IsMaster, true, false)
case "DNS":
if err := updateDNS(strings.Split(value, ",")); err != nil {
return err
@@ -129,9 +131,8 @@ func (u *DeviceService) Update(key, value string) error {
if cmd.CheckIllegal(value) {
return buserr.New("ErrCmdIllegal")
}
std, err := cmd.RunDefaultWithStdoutBashCf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value)
if err != nil {
return errors.New(std)
if err := cmd.RunDefaultBashCf("%s hostnamectl set-hostname %s", cmd.SudoHandleCmd(), value); err != nil {
return err
}
case "Ntp", "LocalTime":
if cmd.CheckIllegal(value) {
@@ -220,51 +221,63 @@ func (u *DeviceService) UpdatePasswd(req dto.ChangePasswd) error {
if cmd.CheckIllegal(req.User, req.Passwd) {
return buserr.New("ErrCmdIllegal")
}
std, err := cmd.RunDefaultWithStdoutBashCf("%s echo '%s:%s' | %s chpasswd", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd())
if err != nil {
if err := cmd.RunDefaultBashCf("%s echo '%s:%s' | %s chpasswd", cmd.SudoHandleCmd(), req.User, req.Passwd, cmd.SudoHandleCmd()); err != nil {
if strings.Contains(err.Error(), "does not exist") {
return buserr.New("ErrNotExistUser")
}
return errors.New(std)
return err
}
return nil
}
func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
if cmd.CheckIllegal(req.Path) {
return buserr.New("ErrCmdIllegal")
}
if !req.IsNew {
std, err := cmd.RunDefaultWithStdoutBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle swapoff %s failed, err: %s", req.Path, std)
}
}
if req.Size == 0 {
if req.Path == path.Join(global.Dir.BaseDir, ".1panel_swap") {
_ = os.Remove(path.Join(global.Dir.BaseDir, ".1panel_swap"))
}
return operateSwapWithFile(true, req)
}
stdDD, err := cmd.RunDefaultWithStdoutBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
taskItem, err := task.NewTaskWithOps(req.Path, task.TaskSwapSet, task.TaskScopeSystem, req.TaskID, 1)
if err != nil {
return fmt.Errorf("handle dd %s failed, std: %s, err: %s", req.Path, stdDD, err)
}
stdChmod, err := cmd.RunDefaultWithStdoutBashCf("%s chmod 0600 %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle chmod 0600 %s failed,std: %s, err: %s", req.Path, stdChmod, err)
}
stdMkswap, err := cmd.RunDefaultWithStdoutBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle mkswap -f %s failed, std: %s, err: %s", req.Path, stdMkswap, err)
global.LOG.Errorf("new task for create container failed, err: %v", err)
return err
}
stdSwapon, err := cmd.RunDefaultWithStdoutBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
_, _ = cmd.RunDefaultWithStdoutBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
return fmt.Errorf("handle swapoff %s failed,std: %s, err: %s", req.Path, stdSwapon, err)
}
return operateSwapWithFile(false, req)
taskItem.AddSubTask(i18n.GetMsgWithMap("SetSwap", map[string]interface{}{"path": req.Path, "size": common.LoadSizeUnit2F(float64(req.Size * 1024))}), func(t *task.Task) error {
if cmd.CheckIllegal(req.Path) {
return buserr.New("ErrCmdIllegal")
}
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
if !req.IsNew {
if err := cmdMgr.RunBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path); err != nil {
return fmt.Errorf("handle swapoff %s failed, %v", req.Path, err)
}
}
if req.Size == 0 {
if req.Path == path.Join(global.Dir.BaseDir, ".1panel_swap") {
_ = os.Remove(path.Join(global.Dir.BaseDir, ".1panel_swap"))
}
return operateSwapWithFile(true, req)
}
taskItem.LogStart(i18n.GetMsgByKey("CreateSwap"))
if err := cmdMgr.RunBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size); err != nil {
return fmt.Errorf("handle dd %s failed, %v", req.Path, err)
}
taskItem.Log("chmod 0600 " + req.Path)
if err := cmdMgr.RunBashCf("%s chmod 0600 %s", cmd.SudoHandleCmd(), req.Path); err != nil {
return fmt.Errorf("handle chmod 0600 %s failed, %v", req.Path, err)
}
taskItem.LogStart(i18n.GetMsgByKey("FormatSwap"))
if err := cmdMgr.RunBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path); err != nil {
return fmt.Errorf("handle mkswap -f %s failed, %v", req.Path, err)
}
taskItem.LogStart(i18n.GetMsgByKey("EnableSwap"))
if err := cmdMgr.RunBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path); err != nil {
_ = cmdMgr.RunBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
return fmt.Errorf("handle swapoff %s failed, %v", req.Path, err)
}
return operateSwapWithFile(false, req)
}, nil)
go func() {
_ = taskItem.Execute()
}()
return nil
}
func (u *DeviceService) LoadConf(name string) (string, error) {
+78 -12
View File
@@ -7,17 +7,20 @@ import (
"path"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/controller"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/filters"
"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/global"
@@ -74,8 +77,21 @@ func (u *DeviceService) Scan() dto.CleanData {
sort.Slice(upgradeTree.Children, func(i, j int) bool {
return common.CompareVersion(upgradeTree.Children[i].Label, upgradeTree.Children[j].Label)
})
upgradeTree.Children[0].IsCheck = false
upgradeTree.Children[0].IsRecommend = false
if global.IsMaster {
var copiesSeeting model.Setting
_ = global.CoreDB.Where("key = ?", "UpgradeBackupCopies").First(&copiesSeeting).Error
copies, _ := strconv.Atoi(copiesSeeting.Value)
if copies == 0 || copies > len(upgradeTree.Children) {
copies = len(upgradeTree.Children)
}
for i := 0; i < copies; i++ {
upgradeTree.Children[i].IsCheck = false
upgradeTree.Children[i].IsRecommend = false
}
} else {
upgradeTree.Children[0].IsCheck = false
upgradeTree.Children[0].IsRecommend = false
}
}
treeData = append(treeData, upgradeTree)
@@ -214,7 +230,7 @@ func (u *DeviceService) Clean(req []dto.Clean) {
}
case "task_log":
if len(item.Name) == 0 {
files, _ := os.ReadDir(path.Join(global.Dir.TaskDir))
files, _ := os.ReadDir(global.Dir.TaskDir)
if len(files) == 0 {
continue
}
@@ -222,15 +238,10 @@ func (u *DeviceService) Clean(req []dto.Clean) {
if file.Name() == "ssl" || !file.IsDir() {
continue
}
dropFileOrDir(path.Join(global.Dir.TaskDir, file.Name()))
dropTaskLog(path.Join(global.Dir.TaskDir, file.Name()))
}
_ = taskRepo.DeleteAll()
} else {
pathItem := path.Join(global.Dir.TaskDir, item.Name)
dropFileOrDir(pathItem)
if len(item.Name) != 0 {
_ = taskRepo.Delete(repo.WithByType(item.Name))
}
dropTaskLog(path.Join(global.Dir.TaskDir, item.Name))
}
case "script":
dropFileOrDir(path.Join(global.Dir.TmpDir, "script", item.Name))
@@ -250,7 +261,7 @@ func (u *DeviceService) Clean(req []dto.Clean) {
_ = settingRepo.Update("LastCleanData", fmt.Sprintf("%v", len(req)))
if restart {
go common.RestartService(false, true, false)
go controller.RestartPanel(false, true, false)
}
}
@@ -530,7 +541,6 @@ func loadTreeWithAllFile(isCheck bool, originalPath, treeType, pathItem string,
}
func dropFileOrDir(itemPath string) {
global.LOG.Debugf("drop file %s", itemPath)
if err := os.RemoveAll(itemPath); err != nil {
global.LOG.Errorf("drop file %s failed, err %v", itemPath, err)
}
@@ -606,6 +616,62 @@ func dropVolumes() (int, int) {
return len(res.VolumesDeleted), int(res.SpaceReclaimed)
}
func dropTaskLog(logDir string) {
files, err := os.ReadDir(logDir)
if err != nil {
return
}
taskType := path.Base(logDir)
var usedTasks []string
switch taskType {
case "Cronjob":
_ = global.DB.Model(&model.JobRecords{}).Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
case "Snapshot":
var (
snapIDs []string
recoverIDs []string
rollbackIDs []string
)
_ = global.DB.Model(&model.Snapshot{}).Where("task_id != ?", "").Select("task_id").Find(&snapIDs).Error
_ = global.DB.Model(&model.Snapshot{}).Where("task_recover_id != ", "").Select("task_id").Find(&recoverIDs).Error
_ = global.DB.Model(&model.Snapshot{}).Where("task_rollback_id != ?", "").Select("task_id").Find(&rollbackIDs).Error
usedTasks = append(usedTasks, snapIDs...)
usedTasks = append(usedTasks, recoverIDs...)
usedTasks = append(usedTasks, rollbackIDs...)
case "Backup":
_ = global.DB.Model(&model.BackupRecord{}).Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
case "Clam":
_ = global.DB.Model(&model.ClamRecord{}).Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
case "Tamper":
xpackDB, err := common.LoadDBConnByPathWithErr(path.Join(global.CONF.Base.InstallDir, "1panel/db/xpack.db"), "xpack.db")
if err == nil {
_ = xpackDB.Table("tampers").Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
}
case "System":
xpackDB, err := common.LoadDBConnByPathWithErr(path.Join(global.CONF.Base.InstallDir, "1panel/db/xpack.db"), "xpack.db")
if err == nil {
_ = xpackDB.Model("nodes").Where("task_id != ?", "").Select("task_id").Find(&usedTasks).Error
}
default:
dropFileOrDir(logDir)
_ = taskRepo.Delete(repo.WithByType(taskType))
return
}
usedMap := make(map[string]struct{})
for _, item := range usedTasks {
if _, ok := usedMap[item]; !ok {
usedMap[item] = struct{}{}
}
}
for _, item := range files {
if _, ok := usedMap[strings.TrimSuffix(item.Name(), ".log")]; ok {
continue
}
_ = os.Remove(logDir + "/" + item.Name())
}
_ = taskRepo.Delete(repo.WithByType(taskType), taskRepo.WithByIDNotIn(usedTasks))
}
func dropWithExclude(pathToDelete string, excludeSubDirs []string, taskItem *task.Task, size *int64, count *int) {
entries, err := os.ReadDir(pathToDelete)
if err != nil {
+16 -8
View File
@@ -28,14 +28,22 @@ func NewIDiskService() IDiskService {
}
func (s *DiskService) GetCompleteDiskInfo() (*response.CompleteDiskInfo, error) {
output, err := cmd.RunDefaultWithStdoutBashC("lsblk -P -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
if err != nil {
return nil, fmt.Errorf("failed to execute lsblk command: %v", err)
}
diskInfos, err := parseLsblkOutput(output)
if err != nil {
return nil, fmt.Errorf("failed to parse lsblk output: %v", err)
var diskInfos []response.DiskBasicInfo
output, err := cmd.RunDefaultWithStdoutBashC("lsblk -J -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
if err == nil {
diskInfos, err = parseLsblkJsonOutput(output)
if err != nil {
return nil, fmt.Errorf("failed to parse lsblk output: %v", err)
}
} else {
output, err = cmd.RunDefaultWithStdoutBashC("lsblk -P -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,TRAN,ROTA")
if err != nil {
return nil, fmt.Errorf("failed to run lsblk command: %v", err)
}
diskInfos, err = parseLsblkOutput(output)
if err != nil {
return nil, fmt.Errorf("failed to parse lsblk output: %v", err)
}
}
result := organizeDiskInfo(diskInfos)
+151 -21
View File
@@ -2,21 +2,146 @@ package service
import (
"bufio"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/agent/utils/re"
"os"
"os/exec"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
)
type LsblkRaw struct {
Blockdevices []LsblkDevice `json:"blockdevices"`
}
type LsblkDevice struct {
Name string `json:"name"`
Size string `json:"size"`
Type string `json:"type"`
Mountpoint string `json:"mountpoint"`
Fstype string `json:"fstype"`
Model string `json:"model"`
Serial string `json:"serial"`
Tran string `json:"tran"`
Rota bool `json:"rota"`
Children []LsblkDevice `json:"children"`
}
func parseDevice(dev LsblkDevice) []response.DiskBasicInfo {
var list []response.DiskBasicInfo
if strings.HasPrefix(dev.Name, "loop") || strings.HasPrefix(dev.Name, "dm-") || dev.Type == "rom" {
return list
}
if dev.Type == "lvm" {
return list
}
diskType := "Unknown"
if dev.Type == "disk" || dev.Type == "part" {
if dev.Rota {
diskType = "HDD"
} else {
diskType = "SSD"
}
}
mountPoint := dev.Mountpoint
filesystem := dev.Fstype
size := dev.Size
var used, avail, totalSize string
var usePercent int
isMounted := mountPoint != ""
isSystem := false
if dev.Fstype == "LVM2_member" && len(dev.Children) > 0 {
for _, child := range dev.Children {
if child.Type == "lvm" && child.Mountpoint != "" {
devicePath := "/dev/mapper/" + child.Name
totalSize, used, avail, usePercent, _ := getDiskUsageInfo(devicePath)
childInfo := response.DiskBasicInfo{
Device: dev.Name,
Size: totalSize,
Model: dev.Model,
DiskType: diskType,
Filesystem: child.Fstype,
MountPoint: child.Mountpoint,
IsMounted: true,
UsePercent: usePercent,
Used: used,
Avail: avail,
Serial: dev.Serial,
IsRemovable: dev.Tran == "usb",
IsSystem: isSystemDisk(child.Mountpoint),
}
list = append(list, childInfo)
}
}
return list
} else if isMounted {
isSystem = isSystemDisk(mountPoint)
devicePath := "/dev/" + dev.Name
totalSize, used, avail, usePercent, _ = getDiskUsageInfo(devicePath)
if totalSize != "" {
size = totalSize
}
}
info := response.DiskBasicInfo{
Device: dev.Name,
Size: size,
Model: dev.Model,
DiskType: diskType,
Filesystem: filesystem,
MountPoint: mountPoint,
IsMounted: isMounted,
UsePercent: usePercent,
Used: used,
Avail: avail,
Serial: dev.Serial,
IsRemovable: dev.Tran == "usb",
IsSystem: isSystem,
}
list = append(list, info)
for _, child := range dev.Children {
childList := parseDevice(child)
list = append(list, childList...)
}
return list
}
func parseLsblkJsonOutput(output string) ([]response.DiskBasicInfo, error) {
raw := &LsblkRaw{}
if err := json.Unmarshal([]byte(output), raw); err != nil {
return nil, fmt.Errorf("failed to parse lsblk json output: %v", err)
}
var disks []response.DiskBasicInfo
for _, dev := range raw.Blockdevices {
if strings.HasPrefix(dev.Name, "loop") ||
strings.HasPrefix(dev.Name, "dm-") {
continue
}
devList := parseDevice(dev)
disks = append(disks, devList...)
}
return disks, nil
}
func organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskInfo {
var result response.CompleteDiskInfo
var systemDisk *response.DiskInfo
diskMap := make(map[string]*response.DiskInfo)
partitions := make(map[string][]response.DiskBasicInfo)
@@ -54,7 +179,7 @@ func organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskI
totalCapacity += capacity
if disk.IsSystem {
systemDisk = disk
result.SystemDisks = append(result.SystemDisks, *disk)
} else if len(disk.Partitions) == 0 {
result.UnpartitionedDisks = append(result.UnpartitionedDisks, disk.DiskBasicInfo)
} else {
@@ -62,7 +187,6 @@ func organizeDiskInfo(diskInfos []response.DiskBasicInfo) response.CompleteDiskI
}
}
result.SystemDisk = systemDisk
result.TotalDisks = len(diskMap)
result.TotalCapacity = totalCapacity
@@ -161,17 +285,25 @@ func parseLsblkOutput(output string) ([]response.DiskBasicInfo, error) {
if fsType == "LVM2_member" {
for _, lvmInfo := range lvmMap {
if lvmInfo.IsMounted {
actualMountPoint = lvmInfo.MountPoint
actualFsType = lvmInfo.Filesystem
actualUsed = lvmInfo.Used
size = lvmInfo.Size
actualAvail = lvmInfo.Avail
actualUsePercent = lvmInfo.UsePercent
isMounted = true
isSystemPartition = lvmInfo.IsSystem
break
lvmDiskInfo := response.DiskBasicInfo{
Device: name,
Size: lvmInfo.Size,
Model: model,
DiskType: "LVM",
IsRemovable: tran == "usb",
IsSystem: lvmInfo.IsSystem,
Filesystem: lvmInfo.Filesystem,
Used: lvmInfo.Used,
Avail: lvmInfo.Avail,
UsePercent: lvmInfo.UsePercent,
MountPoint: lvmInfo.MountPoint,
IsMounted: true,
Serial: serial,
}
diskInfos = append(diskInfos, lvmDiskInfo)
}
}
continue
}
info := response.DiskBasicInfo{
@@ -205,12 +337,10 @@ func getDiskType(rota string) string {
return "Unknown"
}
var kvRe = regexp.MustCompile(`([A-Za-z0-9_]+)=("([^"\\]|\\.)*"|[^ \t]+)`)
func parseKeyValuePairs(line string) map[string]string {
fields := make(map[string]string)
matches := kvRe.FindAllStringSubmatch(line, -1)
matches := re.GetRegex(re.DiskKeyValuePattern).FindAllStringSubmatch(line, -1)
for _, m := range matches {
key := m[1]
raw := m[2]
@@ -394,7 +524,7 @@ func isSystemDisk(mountPoint string) bool {
"/boot/efi",
"/usr",
"/var",
"/home",
"/etc",
}
for _, sysMount := range systemMountPoints {
+16 -47
View File
@@ -13,9 +13,8 @@ import (
"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/controller"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/systemctl"
"github.com/pkg/errors"
)
type DockerService struct{}
@@ -235,7 +234,9 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate, withRestart bool) erro
}
if withRestart {
return restartDocker()
if err := controller.HandleRestart("docker"); err != nil {
return fmt.Errorf("failed to restart Docker: %v", err)
}
}
return nil
}
@@ -279,8 +280,8 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
return err
}
if err := restartDocker(); err != nil {
return err
if err := controller.HandleRestart("docker"); err != nil {
return fmt.Errorf("failed to restart Docker: %v", err)
}
return nil
}
@@ -318,8 +319,8 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
return err
}
if err := restartDocker(); err != nil {
return err
if err := controller.HandleRestart("docker"); err != nil {
return fmt.Errorf("failed to restart Docker: %v", err)
}
return nil
}
@@ -342,25 +343,19 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
return err
}
if err := restartDocker(); err != nil {
return err
if err := controller.HandleRestart("docker"); err != nil {
return fmt.Errorf("failed to restart Docker: %v", err)
}
return nil
}
func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
service := "docker"
sudo := cmd.SudoHandleCmd()
dockerCmd, err := getDockerRestartCommand()
if err != nil {
return err
}
if req.Operation == "stop" {
isSocketActive, _ := systemctl.IsActive("docker.socket")
isSocketActive, _ := controller.CheckExist("docker.socket")
if isSocketActive {
std, err := cmd.RunDefaultWithStdoutBashCf("%s systemctl stop docker.socket", sudo)
if err != nil {
global.LOG.Errorf("handle systemctl stop docker.socket failed, err: %v", std)
if err := controller.HandleStop("docker.socket"); err != nil {
global.LOG.Errorf("handle stop docker.socket failed, err: %v", err)
}
}
}
@@ -369,9 +364,8 @@ func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
return err
}
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s %s %s ", dockerCmd, req.Operation, service)
if err != nil {
return errors.New(string(stdout))
if err := controller.Handle(req.Operation, service); err != nil {
return err
}
return nil
}
@@ -434,32 +428,7 @@ func validateDockerConfig() error {
return nil
}
if err != nil || (stdout != "" && strings.TrimSpace(stdout) != "configuration OK") {
return fmt.Errorf("Docker configuration validation failed, err: %v", stdout)
}
return nil
}
func getDockerRestartCommand() (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashC("which docker")
if err != nil {
return "", fmt.Errorf("failed to find docker: %v", err)
}
dockerPath := stdout
if strings.Contains(dockerPath, "snap") {
return "snap", nil
}
return "systemctl", nil
}
func restartDocker() error {
global.LOG.Info("restart docker")
restartCmd, err := getDockerRestartCommand()
if err != nil {
return err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s restart docker", restartCmd)
if err != nil {
return fmt.Errorf("failed to restart Docker: %s", stdout)
return fmt.Errorf("Docker configuration validation failed, %v", err)
}
return nil
}
+3 -2
View File
@@ -12,8 +12,9 @@ var (
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
appIgnoreUpgradeRepo = repo.NewIAppIgnoreUpgradeRepo()
aiRepo = repo.NewIAiRepo()
mcpServerRepo = repo.NewIMcpServerRepo()
aiRepo = repo.NewIAiRepo()
mcpServerRepo = repo.NewIMcpServerRepo()
tensorrtLLMRepo = repo.NewITensorRTLLMRepo()
mysqlRepo = repo.NewIMysqlRepo()
postgresqlRepo = repo.NewIPostgresqlRepo()
+120
View File
@@ -3,7 +3,12 @@ package service
import (
"bufio"
"context"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/convert"
"github.com/1Panel-dev/1Panel/agent/utils/ini_conf"
"io"
"io/fs"
"os"
@@ -66,6 +71,8 @@ type IFileService interface {
BatchCheckFiles(req request.FilePathsCheck) []response.ExistFileInfo
GetHostMount() []dto.DiskInfo
GetUsersAndGroups() (*response.UserGroupResponse, error)
Convert(req request.FileConvertRequest)
ConvertLog(req dto.PageInfo) (int64, []response.FileConvertLog, error)
}
var filteredPaths = []string{
@@ -554,6 +561,19 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
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))
case "php-fpm-slow-logs":
php, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))
if err != nil {
return nil, err
}
logFilePath = php.GetSlowLogPath()
case constant.Supervisord:
configPath := "/etc/supervisord.conf"
pathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))
if pathSet.ID != 0 || pathSet.Value != "" {
configPath = pathSet.Value
}
logFilePath, _ = ini_conf.GetIniValue(configPath, "supervisord", "logfile")
}
file, err := os.Open(logFilePath)
@@ -723,3 +743,103 @@ func getValidUsers(validGroups map[string]bool) ([]response.UserInfo, map[string
}
return users, groupSet, nil
}
func (f *FileService) Convert(req request.FileConvertRequest) {
convertTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("FileConvert"), task.TaskExec, task.TaskScopeFileConvert, req.TaskID, 1)
if err != nil {
global.LOG.Errorf("Create convert task failed %v", err)
return
}
convertTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey("FileConvert"), task.TaskExec, task.TaskScopeFileConvert), func(t *task.Task) (err error) {
for _, file := range req.Files {
input := filepath.Join(file.Path, file.InputFile)
nameOnly := file.InputFile[0 : len(file.InputFile)-len(file.Extension)]
output := filepath.Join(req.OutputPath, nameOnly+"."+file.OutputFormat)
status, errMsg := convert.MediaFile(input, output, file.OutputFormat, req.DeleteSource)
if status == "FAILED" {
convertTask.Log(fmt.Sprintf("%s -> %s [%s]: %s\n",
input, output, status, errMsg))
} else {
convertTask.Log(fmt.Sprintf("%s -> %s [%s]: %s\n",
input, output, status, "SUCCESS"))
}
}
return nil
}, nil)
go func() {
_ = convertTask.Execute()
}()
}
func (f *FileService) ConvertLog(req dto.PageInfo) (total int64, data []response.FileConvertLog, err error) {
logFilePath := filepath.Join(global.Dir.ConvertLogDir, "convert.log")
file, err := os.Open(logFilePath)
if err != nil {
return 0, nil, err
}
defer file.Close()
const chunkSize = 64 * 1024
stat, err := file.Stat()
if err != nil {
return 0, nil, err
}
fileSize := stat.Size()
var (
buf []byte
remainder []byte
offset = fileSize
lines []string
)
pageStart := int64((req.Page - 1) * req.PageSize)
pageEnd := pageStart + int64(req.PageSize)
for offset > 0 {
readSize := chunkSize
if offset < int64(readSize) {
readSize = int(offset)
}
offset -= int64(readSize)
tmp := make([]byte, readSize)
if _, err := file.ReadAt(tmp, offset); err != nil && err != io.EOF {
return 0, nil, err
}
buf = append(tmp, remainder...)
linesSplit := strings.Split(string(buf), "\n")
if offset > 0 {
remainder = []byte(linesSplit[0])
linesSplit = linesSplit[1:]
}
for i := len(linesSplit) - 1; i >= 0; i-- {
line := strings.TrimSpace(linesSplit[i])
if line == "" {
continue
}
total++
if total > pageStart && total <= pageEnd {
lines = append(lines, line)
}
if total >= pageEnd {
break
}
}
if total >= pageEnd {
break
}
}
for _, line := range lines {
var entry response.FileConvertLog
if err := json.Unmarshal([]byte(line), &entry); err == nil {
data = append(data, entry)
}
}
return total, data, nil
}
+243 -72
View File
@@ -16,17 +16,20 @@ import (
"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/controller"
"github.com/1Panel-dev/1Panel/agent/utils/firewall"
fireClient "github.com/1Panel-dev/1Panel/agent/utils/firewall/client"
"github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables"
"github.com/jinzhu/copier"
)
const confPath = "/etc/sysctl.conf"
const panelSysctlPath = "/etc/sysctl.d/98-onepanel.conf"
type FirewallService struct{}
type IFirewallService interface {
LoadBaseInfo() (dto.FirewallBaseInfo, error)
LoadBaseInfo(tab string) (dto.FirewallBaseInfo, error)
SearchWithPage(search dto.RuleSearch) (int64, interface{}, error)
OperateFirewall(req dto.FirewallOperation) error
OperatePortRule(req dto.PortRuleOperate, reload bool) error
@@ -42,7 +45,7 @@ func NewIFirewallService() IFirewallService {
return &FirewallService{}
}
func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
func (u *FirewallService) LoadBaseInfo(tab string) (dto.FirewallBaseInfo, error) {
var baseInfo dto.FirewallBaseInfo
baseInfo.Version = "-"
baseInfo.Name = "-"
@@ -56,18 +59,16 @@ func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
baseInfo.Name = client.Name()
var wg sync.WaitGroup
wg.Add(3)
wg.Add(2)
go func() {
defer wg.Done()
baseInfo.PingStatus = u.pingStatus()
baseInfo.Version, _ = client.Version()
}()
go func() {
defer wg.Done()
baseInfo.IsActive, _ = client.Status()
}()
go func() {
defer wg.Done()
baseInfo.Version, _ = client.Version()
baseInfo.IsInit, baseInfo.IsBind = loadInitStatus(baseInfo.Name, tab)
}()
wg.Wait()
return baseInfo, nil
@@ -157,15 +158,17 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
if req.Type != des.Type {
continue
}
if backDatas[i].Port == des.Port &&
if backDatas[i].Port == des.DstPort &&
req.Type == "port" &&
backDatas[i].Protocol == des.Protocol &&
backDatas[i].Strategy == des.Strategy &&
backDatas[i].Address == des.Address {
backDatas[i].Address == des.SrcIP {
backDatas[i].ID = des.ID
backDatas[i].Description = des.Description
break
}
if req.Type == "address" && backDatas[i].Strategy == des.Strategy && backDatas[i].Address == des.Address {
if req.Type == "address" && backDatas[i].Strategy == des.Strategy && backDatas[i].Address == des.SrcIP {
backDatas[i].ID = des.ID
backDatas[i].Description = des.Description
break
}
@@ -211,8 +214,8 @@ func (u *FirewallService) OperateFirewall(req dto.FirewallOperation) error {
return fmt.Errorf("not supported operation: %s", req.Operation)
}
if needRestartDocker && req.WithDockerRestart {
if err := restartDocker(); err != nil {
return err
if err := controller.HandleRestart("docker"); err != nil {
return fmt.Errorf("failed to restart Docker: %v", err)
}
}
return nil
@@ -223,6 +226,9 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool)
if err != nil {
return err
}
if len(req.Chain) == 0 && client.Name() == "iptables" {
req.Chain = iptables.Chain1PanelBasic
}
protos := strings.Split(req.Protocol, "/")
itemAddress := strings.Split(strings.TrimSuffix(req.Address, ","), ",")
@@ -329,7 +335,8 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
if reqRule.Port == rule.Port &&
reqRule.TargetPort == rule.TargetPort &&
reqRule.TargetIP == rule.TargetIP &&
proto == rule.Protocol {
proto == rule.Protocol &&
reqRule.Interface == rule.Interface {
shouldKeep = false
break
}
@@ -353,7 +360,8 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
if reqRule.Port == rule.Port &&
reqRule.TargetPort == rule.TargetPort &&
reqRule.TargetIP == rule.TargetIP &&
proto == rule.Protocol {
proto == rule.Protocol &&
reqRule.Interface == rule.Interface {
return buserr.New("ErrRecordExist")
}
}
@@ -383,6 +391,7 @@ func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
Port: r.Port,
TargetIP: r.TargetIP,
TargetPort: r.TargetPort,
Interface: r.Interface,
}, r.Operation); err != nil {
if req.ForceDelete {
global.LOG.Error(err)
@@ -400,7 +409,10 @@ func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload boo
if err != nil {
return err
}
chain := ""
if client.Name() == "iptables" {
chain = iptables.Chain1PanelBasic
}
var fireInfo fireClient.FireInfo
if err := copier.Copy(&fireInfo, &req); err != nil {
return err
@@ -416,7 +428,7 @@ func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload boo
return err
}
req.Address = addressList[i]
if err := u.addAddressRecord(req); err != nil {
if err := u.addAddressRecord(chain, req); err != nil {
return err
}
}
@@ -455,10 +467,18 @@ func (u *FirewallService) UpdateAddrRule(req dto.AddrRuleUpdate) error {
}
func (u *FirewallService) UpdateDescription(req dto.UpdateFirewallDescription) error {
var firewall model.Firewall
if err := copier.Copy(&firewall, &req); err != nil {
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
firewall := model.Firewall{
Type: req.Type,
Chain: req.Chain,
SrcIP: req.SrcIP,
DstIP: req.DstIP,
SrcPort: req.SrcPort,
DstPort: req.DstPort,
Protocol: req.Protocol,
Strategy: req.Strategy,
Description: req.Description,
}
return hostRepo.SaveFirewallRecord(&firewall)
}
@@ -558,7 +578,7 @@ func (u *FirewallService) cleanUnUsedData(client firewall.FirewallClient) {
}
for _, item := range list {
for i := 0; i < len(records); i++ {
if records[i].Port == item.Port && records[i].Protocol == item.Protocol && records[i].Strategy == item.Strategy && records[i].Address == item.Address {
if records[i].DstPort == item.Port && records[i].Protocol == item.Protocol && records[i].Strategy == item.Strategy && records[i].SrcIP == item.Address {
records = append(records[:i], records[i+1:]...)
}
}
@@ -568,62 +588,101 @@ func (u *FirewallService) cleanUnUsedData(client firewall.FirewallClient) {
_ = hostRepo.DeleteFirewallRecordByID(record.ID)
}
}
func (u *FirewallService) pingStatus() string {
if _, err := os.Stat("/etc/sysctl.conf"); err != nil {
data, err := os.ReadFile("/proc/sys/net/ipv4/icmp_echo_ignore_all")
if err != nil {
return constant.StatusNone
}
stdout, _ := cmd.RunDefaultWithStdoutBashCf("%s sysctl -a 2>/dev/null | grep 'net.ipv4.icmp.echo_ignore_all'", cmd.SudoHandleCmd())
if stdout == "net.ipv4.icmp_echo_ignore_all = 1\n" {
return constant.StatusEnable
v6Data, v6err := os.ReadFile("/proc/sys/net/ipv6/icmp/echo_ignore_all")
if v6err != nil {
if strings.TrimSpace(string(data)) == "1" {
return constant.StatusEnable
}
return constant.StatusDisable
} else {
if strings.TrimSpace(string(data)) == "1" && strings.TrimSpace(string(v6Data)) == "1" {
return constant.StatusEnable
}
return constant.StatusDisable
}
return constant.StatusDisable
}
func (u *FirewallService) updatePingStatus(enable string) error {
lineBytes, err := os.ReadFile(confPath)
if err != nil {
return err
var targetPath string
var applyCmd string
if _, err := os.Stat(confPath); os.IsNotExist(err) {
// Debian 13
targetPath = panelSysctlPath
applyCmd = fmt.Sprintf("%s sysctl --system", cmd.SudoHandleCmd())
if err := cmd.RunDefaultBashCf("%s mkdir -p /etc/sysctl.d", cmd.SudoHandleCmd()); err != nil {
return fmt.Errorf("failed to create directory /etc/sysctl.d: %v", err)
}
} else {
targetPath = confPath
applyCmd = fmt.Sprintf("%s sysctl -p", cmd.SudoHandleCmd())
}
files := strings.Split(string(lineBytes), "\n")
var newFiles []string
lineBytes, err := os.ReadFile(targetPath)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to read %s: %v", targetPath, err)
}
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv4/icmp_echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
return fmt.Errorf("failed to apply ipv4 ping status temporarily: %v", err)
}
var hasIpv6 bool
ipv6Status, _ := cmd.RunDefaultWithStdoutBashC("sysctl -a 2>/dev/null | grep 'net.ipv6.icmp.echo_ignore_all'")
if len(strings.ReplaceAll(ipv6Status, "\n", "")) != 0 {
if _, err := os.Stat("/proc/sys/net/ipv6/icmp/echo_ignore_all"); err == nil {
hasIpv6 = true
if err := cmd.RunDefaultBashCf("echo %s | %s tee /proc/sys/net/ipv6/icmp/echo_ignore_all > /dev/null", enable, cmd.SudoHandleCmd()); err != nil {
global.LOG.Warnf("failed to apply ipv6 ping status temporarily: %v", err)
}
}
var files []string
if err == nil {
files = strings.Split(string(lineBytes), "\n")
}
var newFiles []string
hasIPv4Line, hasIPv6Line := false, false
for _, line := range files {
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.Contains(line, "net.ipv4.icmp_echo_ignore_all") {
if strings.HasPrefix(strings.TrimSpace(line), "net.ipv4.icmp_echo_ignore_all") {
newFiles = append(newFiles, "net.ipv4.icmp_echo_ignore_all="+enable)
hasIPv4Line = true
continue
}
if hasIpv6 && strings.Contains(line, "net/ipv6/icmp/echo_ignore_all") || strings.Contains(line, "net.ipv6.icmp.echo_ignore_all") {
if strings.HasPrefix(strings.TrimSpace(line), "net.ipv6.icmp.echo_ignore_all") {
newFiles = append(newFiles, "net.ipv6.icmp.echo_ignore_all="+enable)
hasIPv6Line = true
continue
}
newFiles = append(newFiles, line)
}
if !hasIPv4Line {
newFiles = append(newFiles, "net.ipv4.icmp_echo_ignore_all="+enable)
}
if !hasIPv6Line {
if hasIpv6 && !hasIPv6Line {
newFiles = append(newFiles, "net.ipv6.icmp.echo_ignore_all="+enable)
}
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, constant.FilePerm)
file, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, constant.FilePerm)
if err != nil {
return err
return fmt.Errorf("failed to open %s: %v", targetPath, err)
}
defer file.Close()
_, err = file.WriteString(strings.Join(newFiles, "\n"))
if err != nil {
return err
if _, err = file.WriteString(strings.Join(newFiles, "\n")); err != nil {
return fmt.Errorf("failed to write to %s: %v", targetPath, err)
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("%s sysctl -p", cmd.SudoHandleCmd())
if err != nil {
return fmt.Errorf("update ping status failed, err: %v", stdout)
if err := cmd.RunDefaultBashC(applyCmd); err != nil {
global.LOG.Warnf("failed to apply persistent config with '%s': %v", applyCmd, err)
}
return nil
@@ -643,7 +702,7 @@ func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) er
}
}
}
if err := client.Port(fireClient.FireInfo{Port: "22", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
if err := client.Port(fireClient.FireInfo{Port: loadSSHPort(), Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
if err := client.Port(fireClient.FireInfo{Port: "80", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
@@ -658,14 +717,21 @@ func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) er
func (u *FirewallService) addPortRecord(req dto.PortRuleOperate) error {
if req.Operation == "remove" {
return hostRepo.DeleteFirewallRecord("port", req.Port, req.Protocol, req.Address, req.Strategy)
if req.ID != 0 {
return hostRepo.DeleteFirewallRecordByID(req.ID)
}
return nil
}
if len(req.Description) == 0 {
return nil
}
if err := hostRepo.SaveFirewallRecord(&model.Firewall{
Type: "port",
Port: req.Port,
Chain: req.Chain,
DstPort: req.Port,
Protocol: req.Protocol,
Address: req.Address,
SrcIP: req.Address,
Strategy: req.Strategy,
Description: req.Description,
}); err != nil {
@@ -675,13 +741,18 @@ func (u *FirewallService) addPortRecord(req dto.PortRuleOperate) error {
return nil
}
func (u *FirewallService) addAddressRecord(req dto.AddrRuleOperate) error {
func (u *FirewallService) addAddressRecord(chain string, req dto.AddrRuleOperate) error {
if req.Operation == "remove" {
return hostRepo.DeleteFirewallRecord("address", "", "", req.Address, req.Strategy)
if req.ID != 0 {
return hostRepo.DeleteFirewallRecordByID(req.ID)
}
return nil
}
if err := hostRepo.SaveFirewallRecord(&model.Firewall{
Type: "address",
Address: req.Address,
Chain: chain,
SrcIP: req.Address,
Strategy: req.Strategy,
Description: req.Description,
}); err != nil {
@@ -692,29 +763,36 @@ func (u *FirewallService) addAddressRecord(req dto.AddrRuleOperate) error {
func checkPortUsed(ports, proto string, apps []portOfApp) string {
var portList []int
if strings.Contains(ports, "-") || strings.Contains(ports, ",") {
if strings.Contains(ports, "-") {
port1, err := strconv.Atoi(strings.Split(ports, "-")[0])
if err != nil {
global.LOG.Errorf(" convert string %s to int failed, err: %v", strings.Split(ports, "-")[0], err)
return ""
}
port2, err := strconv.Atoi(strings.Split(ports, "-")[1])
if err != nil {
global.LOG.Errorf(" convert string %s to int failed, err: %v", strings.Split(ports, "-")[1], err)
return ""
}
for i := port1; i <= port2; i++ {
portList = append(portList, i)
}
} else {
portLists := strings.Split(ports, ",")
for _, item := range portLists {
portItem, _ := strconv.Atoi(item)
portList = append(portList, portItem)
}
rangeSplit := ""
if strings.Contains(ports, "-") {
rangeSplit = "-"
}
if strings.Contains(ports, ":") {
rangeSplit = ":"
}
if len(rangeSplit) != 0 {
port1, err := strconv.Atoi(strings.Split(ports, rangeSplit)[0])
if err != nil {
global.LOG.Errorf(" convert string %s to int failed, err: %v", strings.Split(ports, rangeSplit)[0], err)
return ""
}
port2, err := strconv.Atoi(strings.Split(ports, rangeSplit)[1])
if err != nil {
global.LOG.Errorf(" convert string %s to int failed, err: %v", strings.Split(ports, rangeSplit)[1], err)
return ""
}
for i := port1; i <= port2; i++ {
portList = append(portList, i)
}
}
if strings.Contains(ports, ",") {
portLists := strings.Split(ports, ",")
for _, item := range portLists {
portItem, _ := strconv.Atoi(item)
portList = append(portList, portItem)
}
}
if len(portList) != 0 {
var usedPorts []string
for _, port := range portList {
portItem := fmt.Sprintf("%v", port)
@@ -748,3 +826,96 @@ func checkPortUsed(ports, proto string, apps []portOfApp) string {
}
return ""
}
func loadInitStatus(clientName, tab string) (bool, bool) {
if clientName == "firewalld" {
return true, true
}
if clientName == "ufw" && tab != "forward" {
return true, true
}
switch tab {
case "base":
if isExist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasicBefore); !isExist {
return false, false
}
if exist := iptables.CheckRuleExist(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.IoRuleIn); !exist {
return false, false
}
if exist := iptables.CheckRuleExist(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.EstablishedRule); !exist {
return false, false
}
if exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasic); !exist {
return false, false
}
if exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasicAfter); !exist {
return false, false
}
if exist := iptables.CheckRuleExist(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllTcp); !exist {
return false, false
}
if exist := iptables.CheckRuleExist(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllUdp); !exist {
return false, false
}
if bind, _ := iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore); !bind {
return true, false
}
if bind, _ := iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic); !bind {
return true, false
}
if bind, _ := iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter); !bind {
return true, false
}
return true, true
case "advance":
isExist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelInput)
if !isExist {
return false, false
}
isExist, _ = iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelOutput)
if !isExist {
return false, false
}
isBind, _ := iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelInput)
if !isBind {
return true, false
}
isBind, _ = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainOutput, iptables.Chain1PanelOutput)
return true, isBind
case "forward":
stdout, err := cmd.RunDefaultWithStdoutBashC("cat /proc/sys/net/ipv4/ip_forward")
if err != nil {
global.LOG.Errorf("check /proc/sys/net/ipv4/ip_forward failed, err: %v", err)
return false, false
}
if strings.TrimSpace(stdout) == "0" {
return false, false
}
exist, _ := iptables.CheckChainExist(iptables.NatTab, iptables.Chain1PanelPreRouting)
if !exist {
return false, false
}
exist, _ = iptables.CheckChainExist(iptables.NatTab, iptables.Chain1PanelPostRouting)
if !exist {
return false, false
}
exist, _ = iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelForward)
if !exist {
return false, false
}
isBind, _ := iptables.CheckChainBind(iptables.NatTab, "PREROUTING", iptables.Chain1PanelPreRouting)
if !isBind {
return false, false
}
isBind, _ = iptables.CheckChainBind(iptables.NatTab, "POSTROUTING", iptables.Chain1PanelPostRouting)
if !isBind {
return false, false
}
isBind, _ = iptables.CheckChainBind(iptables.FilterTab, "FORWARD", iptables.Chain1PanelForward)
return true, isBind
default:
return false, false
}
}
+15 -27
View File
@@ -17,9 +17,9 @@ import (
"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/controller"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/ini_conf"
"github.com/1Panel-dev/1Panel/agent/utils/systemctl"
"github.com/pkg/errors"
"gopkg.in/ini.v1"
)
@@ -31,7 +31,6 @@ type IHostToolService interface {
CreateToolConfig(req request.HostToolCreate) error
OperateTool(req request.HostToolReq) error
OperateToolConfig(req request.HostToolConfig) (*response.HostToolConfig, error)
GetToolLog(req request.HostToolLogReq) (string, error)
OperateSupervisorProcess(req request.SupervisorProcessConfig) error
GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error)
OperateSupervisorProcessFile(req request.SupervisorProcessFileReq) (string, error)
@@ -53,9 +52,9 @@ func (h *HostToolService) GetToolStatus(req request.HostToolReq) (*response.Host
return res, nil
}
supervisorConfig.IsExist = true
serviceExist, _ := systemctl.IsExist(constant.Supervisord)
serviceExist, _ := controller.CheckExist(constant.Supervisord)
if !serviceExist {
serviceExist, _ = systemctl.IsExist(constant.Supervisor)
serviceExist, _ = controller.CheckExist(constant.Supervisor)
if !serviceExist {
supervisorConfig.IsExist = false
res.Config = supervisorConfig
@@ -77,7 +76,7 @@ func (h *HostToolService) GetToolStatus(req request.HostToolReq) (*response.Host
_, ctlRrr := exec.LookPath("supervisorctl")
supervisorConfig.CtlExist = ctlRrr == nil
active, _ := systemctl.IsActive(supervisorConfig.ServiceName)
active, _ := controller.CheckActive(supervisorConfig.ServiceName)
if active {
supervisorConfig.Status = "running"
} else {
@@ -194,7 +193,7 @@ func (h *HostToolService) CreateToolConfig(req request.HostToolCreate) error {
return err
}
}
if err = systemctl.Restart(req.ServiceName); err != nil {
if err = controller.HandleRestart(req.ServiceName); err != nil {
global.LOG.Errorf("[init] restart %s failed err %s", req.ServiceName, err.Error())
return err
}
@@ -210,7 +209,7 @@ func (h *HostToolService) OperateTool(req request.HostToolReq) error {
serviceName = serviceNameSet.Value
}
}
return systemctl.Operate(req.Operate, serviceName)
return controller.Handle(req.Operate, serviceName)
}
func (h *HostToolService) OperateToolConfig(req request.HostToolConfig) (*response.HostToolConfig, error) {
@@ -252,7 +251,7 @@ func (h *HostToolService) OperateToolConfig(req request.HostToolConfig) (*respon
if err = fileOp.WriteFile(configPath, strings.NewReader(req.Content), fileInfo.Mode()); err != nil {
return nil, err
}
if err = systemctl.Restart(serviceName); err != nil {
if err = controller.HandleRestart(serviceName); err != nil {
_ = fileOp.WriteFile(configPath, bytes.NewReader(oldContent), fileInfo.Mode())
return nil, err
}
@@ -261,25 +260,6 @@ func (h *HostToolService) OperateToolConfig(req request.HostToolConfig) (*respon
return res, nil
}
func (h *HostToolService) GetToolLog(req request.HostToolLogReq) (string, error) {
fileOp := files.NewFileOp()
logfilePath := ""
switch req.Type {
case constant.Supervisord:
configPath := "/etc/supervisord.conf"
pathSet, _ := settingRepo.Get(settingRepo.WithByKey(constant.SupervisorConfigPath))
if pathSet.ID != 0 || pathSet.Value != "" {
configPath = pathSet.Value
}
logfilePath, _ = ini_conf.GetIniValue(configPath, "supervisord", "logfile")
}
oldContent, err := fileOp.GetContent(logfilePath)
if err != nil {
return "", err
}
return string(oldContent), nil
}
func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcessConfig) error {
var (
supervisordDir = path.Join(global.Dir.DataDir, "tools", "supervisord")
@@ -333,6 +313,9 @@ func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, c
_, _ = section.NewKey("priority", "999")
_, _ = section.NewKey("numprocs", req.Numprocs)
_, _ = section.NewKey("process_name", "%(program_name)s_%(process_num)02d")
if req.Environment != "" {
_, _ = section.NewKey("environment", req.Environment)
}
if err = configFile.SaveTo(iniPath); err != nil {
return err
@@ -363,6 +346,8 @@ func handleProcess(supervisordDir string, req request.SupervisorProcessConfig, c
autoRestart.SetValue(req.AutoRestart)
autoStart := section.Key("autostart")
autoStart.SetValue(req.AutoStart)
environment := section.Key("environment")
environment.SetValue(req.Environment)
if err = configFile.SaveTo(iniPath); err != nil {
return err
@@ -435,6 +420,9 @@ func handleProcessConfig(configDir, containerName string) ([]response.Supervisor
if autoStart, _ := section.GetKey("autostart"); autoStart != nil {
config.AutoStart = autoStart.Value()
}
if environment, _ := section.GetKey("environment"); environment != nil {
config.Environment = environment.Value()
}
_ = getProcessStatus(&config, containerName)
result = append(result, config)
}
+100 -44
View File
@@ -228,6 +228,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) error {
Tags: []string{req.Name},
Remove: true,
Labels: stringsToMap(req.Tags),
BuildArgs: stringsToMap2(req.Args),
}
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskBuild, task.TaskScopeImage, req.TaskID, 1)
if err != nil {
@@ -256,17 +257,19 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error {
return err
}
defer client.Close()
imageItemName := strings.ReplaceAll(path.Base(req.ImageName), ":", "_")
taskItem, err := task.NewTaskWithOps(imageItemName, task.TaskPull, task.TaskScopeImage, req.TaskID, 1)
taskItem, err := task.NewTaskWithOps(strings.Join(req.ImageName, ","), task.TaskPull, task.TaskScopeImage, req.TaskID, 1)
if err != nil {
return fmt.Errorf("new task for image pull failed, err: %v", err)
}
go func() {
taskItem.AddSubTask(i18n.GetWithName("ImagePull", req.ImageName), func(t *task.Task) error {
for _, item := range req.ImageName {
itemName := strings.ReplaceAll(path.Base(item), ":", "_")
taskItem.AddSubTask(i18n.GetWithName("ImagePull", itemName), func(t *task.Task) error {
taskItem.Logf("----------------- %s -----------------", itemName)
options := image.PullOptions{}
imageName := req.ImageName
imageName := item
if req.RepoID == 0 {
hasAuth, authStr := loadAuthInfo(req.ImageName)
hasAuth, authStr := loadAuthInfo(item)
if hasAuth {
options.RegistryAuth = authStr
}
@@ -288,7 +291,7 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error {
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
options.RegistryAuth = authStr
}
imageName = repo.DownloadUrl + "/" + req.ImageName
imageName = repo.DownloadUrl + "/" + item
}
dockerCli := docker.NewClientWithExist(client)
err = dockerCli.PullImageWithProcessAndOptions(taskItem, imageName, options)
@@ -298,57 +301,80 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error {
}
return nil
}, nil)
}
go func() {
_ = taskItem.Execute()
}()
return nil
}
func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
file, err := os.Open(req.Path)
taskItem, err := task.NewTaskWithOps(req.Path, task.TaskImport, task.TaskScopeImage, req.TaskID, 1)
if err != nil {
return err
}
defer file.Close()
client, err := docker.NewDockerClient()
if err != nil {
return err
}
defer client.Close()
res, err := client.ImageLoad(context.TODO(), file)
if err != nil {
return err
}
defer res.Body.Close()
content, err := io.ReadAll(res.Body)
if err != nil {
return err
}
if strings.Contains(string(content), "Error") {
return errors.New(string(content))
return fmt.Errorf("new task for image import failed, err: %v", err)
}
taskItem.AddSubTask(i18n.GetWithName("TaskImport", req.Path), func(t *task.Task) error {
file, err := os.Open(req.Path)
if err != nil {
return err
}
defer file.Close()
client, err := docker.NewDockerClient()
if err != nil {
return err
}
defer client.Close()
res, err := client.ImageLoad(context.TODO(), file)
if err != nil {
return err
}
defer res.Body.Close()
content, err := io.ReadAll(res.Body)
if err != nil {
return err
}
if strings.Contains(string(content), "Error") {
return errors.New(string(content))
}
return nil
}, nil)
go func() {
_ = taskItem.Execute()
}()
return nil
}
func (u *ImageService) ImageSave(req dto.ImageSave) error {
client, err := docker.NewDockerClient()
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskExport, task.TaskScopeImage, req.TaskID, 1)
if err != nil {
return err
return fmt.Errorf("new task for image export failed, err: %v", err)
}
defer client.Close()
taskItem.AddSubTask(i18n.GetWithName("TaskExport", req.Name), func(t *task.Task) error {
taskItem.Log(req.TagName)
client, err := docker.NewDockerClient()
if err != nil {
return err
}
defer client.Close()
out, err := client.ImageSave(context.TODO(), []string{req.TagName})
if err != nil {
return err
}
defer out.Close()
file, err := os.OpenFile(fmt.Sprintf("%s/%s.tar", req.Path, req.Name), os.O_WRONLY|os.O_CREATE|os.O_EXCL, constant.FilePerm)
if err != nil {
return err
}
defer file.Close()
if _, err = io.Copy(file, out); err != nil {
return err
}
out, err := client.ImageSave(context.TODO(), []string{req.TagName})
if err != nil {
return err
}
defer out.Close()
file, err := os.OpenFile(fmt.Sprintf("%s/%s.tar", req.Path, req.Name), os.O_WRONLY|os.O_CREATE|os.O_EXCL, constant.FilePerm)
if err != nil {
return err
}
defer file.Close()
if _, err = io.Copy(file, out); err != nil {
return err
}
return nil
}, nil)
go func() {
_ = taskItem.Execute()
}()
return nil
}
@@ -359,9 +385,39 @@ func (u *ImageService) ImageTag(req dto.ImageTag) error {
}
defer client.Close()
if err := client.ImageTag(context.TODO(), req.SourceID, req.TargetName); err != nil {
imageItem, err := client.ImageInspect(context.Background(), req.SourceID)
if err != nil {
return err
}
for _, tag := range req.Tags {
isNew := true
for _, tagOld := range imageItem.RepoTags {
if tag == tagOld {
isNew = false
break
}
}
if isNew {
if err := client.ImageTag(context.TODO(), req.SourceID, tag); err != nil {
return err
}
}
}
for _, tagOld := range imageItem.RepoTags {
isDel := true
for _, tag := range req.Tags {
if tag == tagOld {
isDel = false
break
}
}
if isDel {
if _, err := client.ImageRemove(context.TODO(), tagOld, image.RemoveOptions{}); err != nil {
return err
}
}
}
return nil
}
+6 -5
View File
@@ -16,6 +16,7 @@ import (
"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/controller"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
@@ -134,7 +135,7 @@ func (u *ImageRepoService) Delete(req dto.OperateByID) error {
return err
}
go func() {
_ = restartDocker()
_ = controller.HandleRestart("docker")
}()
return nil
}
@@ -262,8 +263,8 @@ func stopBeforeUpdateRepo() error {
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
if err := controller.HandleRestart("docker"); err != nil {
return fmt.Errorf("failed to restart Docker: %v", err)
}
ticker := time.NewTicker(3 * time.Second)
defer ticker.Stop()
@@ -275,8 +276,8 @@ func stopBeforeUpdateRepo() error {
cancel()
return errors.New("the docker service cannot be restarted")
default:
stdout, err := cmd.RunDefaultWithStdoutBashC("systemctl is-active docker")
if string(stdout) == "active\n" && err == nil {
active, err := controller.CheckActive("docker")
if active && err != nil {
global.LOG.Info("docker restart with new conf successful!")
return nil
}
+339
View File
@@ -0,0 +1,339 @@
package service
import (
"errors"
"fmt"
"net"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/firewall/client"
"github.com/1Panel-dev/1Panel/agent/utils/firewall/client/iptables"
)
type IIptablesService interface {
Search(req dto.SearchPageWithType) (int64, interface{}, error)
OperateRule(req dto.IptablesRuleOp) error
BatchOperate(req dto.IptablesBatchOperate) error
LoadChainStatus(req dto.OperationWithName) dto.IptablesChainStatus
Operate(req dto.IptablesOp) error
}
type IptablesService struct{}
func NewIIptablesService() IIptablesService {
return &IptablesService{}
}
func (s *IptablesService) Search(req dto.SearchPageWithType) (int64, interface{}, error) {
rules, err := iptables.ReadFilterRulesByChain(req.Type)
if err != nil {
return 0, nil, fmt.Errorf("failed to read iptables rules: %w", err)
}
var records []iptables.FilterRules
total, start, end := len(rules), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
records = make([]iptables.FilterRules, 0)
} else {
if end >= total {
end = total
}
records = rules[start:end]
}
rulesInDB, _ := hostRepo.ListFirewallRecord(hostRepo.WithByChain(req.Type))
for i := 0; i < len(records); i++ {
for _, item := range rulesInDB {
if records[i].Strategy == item.Strategy &&
records[i].DstIP == item.DstIP &&
fmt.Sprintf("%v", records[i].DstPort) == item.DstPort &&
records[i].Protocol == item.Protocol &&
records[i].SrcIP == item.SrcIP &&
fmt.Sprintf("%v", records[i].SrcPort) == item.SrcPort {
records[i].ID = item.ID
records[i].Description = item.Description
}
}
}
return int64(total), records, nil
}
func (s *IptablesService) OperateRule(req dto.IptablesRuleOp) error {
action := "ACCEPT"
if req.Strategy == "drop" {
action = "DROP"
}
policy := iptables.FilterRules{
Protocol: req.Protocol,
SrcIP: req.SrcIP,
DstIP: req.DstIP,
Strategy: action,
}
if req.SrcPort != 0 {
policy.SrcPort = fmt.Sprintf("%v", req.SrcPort)
}
if req.DstPort != 0 {
policy.DstPort = fmt.Sprintf("%v", req.DstPort)
}
name := iptables.InputFileName
if req.Chain == iptables.Chain1PanelOutput {
name = iptables.OutputFileName
}
switch req.Operation {
case "add":
if err := s.validateRuleInput(&req); err != nil {
return err
}
if err := iptables.AddFilterRule(req.Chain, policy); err != nil {
return fmt.Errorf("failed to add iptables rule: %w", err)
}
if len(req.Description) != 0 {
rule := &model.Firewall{
Chain: req.Chain,
Protocol: req.Protocol,
SrcIP: req.SrcIP,
SrcPort: policy.SrcPort,
DstIP: req.DstIP,
DstPort: policy.DstPort,
Strategy: req.Strategy,
Description: req.Description,
}
if err := hostRepo.SaveFirewallRecord(rule); err != nil {
return fmt.Errorf("failed to save rule to database: %w", err)
}
}
case "remove":
if err := iptables.DeleteFilterRule(req.Chain, policy); err != nil {
return fmt.Errorf("failed to remove iptables rule: %w", err)
}
if req.ID != 0 {
if err := hostRepo.DeleteFirewallRecordByID(req.ID); err != nil {
return fmt.Errorf("failed to delete rule from database: %w", err)
}
}
}
if err := iptables.SaveRulesToFile(iptables.FilterTab, req.Chain, name); err != nil {
global.LOG.Errorf("persistence for %s failed, err: %v", iptables.Chain1PanelBasic, err)
}
return nil
}
func (s *IptablesService) BatchOperate(req dto.IptablesBatchOperate) error {
for _, rule := range req.Rules {
if err := s.OperateRule(rule); err != nil {
return err
}
}
return nil
}
func (s *IptablesService) Operate(req dto.IptablesOp) error {
targetChain := iptables.ChainInput
if req.Name == iptables.Chain1PanelOutput {
targetChain = iptables.ChainOutput
}
switch req.Operate {
case "init-base":
if ok := cmd.Which("iptables"); !ok {
return fmt.Errorf("failed to find iptables")
}
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelBasicBefore); err != nil {
return err
}
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelBasic); err != nil {
return err
}
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelBasicAfter); err != nil {
return err
}
if err := initPreRules(); err != nil {
return err
}
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore, 1); err != nil {
return err
}
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic, 2); err != nil {
return err
}
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {
return err
}
return nil
case "init-forward":
return client.EnableIptablesForward()
case "init-advance":
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelInput); err != nil {
return err
}
if err := iptables.AddChain(iptables.FilterTab, iptables.Chain1PanelOutput); err != nil {
return err
}
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainOutput, iptables.Chain1PanelOutput, 1); err != nil {
return err
}
number := loadBindNumber(iptables.Chain1PanelInput)
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelInput, number); err != nil {
return err
}
return nil
case "bind-base":
if err := initPreRules(); err != nil {
return err
}
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore, 1); err != nil {
return err
}
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic, 2); err != nil {
return err
}
if err := iptables.BindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter, 3); err != nil {
return err
}
return nil
case "unbind-base":
if err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicAfter); err != nil {
return err
}
if err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasicBefore); err != nil {
return err
}
if err := iptables.UnbindChain(iptables.FilterTab, iptables.ChainInput, iptables.Chain1PanelBasic); err != nil {
return err
}
return nil
case "bind":
if err := iptables.BindChain(iptables.FilterTab, targetChain, req.Name, loadBindNumber(req.Name)); err != nil {
return err
}
return nil
case "unbind":
if err := iptables.UnbindChain(iptables.FilterTab, targetChain, req.Name); err != nil {
return err
}
return nil
}
return nil
}
func (s *IptablesService) LoadChainStatus(req dto.OperationWithName) dto.IptablesChainStatus {
var data dto.IptablesChainStatus
var err error
data.DefaultStrategy, err = iptables.LoadDefaultStrategy(req.Name)
if err != nil {
global.LOG.Error(err)
}
switch req.Name {
case iptables.Chain1PanelBasic:
data.IsBind, err = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)
case iptables.Chain1PanelInput:
data.IsBind, err = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainInput, req.Name)
case iptables.Chain1PanelOutput:
data.IsBind, err = iptables.CheckChainBind(iptables.FilterTab, iptables.ChainOutput, req.Name)
}
return data
}
func (s *IptablesService) validateRuleInput(req *dto.IptablesRuleOp) error {
if req.Protocol != "" {
validProtocols := map[string]bool{"tcp": true, "udp": true, "icmp": true, "all": true}
if !validProtocols[strings.ToLower(req.Protocol)] {
return fmt.Errorf("invalid protocol: %s, must be tcp, udp, icmp or all", req.Protocol)
}
}
if req.SrcIP != "" {
if err := s.validateIPOrCIDR(req.SrcIP); err != nil {
return fmt.Errorf("invalid source IP: %w", err)
}
}
if req.DstIP != "" {
if err := s.validateIPOrCIDR(req.DstIP); err != nil {
return fmt.Errorf("invalid destination IP: %w", err)
}
}
if req.SrcPort > 65535 {
return fmt.Errorf("invalid source port: %d, must be between 1 and 65535", req.SrcPort)
}
if req.DstPort > 65535 {
return fmt.Errorf("invalid destination port: %d, must be between 1 and 65535", req.DstPort)
}
if (req.SrcPort > 0 || req.DstPort > 0) && req.Protocol == "" {
return fmt.Errorf("port specification requires protocol (tcp/udp)")
}
return nil
}
func (s *IptablesService) validateIPOrCIDR(ipStr string) error {
if strings.Contains(ipStr, "/") {
_, _, err := net.ParseCIDR(ipStr)
if err != nil {
return fmt.Errorf("invalid CIDR format: %w", err)
}
return nil
}
ip := net.ParseIP(ipStr)
if ip == nil {
return fmt.Errorf("invalid IP address format")
}
return nil
}
func loadBindNumber(chain string) int {
if chain == iptables.Chain1PanelOutput {
return 1
}
number := 1
if exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasicBefore); exist {
number++
}
if exist, _ := iptables.CheckChainExist(iptables.FilterTab, iptables.Chain1PanelBasic); exist {
number++
}
return number
}
func initPreRules() error {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.IoRuleIn); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, iptables.EstablishedRule); err != nil {
return err
}
panelPort := ""
if !global.IsMaster {
panelPort = global.CONF.Base.Port
} else {
var portSetting model.Setting
_ = global.CoreDB.Where("key = ?", "ServerPort").First(&portSetting).Error
if len(portSetting.Value) != 0 {
panelPort = portSetting.Value
}
}
if len(panelPort) == 0 {
return errors.New("find 1panel service port failed")
}
ports := []string{"80", "443", panelPort, loadSSHPort()}
for _, item := range ports {
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicBefore, fmt.Sprintf("-p tcp -m tcp --dport %v -j ACCEPT", item)); err != nil {
return err
}
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllTcp); err != nil {
return err
}
if err := iptables.AddRule(iptables.FilterTab, iptables.Chain1PanelBasicAfter, iptables.DropAllUdp); err != nil {
return err
}
return nil
}
+5 -2
View File
@@ -10,7 +10,7 @@ import (
"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/cmd/server/mcp"
"github.com/1Panel-dev/1Panel/agent/cmd/server/ai"
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
@@ -565,7 +565,7 @@ func handleEnv(mcpServer *model.McpServer) gotenv.Env {
func handleCreateParams(mcpServer *model.McpServer, environments []request.Environment, volumes []request.Volume) error {
var composeContent []byte
if mcpServer.ID == 0 {
composeContent = mcp.DefaultMcpCompose
composeContent = ai.DefaultMcpCompose
} else {
composeContent = []byte(mcpServer.DockerCompose)
}
@@ -680,6 +680,9 @@ func GetWebsiteID() uint {
}
func pullImage(imageType string) {
if global.CONF.Base.IsOffLine {
return
}
if imageType == "npx" {
if err := docker.PullImage("supercorp/supergateway:latest"); err != nil {
global.LOG.Errorf("docker pull mcp image error: %s", err.Error())
+150 -12
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"sort"
"strconv"
"time"
@@ -21,6 +22,7 @@ import (
"github.com/shirou/gopsutil/v4/load"
"github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net"
"github.com/shirou/gopsutil/v4/process"
)
type MonitorService struct {
@@ -64,12 +66,24 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
itemData.Param = "base"
for _, base := range bases {
itemData.Date = append(itemData.Date, base.CreatedAt)
if req.Param == "all" || req.Param == "cpu" {
var processes []dto.Process
_ = json.Unmarshal([]byte(base.TopCPU), &processes)
base.TopCPUItems = processes
base.TopCPU = ""
}
if req.Param == "all" || req.Param == "mem" {
var processes []dto.Process
_ = json.Unmarshal([]byte(base.TopMem), &processes)
base.TopMemItems = processes
base.TopMem = ""
}
itemData.Value = append(itemData.Value, base)
}
data = append(data, itemData)
}
if req.Param == "all" || req.Param == "io" {
bases, err := monitorRepo.GetIO(repo.WithByCreatedAt(req.StartTime, req.EndTime))
bases, err := monitorRepo.GetIO(repo.WithByName(req.IO), repo.WithByCreatedAt(req.StartTime, req.EndTime))
if err != nil {
return nil, err
}
@@ -83,7 +97,7 @@ func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorDa
data = append(data, itemData)
}
if req.Param == "all" || req.Param == "network" {
bases, err := monitorRepo.GetNetwork(repo.WithByName(req.Info), repo.WithByCreatedAt(req.StartTime, req.EndTime))
bases, err := monitorRepo.GetNetwork(repo.WithByName(req.Network), repo.WithByCreatedAt(req.StartTime, req.EndTime))
if err != nil {
return nil, err
}
@@ -169,8 +183,14 @@ func (m *MonitorService) Run() {
if len(totalPercent) == 1 {
itemModel.Cpu = totalPercent[0]
}
topCPU := loadTopCPU()
if len(topCPU) != 0 {
topItemCPU, err := json.Marshal(topCPU)
if err == nil {
itemModel.TopCPU = string(topItemCPU)
}
}
cpuCount, _ := cpu.Counts(false)
loadInfo, _ := load.Avg()
itemModel.CpuLoad1 = loadInfo.Load1
itemModel.CpuLoad5 = loadInfo.Load5
@@ -179,6 +199,13 @@ func (m *MonitorService) Run() {
memoryInfo, _ := mem.VirtualMemory()
itemModel.Memory = memoryInfo.UsedPercent
topMem := loadTopMem()
if len(topMem) != 0 {
topMemItem, err := json.Marshal(topMem)
if err == nil {
itemModel.TopMem = string(topMemItem)
}
}
if err := settingRepo.CreateMonitorBase(itemModel); err != nil {
global.LOG.Errorf("Insert basic monitoring data failed, err: %v", err)
@@ -201,9 +228,18 @@ func (m *MonitorService) Run() {
func (m *MonitorService) loadDiskIO() {
ioStat, _ := disk.IOCounters()
var diskIOList []disk.IOCountersStat
var ioStatAll disk.IOCountersStat
for _, io := range ioStat {
ioStatAll.Name = "all"
ioStatAll.ReadBytes += io.ReadBytes
ioStatAll.WriteBytes += io.WriteBytes
ioStatAll.ReadTime += io.ReadTime
ioStatAll.WriteTime += io.WriteTime
ioStatAll.WriteCount += io.WriteCount
ioStatAll.ReadCount += io.ReadCount
diskIOList = append(diskIOList, io)
}
diskIOList = append(diskIOList, ioStatAll)
m.DiskIO <- diskIOList
}
@@ -234,29 +270,29 @@ func (m *MonitorService) saveIODataToDB(ctx context.Context, interval float64) {
var itemIO model.MonitorIO
itemIO.Name = io1.Name
if io2.ReadBytes != 0 && io1.ReadBytes != 0 && io2.ReadBytes > io1.ReadBytes {
itemIO.Read = uint64(float64(io2.ReadBytes-io1.ReadBytes) / interval / 60)
itemIO.Read = uint64(float64(io2.ReadBytes-io1.ReadBytes) / interval)
}
if io2.WriteBytes != 0 && io1.WriteBytes != 0 && io2.WriteBytes > io1.WriteBytes {
itemIO.Write = uint64(float64(io2.WriteBytes-io1.WriteBytes) / interval / 60)
itemIO.Write = uint64(float64(io2.WriteBytes-io1.WriteBytes) / interval)
}
if io2.ReadCount != 0 && io1.ReadCount != 0 && io2.ReadCount > io1.ReadCount {
itemIO.Count = uint64(float64(io2.ReadCount-io1.ReadCount) / interval / 60)
itemIO.Count = uint64(float64(io2.ReadCount-io1.ReadCount) / interval)
}
writeCount := uint64(0)
if io2.WriteCount != 0 && io1.WriteCount != 0 && io2.WriteCount > io1.WriteCount {
writeCount = uint64(float64(io2.WriteCount-io1.WriteCount) / interval * 60)
writeCount = uint64(float64(io2.WriteCount-io1.WriteCount) / interval)
}
if writeCount > itemIO.Count {
itemIO.Count = writeCount
}
if io2.ReadTime != 0 && io1.ReadTime != 0 && io2.ReadTime > io1.ReadTime {
itemIO.Time = uint64(float64(io2.ReadTime-io1.ReadTime) / interval / 60)
itemIO.Time = uint64(float64(io2.ReadTime-io1.ReadTime) / interval)
}
writeTime := uint64(0)
if io2.WriteTime != 0 && io1.WriteTime != 0 && io2.WriteTime > io1.WriteTime {
writeTime = uint64(float64(io2.WriteTime-io1.WriteTime) / interval / 60)
writeTime = uint64(float64(io2.WriteTime-io1.WriteTime) / interval)
}
if writeTime > itemIO.Time {
itemIO.Time = writeTime
@@ -294,10 +330,10 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64)
itemNet.Name = net1.Name
if net2.BytesSent != 0 && net1.BytesSent != 0 && net2.BytesSent > net1.BytesSent {
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / interval / 60
itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / interval
}
if net2.BytesRecv != 0 && net1.BytesRecv != 0 && net2.BytesRecv > net1.BytesRecv {
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / interval / 60
itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / interval
}
netList = append(netList, itemNet)
break
@@ -314,6 +350,108 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64)
}
}
func loadTopCPU() []dto.Process {
processes, err := process.Processes()
if err != nil {
return nil
}
top5 := make([]dto.Process, 0, 5)
for _, p := range processes {
percent, err := p.CPUPercent()
if err != nil {
continue
}
minIndex := 0
if len(top5) >= 5 {
minCPU := top5[0].Percent
for i := 1; i < len(top5); i++ {
if top5[i].Percent < minCPU {
minCPU = top5[i].Percent
minIndex = i
}
}
if percent < minCPU {
continue
}
}
name, err := p.Name()
if err != nil {
name = "undifine"
}
cmd, err := p.Cmdline()
if err != nil {
cmd = "undifine"
}
user, err := p.Username()
if err != nil {
user = "undifine"
}
if len(top5) == 5 {
top5[minIndex] = dto.Process{Percent: percent, Pid: p.Pid, User: user, Name: name, Cmd: cmd}
} else {
top5 = append(top5, dto.Process{Percent: percent, Pid: p.Pid, User: user, Name: name, Cmd: cmd})
}
}
sort.Slice(top5, func(i, j int) bool {
return top5[i].Percent > top5[j].Percent
})
return top5
}
func loadTopMem() []dto.Process {
processes, err := process.Processes()
if err != nil {
return nil
}
top5 := make([]dto.Process, 0, 5)
for _, p := range processes {
stat, err := p.MemoryInfo()
if err != nil {
continue
}
memItem := stat.RSS
minIndex := 0
if len(top5) >= 5 {
min := top5[0].Memory
for i := 1; i < len(top5); i++ {
if top5[i].Memory < min {
min = top5[i].Memory
minIndex = i
}
}
if memItem < min {
continue
}
}
name, err := p.Name()
if err != nil {
name = "undifine"
}
cmd, err := p.Cmdline()
if err != nil {
cmd = "undifine"
}
user, err := p.Username()
if err != nil {
user = "undifine"
}
percent, _ := p.MemoryPercent()
if len(top5) == 5 {
top5[minIndex] = dto.Process{Percent: float64(percent), Pid: p.Pid, User: user, Name: name, Cmd: cmd, Memory: memItem}
} else {
top5 = append(top5, dto.Process{Percent: float64(percent), Pid: p.Pid, User: user, Name: name, Cmd: cmd, Memory: memItem})
}
}
sort.Slice(top5, func(i, j int) bool {
return top5[i].Memory > top5[j].Memory
})
return top5
}
func StartMonitor(removeBefore bool, interval string) error {
if removeBefore {
monitorCancel()
@@ -330,7 +468,7 @@ func StartMonitor(removeBefore bool, interval string) error {
now := time.Now()
nextMinute := now.Truncate(time.Minute).Add(time.Minute)
time.AfterFunc(time.Until(nextMinute), func() {
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %sm", interval), service)
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %ss", interval), service)
if err != nil {
return
}

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