Compare commits

..
491 Commits
Author SHA1 Message Date
蘭 99bc8177d4 feat: Save custom theme color scheme (#7690) 2025-01-10 04:13:44 +00:00
ssongliu cf2a1372f2 style: Optimize some internationalization styles (#7689) 2025-01-10 04:11:44 +00:00
蘭 42c30f324a feat: Improve international language (#7687) 2025-01-10 02:21:44 +00:00
蘭 dab83fee20 feat: Modify international version functions (#7686) 2025-01-10 02:19:44 +00:00
蘭 6f3c5586fc feat: Update international address (#7685) 2025-01-10 02:17:43 +00:00
ssongliu aab8df6abc fix: Fix the issue of database cover exception display. (#7682) 2025-01-09 13:59:41 +00:00
蘭 b9be79b05a feat: Menu enables vue-router mode (#7681) 2025-01-09 12:15:42 +00:00
zhengkunwang ec3024f2c3 feat: Change AppDto to AppItem (#7680) 2025-01-09 12:13:41 +00:00
ssongliu 070f61e229 fix: Fix the issue of pagination errors in the backup list (#7673) 2025-01-09 03:07:40 +00:00
蘭 a82a4ebfc5 style: Optimize style and layout (#7671) 2025-01-08 10:19:37 +00:00
蘭 87009ddb27 feat: Modify the copyright (#7670) 2025-01-08 07:39:37 +00:00
蘭 3650ff27b3 feat: Dealing with Sass version compatibility issues and removing debugger from build (#7669) 2025-01-08 07:21:38 +00:00
ssongliu 6c08a3114a fix: Fix the issue of interface returning internationalization failure (#7668) 2025-01-08 07:19:37 +00:00
蘭 381cb61185 feat: Internationalization language order (#7666) 2025-01-08 03:33:37 +00:00
蘭 8d69da7702 feat: Break long menu items into multiple lines (#7665) 2025-01-08 03:31:36 +00:00
ssongliu 8922395fdc fix: Fix the issue of frontend packaging errors (#7661) 2025-01-07 06:47:35 +00:00
ssongliu 83db40e261 feat: Support for asynchronously obtaining the backup file size (#7660) 2025-01-07 06:35:33 +00:00
zhengkunwang 95ec6c62ef feat(system): Fix Issue Allowing Access via Old Secure Entry After Mo… (#7659)
Refs https://github.com/1Panel-dev/1Panel/issues/7658
2025-01-07 06:09:32 +00:00
蘭 56fc9ea428 feat: Improve international language proficiency (#7656) 2025-01-07 04:19:33 +00:00
蘭 88c6fc050b feat: Upgrade package version (#7655) 2025-01-07 04:03:33 +00:00
蘭 2392f91567 feat: Panel aliases support common special characters (#7654)
Refs #6909
2025-01-07 04:01:33 +00:00
蘭 5f0c740eb0 feat: Improve and format language files (#7645) 2025-01-06 05:48:21 +00:00
Lucas Pedro 120d0dd7c6 Traducao pt br (#7624) 2025-01-06 10:57:34 +08:00
liuchang_cloud 2844f6db7f feat: Support Japanese by Google Translate (#7437) 2025-01-06 10:39:49 +08:00
王贺 570074f92d Update README.md 2025-01-04 10:56:43 +08:00
Anye a13047c855 Update Default PHP Extensions (#7637) 2025-01-04 10:44:41 +08:00
蘭 ca0d24bf56 feat: Improve and format language files (#7634) 2025-01-03 08:52:09 +00:00
Pisyek K. 2b18251360 chore: add bahasa melayu translation (#7623)
* chore: add bahasa melayu translation
2025-01-03 11:42:13 +08:00
蘭 f7c875acda feat: Save the theme color scheme to the client (#7626)
Refs #7074
2025-01-03 02:30:07 +00:00
ssongliu a291a50c9f feat: Support for license import in the international version (#7625) 2025-01-02 15:00:05 +00:00
蘭 5bc7bc6aac fix: Fix missing response types in Swagger API endpoints (#7611)
Refs #7607
2024-12-31 09:57:55 +00:00
zhengkunwang 9c376300ff feat(ssl): Add Support for Rain Yun in DNS Account (#7613)
Refs https://github.com/1Panel-dev/1Panel/issues/7115
2024-12-31 09:55:54 +00:00
Ryan Wang 5187b20252 fix: correct Swagger API definitions (#7606)
Signed-off-by: Ryan Wang <i@ryanc.cc>
2024-12-31 10:32:47 +08:00
ssongliu fc401ffd8f fix: Optimize image deletion error return (#7602)
Refs #7068
2024-12-30 09:17:49 +00:00
zhengkunwang 667ee5256e fix: Fix Application Startup Failure Caused by Case-Sensitive Applica… (#7601)
Refs https://github.com/1Panel-dev/1Panel/issues/7600
2024-12-30 09:15:49 +00:00
wanghe-fit2cloud f8356de93c Modify the copyright 2024-12-30 16:38:54 +08:00
endymx 55bb997d51 feat(iptables): Support forwarding for complex networks (#7598)
* feat(iptables): 支持对复杂网络的转发

* refactor(iptables): 提供更完善的接口和报错
2024-12-30 13:55:53 +08:00
ssongliu 7c46f54e15 fix: Fix the issue of failure to retrieve firewall status (#7597)
Refs #7527
2024-12-30 05:37:49 +00:00
ssongliu 6dbb6d2247 fix: Fix the issue of backup failure due to complex passwords in the pg (#7596) 2024-12-30 13:36:51 +08:00
ssongliu 9d0ae45edc feat: Optimize the display of scheduled task execution cycles (#7595) 2024-12-30 05:35:49 +00:00
蘭 b467dfa3b2 feat: Add expiration time setting for API key (#7584) 2024-12-30 05:33:49 +00:00
蘭 c13387bc70 style: Optimize the display of status cards on the overview page (#7562)
* style: Optimize the display of status cards on the overview page
2024-12-27 14:01:02 +08:00
BugKing 41b2523750 docs: replace Discord link in readme (#7573) 2024-12-26 18:32:50 +08:00
zhengkunwang afe82bd4be feat: Remove Default Configuration for Reverse Proxy Cache (#7566) 2024-12-26 10:16:07 +08:00
JB-SelfCompany 0f296fb70b Update README for Russian language (#7560) 2024-12-25 16:37:06 +08:00
JB-SelfCompanyandBubblegum d96ccd06a3 Russian language support. (#7554)
* Add Russian language

* Use prettier

---------

Co-authored-by: Bubblegum <jack_benq@selfhosted.lan>
2024-12-25 09:34:50 +08:00
BugKing 14095affa9 docs: add discord badge in readme (#7544) 2024-12-24 14:03:05 +08:00
Anye ed60565bc9 Update cache.conf (#7516)
Remove `proxy_cache ` from the global config to prevent it from being enabled for sites that don't need caching.
2024-12-23 11:21:23 +08:00
david082321 8313912c4f fix: Update zh-Hant translation (#7515) 2024-12-21 20:34:24 +08:00
wolf 6274015d06 fix: Optimize some translations (#7495) 2024-12-20 18:45:45 +08:00
蘭 d195ce9155 feat: Login to access Swagger documentation (#7496) 2024-12-20 18:37:59 +08:00
Flyum cd0cd8a152 fix: Modify some English translations (#7494) 2024-12-20 18:06:44 +08:00
liuboF2candliubo 1f45ed6b06 fix: Correct English internationalization translations (#7493)
Co-authored-by: liubo <liubo@fit2cloud.com>
2024-12-20 18:05:58 +08:00
zhengkunwang 7f6ad89edf feat: Optimize some translations (#7492) 2024-12-20 17:48:52 +08:00
liuboF2candliubo 5cdea288e2 fix: 修正英文国际化翻译 (#7491)
Co-authored-by: liubo <liubo@fit2cloud.com>
2024-12-20 17:29:43 +08:00
wanghe-fit2cloud e56feaf826 docs: Optimize README 2024-12-20 16:35:23 +08:00
zhengkunwang 1288a09647 fix: Optimize some translations (#7490) 2024-12-20 16:33:35 +08:00
wanghe-fit2cloud 0cdaafa1c0 docs: Optimize README 2024-12-20 16:21:23 +08:00
Frist soulful 0b3334253c Update zh-Hant.yaml (#7489) 2024-12-20 16:20:31 +08:00
Yuki Guo 10f72f12d2 docs: Update tw.ts (#7487) 2024-12-20 16:08:07 +08:00
feng626 01643b2816 perf: Optimize FTP sync function for better performance and readability (#7479) 2024-12-20 15:45:05 +08:00
zrfit 56e0da4fe3 docs: Update README.tr.md (#7486) 2024-12-20 15:41:27 +08:00
song-cc-rock 144c397bf4 fix: update zh-hant spell 2024-12-20 15:34:57 +08:00
ZhaoJiSen e06541984f Add ImportMeta type definition (#7481) 2024-12-20 15:32:58 +08:00
zhengkunwang ea7e92db84 feat: Remove Redundant Code (#7484) 2024-12-20 07:32:25 +00:00
wxg 8d48640c1d fix: Update zh-Hant.yaml (#7483) 2024-12-20 15:31:25 +08:00
zrfit cfe5cb4b6b Add File README.tr.md (#7480) 2024-12-20 15:25:53 +08:00
9527 05c4d7dd38 docs: Add German README (#7482) 2024-12-20 15:20:45 +08:00
wangyicheng 13c6e0df99 docs: Update README.es.md (#7477) 2024-12-20 15:11:10 +08:00
MignonZhang dafd6ba71e fix: fix typos (#7478) 2024-12-20 15:09:26 +08:00
莫鹍 2751c37716 refactor(i18n): fix spelling error (#7476) 2024-12-20 15:08:22 +08:00
蘭 50c01762e7 feat: Open international professional features (#7472) 2024-12-20 07:04:26 +00:00
w940853815 a77ebd3eb6 fix: Typo in hostname save function and button click (#7475) 2024-12-20 15:02:30 +08:00
gerry-f2c d84f0b5597 style: Optimize the backup account style code (#7474) 2024-12-20 15:00:14 +08:00
Slooop a3cdf87ea2 style: Optimize the backup account style code (#7473) 2024-12-20 14:59:57 +08:00
Chenyang Shen 8ab3c16b3d Update zh-Hant.yaml 2024-12-20 14:54:24 +08:00
Shenguobin0102 18e943fd06 fix: Modified the RTL format to right-align the first sentence of the body (#7470) 2024-12-20 14:52:53 +08:00
zyyfit 3b7a067dda Create README.fr.md (#7469) 2024-12-20 14:24:09 +08:00
ZhaoQian1222 4636ba815b fix: Corrected some internationalization translations (#7468) 2024-12-20 14:22:30 +08:00
Shenguobin0102 317fad2a28 docs: Update README.ar.md (#7467) 2024-12-20 14:18:31 +08:00
MatataYou 0ae28a1dd9 Update README.es.md (#7466) 2024-12-20 14:05:28 +08:00
Anye bbfe5f1d96 style: Modify some styles to improve the appearance of the version button (#7464) 2024-12-20 13:59:02 +08:00
zhengkunwang b713f20b66 style: Optimize OpenResty Warnings Displayed During Zooming (#7465)
Refs https://github.com/1Panel-dev/1Panel/issues/7452
2024-12-20 05:50:25 +00:00
Shenguobin0102 3eb7a5adc5 docs: Add Arabic README (#7463) 2024-12-20 13:49:23 +08:00
MatataYou 956af78749 docs: Add Spanish Language README (#7461) 2024-12-20 13:43:55 +08:00
zhengkunwang 69ad01687e feat: Optimize Location Information Display in System Login Logs (#7459) 2024-12-20 04:16:25 +00:00
Anye e83d07e88e style: Modify some styles to improve the appearance of the version button (#7462) 2024-12-20 12:03:49 +08:00
zhengkunwang 6683696032 fix(system): Fix Cloudflare Proxy Access Issue with 1Panel When Secure Entry Is Enabled (#7458) 2024-12-20 11:03:49 +08:00
zhengkunwang 3396a5ffea go: upgraded golang.org/x/crypto v0.28.0 => v0.31.0 (#7456) 2024-12-20 02:26:24 +00:00
zhengkunwang 18a14d3b75 go: upgraded golang.org/x/net v0.30.0 => v0.33.0 (#7455) 2024-12-20 02:14:23 +00:00
王贺 2b13352004 chore: Add Indonesian language (#7445) 2024-12-19 23:20:48 +08:00
maninhill 94e5d7e642 chore: Update README.md 2024-12-19 22:42:35 +08:00
maninhill 001b4dbada docs: Create README.id.md 2024-12-19 22:41:22 +08:00
BugKing 1192dec689 docs: Add Product Hunt badge (#7434) 2024-12-19 11:55:18 +08:00
蘭 956d47f9da refactor: Demo server, prohibit operation (#7433) 2024-12-19 02:06:19 +00:00
John Niang 063a071228 Fix invalid postion of before content in home page in Firefox Linux (#7429) 2024-12-18 18:33:59 +08:00
Ryan Wang c54a7de827 docs: add Korean README documentation (#7427)
Signed-off-by: Ryan Wang <i@ryanc.cc>
2024-12-18 17:15:10 +08:00
老广 0410e48aad docs: Add README.pt-br.md (#7425) 2024-12-18 16:29:44 +08:00
liuchang_cloud a4d95737d3 docs: Update README.md (#7424) 2024-12-18 16:16:14 +08:00
jianneng-fit2cloud bc64d83ab2 style: Container style (#7422) 2024-12-18 15:32:30 +08:00
蘭 87616e388b style: Optimize style (#7414)
Refs #7419
2024-12-18 06:44:14 +00:00
taojinlongandtaojinlong 3f35f90fc2 fix: fix typos (#7413)
Co-authored-by: taojinlong <jinlong@fit2cloud.com>
2024-12-18 14:06:00 +08:00
王嘉豪 c3ba69814c Update fa.yaml (#7411) 2024-12-18 14:05:46 +08:00
wisonic-s 66a517e58d fix: fix typos (#7407) 2024-12-18 14:05:24 +08:00
Bryan 3108c927af fix: typos (#7405) 2024-12-18 14:04:51 +08:00
Junjun 5738cceac9 fix typos (#7404) 2024-12-18 14:04:17 +08:00
dataeaseShu db2f648bce fix: fix typos (#7403) 2024-12-18 14:04:06 +08:00
fit2cloud-chenyw 6012a6cf9f fix: datas should be dataList (#7402) 2024-12-18 14:03:56 +08:00
ulleo a4dcbc90e6 fix: fix typos (#7401) 2024-12-18 14:03:40 +08:00
BugKing 955ecce952 docs: Update README.md (#7416) 2024-12-18 13:34:56 +08:00
maninhill 91fba21ed3 chore: Update README.md 2024-12-18 11:55:20 +08:00
wanghe-fit2cloud 490e2934b8 chore: Optimize the README file 2024-12-18 11:54:06 +08:00
wanghe-fit2cloud cb6d3d4fc3 chore: Optimize the README file 2024-12-18 11:52:05 +08:00
xuwei-fit2cloud 7f2a09eab3 fix: fix typos 2024-12-18 10:25:43 +08:00
John Niang c522a308b9 Refine English translation and UI style (#7399) 2024-12-18 10:24:00 +08:00
ssongliu dde686d55f style: Increase the height of the script input box (#7398) 2024-12-18 02:12:14 +00:00
maninhill 2f0b4888de chore: Update README_EN.md 2024-12-18 09:37:56 +08:00
ssongliu 35e7b11709 style: Increase the height of the script input box (#7396) 2024-12-17 15:42:11 +00:00
ssongliu 6cf3f158c7 fix: Improve cache cleaning for internationalization (#7395) 2024-12-17 15:10:11 +00:00
蘭 4944e705dc fix: Fix the issue of clearing cache title judgment (#7394) 2024-12-17 15:02:11 +00:00
蘭 3b38b9d86b feat: Optimize style and description (#7393) 2024-12-17 12:46:11 +00:00
zhengkunwang 6275eec918 style: change some label (#7392) 2024-12-17 12:44:11 +00:00
zhengkunwang ad652ca6f5 style: change some label (#7387) 2024-12-17 08:50:09 +00:00
zhengkunwang 7029d985c6 style: change some column width (#7385) 2024-12-17 06:44:08 +00:00
ssongliu 9539fd40eb fix: Fix the issue of compose name validation errors (#7382)
Refs #7380
2024-12-17 04:16:12 +00:00
蘭 4c03a482bd feat: Modify document address (#7381) 2024-12-17 02:28:12 +00:00
蘭 f58ed5614e feat: Optimize style and description (#7378) 2024-12-16 14:16:10 +00:00
zhengkunwang 198e78e0b4 feat: change default language (#7377) 2024-12-16 11:04:10 +00:00
wanghe-fit2cloud 1f16e09daa chore: Optimize the prompt messages on some pages 2024-12-16 18:27:15 +08:00
蘭 16f47f8664 fix: Fix the issue of incorrect display of prompts on the English language login page (#7376) 2024-12-16 09:40:10 +00:00
蘭 40604c7880 feat: Optimize international document addresses (#7372) 2024-12-16 09:38:09 +00:00
ssongliu fa86c4f39a fix: Fixed an issue where the language pack initialization download f… (#7375) 2024-12-16 09:00:10 +00:00
zhengkunwang 7f3cc784cd feat: Fix Application Store Style Issue Under Poor Network Conditions (#7374)
Refs https://github.com/1Panel-dev/1Panel/issues/7370
2024-12-16 08:58:09 +00:00
zhengkunwang 836f6c3520 feat: Fix Translation Issue in Input Field Placeholder on First Login (#7369) 2024-12-16 05:46:09 +00:00
蘭 2ff1e3a245 style: Optimize international style layout (#7368) 2024-12-16 05:44:08 +00:00
wanghe-fit2cloud f7078a6140 chore: Update the overview image 2024-12-16 11:42:49 +08:00
ssongliu 141786e4e9 fix: FIx the issue of container editing not opening successfully (#7360) 2024-12-13 14:35:56 +00:00
蘭 2d098f8078 style: Optimize layout style (#7359) 2024-12-13 14:33:56 +00:00
John Niang 229254e3d3 Replace whitelist and blacklist with allowlist and blocklist (#7358) 2024-12-13 22:33:07 +08:00
刘瑞斌 96329967ba feat: CornJob shell script syntax highlighting (#7357) 2024-12-13 21:31:58 +08:00
ssongliu 0d988bbd6a pref(system): Optimize and upgrade related internationalization translations. (#7356) 2024-12-12 15:57:50 +00:00
蘭 1ff4f33ceb style: Optimize layout style (#7354) 2024-12-12 22:20:21 +08:00
John Niang c95d61397d Refine English translation again and again (#7353) 2024-12-12 18:46:01 +08:00
蘭 590238dc13 style: Optimize layout style (#7352) 2024-12-12 10:19:50 +00:00
蘭 ed1b2613f2 style: Code Style Format (#7351) 2024-12-12 09:33:49 +00:00
蘭 c0e0db670c feat: Adjust the empty status prompt layout (#7350) 2024-12-12 09:03:50 +00:00
蘭 9132a2c4a9 fix: Fixed the bug of displaying prompt information for first-time login (#7348) 2024-12-12 09:01:51 +00:00
蘭 5e880ba25e feat: Language pack content adjustment (#7349) 2024-12-12 08:59:50 +00:00
蘭 5a1928bee6 feat: Optimize English description and layout (#7346) 2024-12-12 07:23:49 +00:00
ssongliu 82e9e72c70 feat: Fix the initialization errors of GeoIP (#7345) 2024-12-12 06:55:49 +00:00
ssongliu c35f55f70c pref: Optimize the button style for database connection information (#7342) 2024-12-12 03:53:48 +00:00
ssongliu 0542b39373 pref: Optimize the button style for database connection information (#7341) 2024-12-12 02:37:49 +00:00
igophper 013cf21863 fix: fixed the issue of abnormal log request (#7338)
Refs #7339
2024-12-12 10:36:41 +08:00
dependabot[bot] c648f9b14a build(deps): bump golang.org/x/crypto from 0.28.0 to 0.31.0 (#7340)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.28.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.28.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-12 10:36:01 +08:00
igophper d9407c425f fix: correct typos in typescript interface names (#7337) 2024-12-12 09:51:21 +08:00
igophper 2c8dad980b refactor(init): run docker network creation and firewall setup in a goroutine (#7336) 2024-12-11 22:33:37 +08:00
zhengkunwang 75d94f5b89 feat: Fix 500 Error After Setting Authorized IP (#7334) 2024-12-11 10:47:45 +00:00
蘭 4d760d3019 feat: Some adjustments to internationalization (#7332) 2024-12-11 10:45:45 +00:00
ssongliu 20c9230291 feat(init): Add support for geo ip pack initialization (#7333) 2024-12-11 18:44:18 +08:00
liqiang-fit2cloud 02c3a34f13 build: try another LLM for code review. 2024-12-11 17:27:52 +08:00
Meng Zhuo c2dadc2b92 fix: typo in makefile (#7327) 2024-12-11 16:07:26 +08:00
ssongliu f5de005ad7 feat: Add YAML format to CodeMirror (#7326)
Refs #7274
2024-12-11 07:47:43 +00:00
zhengkunwang 335a82332f feat: Handle HTTP 444 Status Code (#7325) 2024-12-11 06:57:43 +00:00
wanghe-fit2cloud a81cb5c1ab chore: Remove some unused variables 2024-12-11 14:31:59 +08:00
ssongliu aaf1d86a55 fix(database-pg): Fixed the issue of PostgreSQL not being able to back up. (#7324) 2024-12-11 06:25:43 +00:00
蘭 cbad4dd172 style: Optimize the layout of app operation buttons (#7322) 2024-12-11 06:23:43 +00:00
蘭 932da58759 fix: i18n file enhancement (#7321) 2024-12-11 06:21:43 +00:00
John Niang 01defdf0f6 Refine pluralization of some English words (#7318)
* Refine plural translation of English words

* Remove useless i18n modifier
2024-12-11 09:23:37 +08:00
王贺 b33cce3059 chore: Include the GeoIP.mmdb file when building the installation package (#7317) 2024-12-10 19:04:08 +08:00
ssongliu 17ef66aa49 feat(init): add support for international language pack initialization (#7315) 2024-12-10 18:36:53 +08:00
zhengkunwang f01d770fec feat: Fix Missing CPU and Memory Limit Warnings When Editing Installe… (#7313) 2024-12-10 09:55:39 +00:00
wanghe-fit2cloud 06cc7fcc02 chore: Include language packs when building the installation package 2024-12-10 17:50:49 +08:00
zhengkunwang 4635047004 feat: Remove Unused Components (#7311) 2024-12-10 08:21:38 +00:00
蘭 88b70eba53 style: Improve password action buttons styling in database connection info (#7309)
Refs #7284
2024-12-10 07:59:38 +00:00
蘭 6ad48250f8 style: Optimize input to disable background color (#7308) 2024-12-10 07:57:38 +00:00
ssongliu 9ac9f76a1d fix(container): Fix the issue of container command parsing failure (#7307) 2024-12-10 07:55:39 +00:00
igophper b7702daea3 fix: 解决控制台报错问题 (#7299) 2024-12-10 15:08:58 +08:00
zhengkunwang db7180a761 feat: Add Return Pages for Unauthorized Domains and IPs (#7306) 2024-12-10 11:58:32 +08:00
蘭 dbe62082ff style: Adjusted some English layouts (#7305) 2024-12-10 03:49:37 +00:00
蘭 06bf2bbc2f fix: Fix alert setting issue (#7304) 2024-12-10 03:17:38 +00:00
zhengkunwang 162a0c543c feat: Fix Issue with DNS Account Deletion After Certificate Update Ap… (#7303)
Refs https://github.com/1Panel-dev/1Panel/issues/7300
2024-12-10 03:15:38 +00:00
蘭 2bc07c91df pref: Improve Swagger authentication (#7298) 2024-12-09 09:57:34 +00:00
蘭 3d84a5589f fix: Dealing with API interface issues (#7295) 2024-12-09 07:29:33 +00:00
BugKing b88438a0a3 chore: refile English translation (#7283) 2024-12-06 18:53:52 +08:00
王贺 b73dcc9388 Modify the issues-translate-action token (#7281) 2024-12-06 17:21:01 +08:00
Ryan Wang 12ad08a619 chore: fix phantom dependency issue (#7279)
Signed-off-by: Ryan Wang <i@ryanc.cc>
2024-12-06 17:16:19 +08:00
王贺 0c000a4994 fix: Modify the default open source license in Swagger (#7277)
Refs #7275
2024-12-06 08:57:20 +00:00
蘭 d2da7e2ea5 pref: Improve the API interface process (#7272) 2024-12-06 08:19:19 +00:00
蘭 873bba92db Fix: Postgres fails to connect when using special characters (#7269) 2024-12-06 10:19:41 +08:00
zhengkunwang 3e14186d3d feat: Add .NET Runtime Environment for Websites (#7266) 2024-12-05 10:05:14 +00:00
Cookie 5be325405d Fix: Postgres fails to connect when using special characters (#7262)
* Update client.go

* Update remote.go

* Update client.go
2024-12-05 16:00:52 +08:00
ssongliu 5b92b75090 feat(cmd): Add internationalization for the output of the 1pctl version (#7263) 2024-12-05 07:43:14 +00:00
ssongliu 7202de6f4c feat: Add pt-BR support for i18n (#7261) 2024-12-05 06:43:15 +00:00
zhengkunwang e6a772ebfd feat: Add System Version Restrictions for Applications (#7260) 2024-12-05 06:41:14 +00:00
John Niang f320492164 Refine English translation (#7247) 2024-12-04 21:32:59 +08:00
zhengkunwang d8fd430856 fix: Fix Issue with Podman Unable to Create PHP Runtime Website (#7253) 2024-12-03 11:01:05 +00:00
zhengkunwang 8e3a06b07f feat: Internationalize Certain Files (#7252) 2024-12-03 10:17:05 +00:00
maninhill b6f65f278d chore: Create README_CN.md 2024-12-03 18:04:15 +08:00
maninhill 3e8dfb6841 chore: Update README.md 2024-12-03 17:44:40 +08:00
maninhill ffeb47eaac chore: Update README_EN.md 2024-12-03 17:37:36 +08:00
maninhill 4cffe1d6bc chore: Update README_EN.md 2024-12-03 17:35:53 +08:00
wanghe-fit2cloud 49b3b6ded5 feat: Improve system language translations 2024-12-03 15:31:27 +08:00
BugKing f294130746 chore: fix typo for CloudDNS (#7243) 2024-12-03 15:28:29 +08:00
wanghe-fit2cloud 1910f1b78a feat: 1pctl command supports more languages 2024-12-03 15:27:34 +08:00
wanghe-fit2cloud cc1d2f1eae chore: Update app.yaml 2024-12-03 13:51:06 +08:00
wanghe-fit2cloud 048121a936 chore: Update workflows 2024-12-02 14:24:01 +08:00
John Niang e41e74d730 Refine English translation (#7232) 2024-12-01 22:14:44 +08:00
蘭 0ef1a26e95 pref: Remove Swagger description information (#7214) 2024-11-30 11:44:50 +00:00
zhengkunwang 00701a3999 feat(website): Translate Chinese Parts of Website Default Files (#7213) 2024-11-30 11:42:51 +00:00
wanghe-fit2cloud 8453e60609 chore: Streamline some issue templates 2024-11-29 22:51:42 +08:00
wanghe-fit2cloud d139073948 chore: Update workflows 2024-11-29 22:21:35 +08:00
王贺 f6e6f9cbfb chore: Create issue-translator.yml 2024-11-29 19:51:11 +08:00
wanghe-fit2cloud 66e4af2be5 chore: Update Security policy 2024-11-29 15:30:41 +08:00
wanghe-fit2cloud 7327ee3ff8 feat: Optimize the CI script 2024-11-29 15:11:13 +08:00
zhengkunwang 23087bdb6f feat(logs): Change IP Database to Support Internationalization (#7212)
change ip database from qqwry to geoip
2024-11-29 03:46:44 +00:00
zhengkunwang 5e9a69d8f3 Update README_EN.md (#7210) 2024-11-29 10:09:07 +08:00
zhengkunwang 3e96e83c3c feat(appstore): Handle Requests After Application Installation (#7206) 2024-11-28 10:42:40 +00:00
蘭 83ce744fb2 feat: Adapt to internationalization (#7205) 2024-11-28 10:40:39 +00:00
zhengkunwang ae03b85a97 fix(website): Fix Issue with Switching PHP Runtime Environment (#7203)
Refs https://github.com/1Panel-dev/1Panel/issues/7097
2024-11-28 07:26:38 +00:00
ssongliu 53e91a87b0 fix(container-command): Fix the issue of container command parsing failure. (#7200) 2024-11-27 17:27:50 +08:00
ssongliu 519c6bca66 feat(cmd): Add support for internationalization of 1Panel commands (#7192) 2024-11-27 08:02:35 +00:00
John Niang f7dcacab92 Fix lint error when building frontend (#7198) 2024-11-27 16:00:43 +08:00
蘭 00c6466423 feat(alert): Add alert settings (#7196)
Refs #6357
Refs #7005
2024-11-27 07:14:33 +00:00
zhengkunwang b8d8867ebb feat(appstore): Restrict application updates based on the app's suppo… (#7174) 2024-11-25 08:18:24 +00:00
zhengkunwang 7b1d746ce7 feat(website): Optimize website error_log configuration (#7173)
Refs **https://github.com/1Panel-dev/1Panel/issues/7161**
2024-11-25 08:16:25 +00:00
igophper 7dc0a2f664 fix: 解决更新端口不更新防火墙规则的问题 (#7169) 2024-11-25 16:12:52 +08:00
zhengkunwang 52f7df0350 feat(runtime): correct typo in variable name (#7172) 2024-11-25 12:05:42 +08:00
zhengkunwang 38d1cac5d9 feat(runtime): Add .NET runtime (#7155) 2024-11-24 09:11:23 +08:00
zhengkunwang c3565f72c4 feat(system-security): Support Hot Reloading of System Certificates (#7152)
Refs https://github.com/1Panel-dev/1Panel/issues/7129
2024-11-22 09:48:13 +00:00
ssongliu 7fdb0a5078 fix: Fixed the fault in determining the firewall port forwarding version (#7150) 2024-11-22 02:16:14 +00:00
liqiang-fit2cloud ce30a7d5cd Update llm-code-review.yml 2024-11-22 10:11:16 +08:00
蘭 28597721f2 feat: Add API interface authentication function (#7146) 2024-11-21 22:09:00 +08:00
zhengkunwang c2fd02ac48 feat(system-security): Added 444 and 500 responses for unauthenticated settings (#7143) 2024-11-21 17:46:13 +08:00
ssongliu a8aa1f87f2 fix: Fixed the fault in determining the firewall port forwarding version (#7141) 2024-11-21 09:30:10 +00:00
zhengkunwang 2ba17d89ef feat(system-security): Optimized unauthenticated settings to enhance system security (#7142) 2024-11-21 16:31:07 +08:00
wanghe-fit2cloud aaae8a5d3b fix: Remove the build cache 2024-11-20 12:08:35 +08:00
wanghe-fit2cloud 5ca91157ca chore: modify question templates. 2024-11-19 18:12:11 +08:00
wanghe-fit2cloud bad1b0b105 perf: Update related workflows 2024-11-19 18:08:58 +08:00
wanghe-fit2cloud d14a149c57 Remove .vscode folder from the repository 2024-11-19 18:06:57 +08:00
吴小白 9c6c77615e build(deps): bump goreleaser from v2.4.5 to latest (#7122)
#### What this PR does / why we need it?
- https://github.com/goreleaser/goreleaser/issues/5292
- https://github.com/goreleaser/goreleaser/commit/700889269f740f659c9f3867f2b79fed677b74b0

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-11-19 03:56:05 +00:00
吴小白 913e64bb75 perf: remove go cache (#7114)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-11-18 08:02:03 +00:00
蘭 fd8ef545e8 style: 主题样式优化 (#7113) 2024-11-18 07:42:03 +00:00
ssongliu d068cc40f0 fix: 修改未激活状态下信息内容 (#7112) 2024-11-18 06:58:03 +00:00
ssongliu ee0e17a496 fix: 解决数据库切换版本不正确的问题 (#7111) 2024-11-18 06:16:04 +00:00
吴小白 f1afc0f4d9 build(deps): bump goreleaser from v2 to v2.4.5 (#7110)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-11-18 06:14:03 +00:00
吴小白 ea1afe7610 build(deps): bump golang from 1.22 to 1.23 (#7109) 2024-11-18 11:33:25 +08:00
ssongliu 4ade175a3d fix: 统一 xpack 失联接口返回内容 (#7106) 2024-11-18 02:30:03 +00:00
igophper c6ea06153a fix: 解决编辑端口规则不能保存的问题 (#7100) 2024-11-17 22:52:02 +08:00
ssongliu eb36a0f8cb fix: 优化许可证错误信息 (#7094) 2024-11-16 13:00:02 +00:00
ssongliu 3087c1a6e8 fix: 完善 license 同步失联逻辑 (#7090) 2024-11-15 15:22:01 +00:00
蘭 e5b0a1c591 feat: Echarts 组件适配主题颜色 (#7087) 2024-11-15 20:14:34 +08:00
igophper af41e8e429 feat: 增加DNS服务商CloudDNS (#7081)
Refs #7038
2024-11-15 16:46:45 +08:00
蘭 7e677ee9fa fix: 获取告警短信使用量 (#7077) 2024-11-15 08:06:02 +00:00
蘭 eea4cf68ad fix: 修复许可证失效主题切换错误问题 (#7075) 2024-11-15 07:24:01 +00:00
刘瑞斌 3a400dd15a feat: 增加火山引擎DNS (#7006) 2024-11-15 11:27:41 +08:00
maninhill fbbbd52d72 chore: Update README_EN.md 2024-11-15 06:58:19 +08:00
maninhill 27664c7dce chore: Update README.md 2024-11-14 22:56:36 +08:00
蘭 e1bdd215b1 fix: 编辑计划任务和病毒扫描告警问题处理 (#7058) 2024-11-14 21:52:37 +08:00
蘭 d04a35565e feat: 主题样式优化 (#7053) 2024-11-14 09:32:01 +00:00
蘭 7aad2cf4af fix: 客户端缓存导致界面设置 Logo 不能正常显示 (#7048)
Refs #7020
2024-11-14 08:18:02 +00:00
蘭 bfa667584a fix: 编辑计划任务和病毒扫描告警问题处理 (#7047)
Refs #7039
2024-11-14 16:15:47 +08:00
ssongliu 1858cb4df8 fix: 导入恢复限制文件数量 (#7045) 2024-11-14 06:20:01 +00:00
ssongliu dc30a3d1c3 fix: 解决 vue 组件未兼容繁体的问题 (#7044)
Refs #7033
2024-11-14 06:18:02 +00:00
蘭 6e690cc6f2 style: 主题样式优化 (#7037) 2024-11-13 14:16:01 +00:00
蘭 911b4076f5 feat: 主题色保存/恢复增加确认提示 (#7036) 2024-11-13 14:14:01 +00:00
蘭 4c01b1e542 fix: 删除目录校验 (#7035) 2024-11-13 14:12:01 +00:00
蘭 ec372dbb98 fix: 删除目录校验是否为禁止删除的目录 (#7031) 2024-11-13 07:10:00 +00:00
蘭 0b87025c52 feat: 面板目录禁止删除 (#7029)
Refs #7008
2024-11-13 06:52:02 +00:00
蘭 7d4230ecfb fix: 关闭代理同时更新 Docker 代理 (#7028) 2024-11-13 06:50:00 +00:00
ssongliu d06583773a fix: 优化概览页磁盘显示 (#7023) 2024-11-13 06:46:01 +00:00
ssongliu 67c1fbd07a fix: 优化登陆页面特征信息 (#7027)
Refs #7000
2024-11-13 06:44:00 +00:00
蘭 5c7fab8f01 style: 面板样式优化 (#7026) 2024-11-13 06:30:00 +00:00
ssongliu 06554375cb fix: 修改面板证书显示域名信息 (#7019) 2024-11-12 23:04:00 +08:00
蘭 e65329e7b3 perf: 优化 docker 代理 (#7016) 2024-11-12 11:18:00 +00:00
蘭 f5435c581b perf: 优化主题色及样式 (#7015) 2024-11-12 10:32:00 +00:00
ssongliu 5e8b23db6a fix: 快照恢复忽略应用恢复失败问题 (#7012) 2024-11-12 17:34:23 +08:00
蘭 f2849aa6ec perf: 优化主题色及样式 (#7009) 2024-11-12 13:48:49 +08:00
Xiao TanandBH1XAQ ee947b6468 fix: 修复 pure-ftpd 上传缓慢问题 (#6994)
* fix: 修复 pure-ftpd 上传缓慢问题

* fix: 修复 pure-ftpd 修改密码用户名的判断依据

---------

Co-authored-by: BH1XAQ <tanxiao@16iot.cn>
2024-11-12 09:27:21 +08:00
蘭 49c072b40d perf: 优化主题色及样式 (#7002) 2024-11-11 22:46:27 +08:00
蘭 204f9f1fbc feat: 面板代理服务器提供 docker 代理 (#6986) 2024-11-08 18:19:48 +08:00
zhengkunwang cccbaafd1b feat: 修改应用安装超时时间 (#6983) 2024-11-08 06:55:21 +00:00
BugKing 7bc06bb6b1 docs: Update README.md 2024-11-08 09:25:05 +08:00
BugKing c16d5190c6 docs: Update README.md
增加专业版简要说明。
2024-11-08 09:25:05 +08:00
蘭 ffdd9b9d79 feat: 增加自定义主题颜色,调整默认暗色主题配色 (#6964) 2024-11-06 09:39:20 +00:00
ssongliu 8d35c54672 fix: 优化防火墙端口转发错误信息 (#6967) 2024-11-06 08:31:19 +00:00
ssongliu eb0164907c fix: 解决面板证书信息获取失败的问题 (#6962)
Refs #6863
2024-11-06 06:37:19 +00:00
wanghe 478d44885c Update OWNERS (#6963) 2024-11-06 14:33:34 +08:00
dependabot[bot] 541bc456be build(deps): bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 (#6959)
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.5.0...v4.5.1)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-06 10:22:52 +08:00
liqiang-fit2cloud 5ce75992a3 Update llm-code-review.yml 2024-11-05 14:57:12 +08:00
蘭 c8b3ba7f6c style: 组件样式调整 (#6943) 2024-11-04 10:50:53 +00:00
liqiang-fit2cloud d92ce86bb8 build: add code review action. 2024-11-04 17:59:28 +08:00
ssongliu f7b4c8cd28 fix: 优化容器创建提示信息 (#6925) 2024-11-01 14:27:10 +00:00
YaKun9 24c063b106 feat: 概览-状态,磁盘数量多过时默认收起 (#6918)
Refs https://github.com/1Panel-dev/1Panel/issues/6917
feat: 概览-状态,磁盘数量多过时默认收起
2024-11-01 14:25:10 +00:00
igophper e478d2e624 fix: 解决清空网站日志时的闪烁问题 (#6905)
Refs #6904
2024-10-31 10:19:36 +08:00
ssongliu dc0f726029 fix: 解决操作日志打印错误的问题 (#6899) 2024-10-30 17:02:19 +08:00
ssongliu 784b8da7af fix: 解决远程数据库无法切换的问题 (#6893)
Refs #6883
2024-10-30 08:14:41 +00:00
ssongliu 79e9dc9914 fix: 解决全屏切换直接关闭后菜单不显示的问题 (#6894)
Refs #6889
2024-10-30 08:12:39 +00:00
wanghe 4849c6b42d Update README.md 2024-10-30 13:51:35 +08:00
wanghe 024d142c8d Update README.md 2024-10-28 15:58:12 +08:00
ssongliu 126aea931c fix: 解决容器编排路径为空时无法删除的问题 (#6862) 2024-10-27 15:18:02 +00:00
ssongliu 2372281d5e fix: 解决容器镜像仓库编辑失败的问题 (#6845)
Refs #6839
2024-10-25 14:11:55 +00:00
ssongliu 376d6609f2 fix: 补全进程守护 watch 引用 (#6836) 2024-10-24 15:00:18 +08:00
ssongliu 96ab4c53af fix: 解决计划任务告警误报的问题 (#6825) 2024-10-23 14:08:19 +00:00
ssongliu 96cf46d834 fix: 解决低版本 dockerd 命令不兼容的问题 (#6824) 2024-10-23 10:34:19 +00:00
ssongliu 208414704d style: 容器样式调整 (#6822)
Refs #6819
2024-10-23 09:32:19 +00:00
zhengkunwang 7f453aa145 feat: 启动运行环境增加默认网络检测 (#6820) 2024-10-23 09:08:20 +00:00
蘭 123997fa13 feat: 面板重启导致计划任务失败时进行短信告警 (#6817) 2024-10-23 03:46:19 +00:00
zhengkunwang 7faef2f8a7 feat: 创建运行环境网站增加 Python 选项 (#6815) 2024-10-23 02:52:16 +00:00
ssongliu f93cf3b996 fix: 解决快照列表排序的问题 (#6816)
Refs #6810
2024-10-23 02:46:17 +00:00
zhengkunwang df5b69756c feat: 创建运行环境网站增加默认网络创建 (#6807) 2024-10-22 11:24:34 +00:00
蘭 745bb98abe fix: 完善短信告警国际化 (#6806) 2024-10-22 11:20:32 +00:00
ssongliu cf1d02771f fix: 解决 Docker 重启失败的问题 (#6805) 2024-10-22 09:42:28 +00:00
zhengkunwang 52377b0042 fix: 解决 Python 运行环境编辑报错的问题 (#6801) 2024-10-22 08:10:28 +00:00
蘭 270501a237 fix: 文件夹选择控件,选择文件时不默认选择当前目录 (#6799)
Refs #6012
2024-10-22 07:40:29 +00:00
zhengkunwang 2e5283b8d3 fix: 解决 Mysql 特定版本忽略升级失效的问题 (#6800)
Refs https://github.com/1Panel-dev/1Panel/issues/6796
2024-10-22 07:34:29 +00:00
ssongliu 8cad47b29e feat: 修改容器操作超时时间 (#6798)
Refs #6747
2024-10-22 07:20:28 +00:00
Ludovic Fernandez 2458d60d80 fix: otel dependencies (#6793) 2024-10-22 07:39:07 +08:00
ssongliu 9a79dc4cdd feat: SSL 配置增加重启选项 (#6792) 2024-10-21 22:47:14 +08:00
Ludovic Fernandez e531b509d6 fix: huawei cloud (#6791) 2024-10-21 22:15:32 +08:00
蘭 d287fc1186 fix: 解析病毒扫描内容格式处理 (#6790) 2024-10-21 14:06:20 +00:00
zhengkunwang f47f7c7361 fix: 解决创建运行环境报名称存在的问题 (#6785) 2024-10-21 08:50:22 +00:00
zhengkunwang 7a6c99d57d fix: 解决证书自动续签日志报错的问题 (#6784) 2024-10-21 07:10:20 +00:00
zhengkunwang 5105114321 fix: 解决网站开启 HTTPS 之后 404 配置丢失的问题 (#6775) 2024-10-18 09:17:38 +00:00
zhengkunwang 60beebcb4c style: 日志样式调整 (#6774) 2024-10-18 08:55:38 +00:00
ssongliu fa03b4226a fix: 解决容器编排跳转路径错误的问题 (#6773) 2024-10-18 08:43:37 +00:00
蘭 b047a053c5 fix: 解决移动端主机文件批量删除错误 (#6765) 2024-10-18 03:29:36 +00:00
ssongliu 42907cd228 feat: postgresql 远程数据库支持 17.x 版本 (#6759) 2024-10-17 13:37:32 +00:00
蘭 425892cfc4 refactor: 调整包引用 (#6758) 2024-10-17 13:35:32 +00:00
ssongliu 16fd804e93 feat: 优化数据库切换效果 (#6757)
Refs #6755
2024-10-17 10:05:32 +00:00
John Bro 2f147dde13 fix: 容器部分操作动态修改执行命令 (#6750)
Refs #3242
2024-10-17 10:03:31 +00:00
蘭 0d04f07f2a fix: Switch 组件 small 尺寸样式修复 (#6752) 2024-10-17 07:31:30 +00:00
蘭 96485218b7 feat: 病毒扫描定时扫描和短信告警合并提示许可证绑定 (#6751) 2024-10-17 07:29:31 +00:00
蘭 227cc57426 feat: 计划任务失败告警支持全类型告警 (#6749) 2024-10-17 07:27:31 +00:00
zhengkunwang b99061777a feat: PHP 镜像 build 增加超时时间 (#6744) 2024-10-17 04:13:30 +00:00
zhengkunwang 8749dc649a style: 优化一些样式 (#6740) 2024-10-16 09:37:27 +00:00
zhengkunwang 65da51a478 style: 优化应用升级文件对比样式 (#6733) 2024-10-16 04:07:26 +00:00
蘭 0c31ba1c5f feat: 文件夹选择控件,默认选中当前目录 (#6720)
Refs #6012
2024-10-15 10:21:22 +00:00
蘭 acf94b052b feat: 增加病毒扫描和计划任务告警 (#6709) 2024-10-14 09:51:17 +00:00
蘭 556441d811 style: 解决 Alert 提示组件标题存在链接时文字错位问题 (#6711) 2024-10-14 09:41:17 +00:00
zhengkunwang efcf17e47c style: 优化登录页软件许可协议提示语样式 (#6712) 2024-10-14 09:31:18 +00:00
zhengkunwang 02b7744995 pref: 优化网站日志读取 (#6710)
Refs https://github.com/1Panel-dev/1Panel/issues/6642
2024-10-14 09:29:17 +00:00
ssongliu c86947b69c style: 修改按钮组边框宽度不一致的问题 (#6706)
Refs #6704
2024-10-14 06:41:16 +00:00
ssongliu 839b8c2844 feat: 修改容器列表应用商店容器默认显示 (#6705) 2024-10-14 06:29:16 +00:00
John Bro e4a634e6e8 fix: 容器重启前增加校验操作 (#6696) 2024-10-14 10:36:55 +08:00
wild_pointer eb99c10ad0 fix: 修复“修改文件权限时,文件所属用户和用户组不显示”问题 (#6698) 2024-10-14 10:24:48 +08:00
zzzandzengzz01 175b7b89e4 feat:firewalld和ufw同时存在时,不进行操作 (#6688)
Co-authored-by: zengzz01 <zengzz01@mingyuanyun.com>
2024-10-11 18:03:33 +08:00
zhengkunwang 5374afb226 feat: 增加华为云申请证书 (#6687)
Refs https://github.com/1Panel-dev/1Panel/issues/6047
2024-10-11 06:41:05 +00:00
John Bro 1a44f48fad fix: 日志查看统一改成网页全屏以及样式调整 (#6682)
Refs #5873
2024-10-10 10:55:02 +00:00
zhengkunwang 50dbe7cfc0 feat: 增加 Python 运行环境 (#6681) 2024-10-10 18:49:13 +08:00
ssongliu c7e576c879 fix: 概览页磁盘显示排除 overlay 类型 (#6672)
Refs #6637
2024-10-10 03:07:00 +00:00
ssongliu a3e4847744 feat: 快照列表改为异步获取大小 (#6670)
Refs #6540
2024-10-10 03:05:00 +00:00
ssongliu 903f8ed5a7 feat: 容器列表缓存是否显示应用商店容器 (#6667)
Refs #5885
2024-10-10 02:38:59 +00:00
zhengkunwang b091c7f364 feat: 反向代理文本替换增加 sub_filter_types 参数 (#6661)
Refs https://github.com/1Panel-dev/1Panel/issues/6490
2024-10-09 07:02:56 +00:00
zhengkunwang e069db3c45 feat: 反代回源 SNI 支持设置 proxy_ssl_name (#6659)
Refs https://github.com/1Panel-dev/1Panel/issues/5960
2024-10-09 06:42:56 +00:00
see-more 047264dbbc chore;删除error_code组件的body标签 (#6656) 2024-10-09 10:24:47 +08:00
ssongliu 0c216ba87a feat: 支持离线版许可证 (#6651) 2024-10-08 13:56:55 +00:00
ssongliu 31cdc28143 fix: 修改上传恢复提示信息 (#6647) 2024-10-08 02:58:52 +00:00
zhengkunwang e03b728240 feat: 自签证书支持 ipv6 (#6627)
Refs https://github.com/1Panel-dev/1Panel/issues/6623
2024-09-29 10:25:47 +00:00
ssongliu 4c275ba52f style: 修改许可证即将过期样式 (#6616) 2024-09-29 06:37:45 +00:00
ssongliu 2ac9a888a8 feat: 移除编排删除后跳转 (#6613) 2024-09-29 02:15:45 +00:00
Anye f9bdcf8938 feat: 新增 BookStack 的 PHP 扩展模板 (#6611) 2024-09-29 10:01:55 +08:00
ssongliu c3bef26ae6 feat: 调整编排删除逻辑 (#6610) 2024-09-28 22:29:08 +08:00
ssongliu 634849c347 feat: Compose 环境变量编辑增加是否 1Panel 创建校验 (#6605) 2024-09-28 01:37:40 +00:00
John Bro c557b737f6 fix: 重构容器编排环境变量设置逻辑 (#6600) 2024-09-27 14:25:37 +00:00
zhengkunwang 1d326afa93 feat: 优化日志加载 (#6593) 2024-09-26 14:37:32 +00:00
wanghe-fit2cloud cb6c58cb5d Revert "style:优化概览界面的布局 (#6491)"
This reverts commit 7f06c7e92e.
2024-09-25 10:24:33 +08:00
John Bro ec4d708569 feat: 容器编排列表增加编排目录并支持跳转 (#6565)
Refs #3579
2024-09-24 14:25:23 +00:00
John Bro 05b19cefa5 feat: 容器日志下载只支持全量下载 (#6559)
Refs #2399
2024-09-24 14:23:24 +00:00
John Broandzhoujunhong fdd57d0801 feat: 容器编排及模板列表支持排序 (#6566)
Co-authored-by: zhoujunhong <1298308460@qq.com>
2024-09-24 22:21:28 +08:00
Kataick c16d6d1644 flx: 修复会默认出现水平滚动条的问题 (#6564) 2024-09-24 16:01:05 +08:00
John Bro 656b39c880 fix: 添加国际化及样式修改 (#6555) 2024-09-23 10:11:14 +00:00
John Bro 753c996291 fix: 编排模板列表日期格式化 (#6551) 2024-09-23 06:33:15 +00:00
ssongliu dcdd0cc2bb fix: 解决服务包含 .socket 时起停失败的问题 (#6553)
Refs #6524
2024-09-23 06:31:14 +00:00
ssongliu 2f32df16d9 fix: 解决部分浏览器无法打开文件菜单的问题 (#6537) 2024-09-19 10:56:55 +00:00
John Bro 9caa9c47b4 feat: 容器编排显示容器状态,编排模板样式优化 (#6534)
Refs #4404
2024-09-19 10:54:56 +00:00
蘭 55bec8a911 fix: 解决图片/视频预览时,重命名文件名导致浏览器缓存文件内容问题 (#6533)
Refs #6505
2024-09-19 06:00:54 +00:00
John Bro 43f95fd40e fix: 创建和编辑 compose 文件支持设置环境变量 (#6503) 2024-09-18 06:00:49 +00:00
Anye 641812e7c1 fix: 调整Logo长宽比,防止显示被拉伸 (#6510)
* fix: 调整Logo长宽比,防止显示被拉伸
2024-09-17 12:52:58 +08:00
zhengkunwang 95d3e75425 feat: demo 环境增加首页状态接口过滤 (#6498) 2024-09-14 10:12:32 +00:00
see-more 7f06c7e92e style:优化概览界面的布局 (#6491)
* fix: <body> cannot be child of <div>

* style:优化概览界面的布局
2024-09-14 13:43:52 +08:00
ssongliu 0e6fdffeec feat: S3 备份账号支持 Path Style (#6489)
Refs #4553
2024-09-13 10:42:27 +00:00
endymx f77681b9c9 feat(ssl证书): 实现华为云dns (#6407) 2024-09-13 10:53:06 +08:00
John Bro 535d4bbe1b fix: 删除容器后 compose 记录保存以便后续处理 (#6447)
Refs https://github.com/1Panel-dev/1Panel/issues/5099
2024-09-12 10:06:22 +00:00
zhengkunwang 1dfbca3d5b feat: 删除多余代码 (#6463) 2024-09-11 23:10:15 +08:00
zhengkunwang d2ca265cc2 perf: 优化首页加载速度 (#6461) 2024-09-11 14:12:16 +00:00
ssongliu fc70345fcd fix: 优化移动端运行时样式 (#6460) 2024-09-11 22:02:11 +08:00
ssongliu 6ae9b982a9 fix: 优化移动端样式 (#6455) 2024-09-11 10:22:15 +00:00
蘭 0122b4c6ce fix: 解决主机文件压缩解压特殊字符问题 (#6451) 2024-09-11 08:20:15 +00:00
ssongliu 08c0faa20e feat: 优化概览页内存单位 (#6450) 2024-09-11 07:38:15 +00:00
ssongliu ab7efea8c5 style: 优化移动端样式 (#6448) 2024-09-11 07:24:15 +00:00
zhengkunwang f4623664d5 feat: 日志页面增加加载状态 (#6446) 2024-09-11 03:48:14 +00:00
ssongliu df147435c5 fix: 移除重复样式 (#6445) 2024-09-11 03:16:14 +00:00
see-more 71c1c5ae83 style:优化设置移动端界面 (#6443)
* style:优化设置移动端界面

* style:设置-安全界面样式

* style:修复设置-备份账号服务器磁盘移动端无法剧中

* style:优化移动端设置界面
2024-09-11 10:30:46 +08:00
see-more 0ea142d6db style:优化日志审计页面样式 (#6441) 2024-09-11 10:16:02 +08:00
ssongliu f88a3059be fix: 修改移动端样式 (#6440) 2024-09-10 14:04:12 +00:00
蘭 48281ddf86 fix: 主机文件解压文件 mimetype 类型增加 (#6437) 2024-09-10 14:02:11 +00:00
zhengkunwang 7cdcf9b757 feat: 增加 Intel Arc 显卡 适配 (#6435) 2024-09-10 18:55:29 +08:00
ssongliu ef0bb12fbd fix: 处理代码格式问题 (#6433) 2024-09-10 07:40:09 +00:00
蘭 a0857f86b2 fix: 主机文件打开软链接优化 (#6432) 2024-09-10 07:26:10 +00:00
wangdan-fit2cloud 2219b3f47b perf: 优化工具栏样式 (#6431)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-09-10 07:18:10 +00:00
蘭 b6c873f22d fix: 主机文件的路径栏路径 Bug 解决 (#6428)
Refs #6310
2024-09-10 03:46:08 +00:00
蘭 64bc92a7fd fix: 限制二进制文件解压的类型 (#6425)
Refs #6113
2024-09-10 02:14:08 +00:00
ssongliu 6c4c0f7595 fix: 解决服务器重启按钮被遮住的样式问题 (#6424) 2024-09-09 22:56:14 +08:00
蘭 2d9c977805 fix: 解决文件编辑器行号问题 (#6423)
Refs #5918

升级了编辑器版本,调整了编辑器的主题,需要整体测试一下编辑器的样式!
2024-09-09 10:08:04 +00:00
ssongliu dff0375848 fix: 解决容器端口的显示问题 (#6417) 2024-09-09 07:26:04 +00:00
ssongliu ab825a1a70 feat: 防火墙操作支持 ipv6 网段 (#6415) 2024-09-09 11:19:29 +08:00
Anye 466617847f fix: 创建CA机构生成的应为证书而非证书签名请求 (#6404) 2024-09-07 21:56:38 +08:00
蘭 a53fff2416 fix: 解决软连接被识别为文件问题 (#6400)
Refs #5879
2024-09-06 09:37:52 +00:00
zhengkunwang dd1430dbbf feat: 反向代理配置文件统一 (#6396)
Refs https://github.com/1Panel-dev/1Panel/issues/6395
2024-09-06 06:33:50 +00:00
蘭 5aad16968b perf: 优化文件管理器终端命令 (#6393)
Refs #5891
2024-09-06 03:51:50 +00:00
see-more f2d3acb650 style:更新所有移动端container路径下样式 (#6387)
* style:更新所有移动端container路径下样式

* style:修复el-button的默认margin
2024-09-06 10:00:53 +08:00
see-more 666f8c813d style:移动端文件页面样式和防火墙页面样式 (#6386)
* style:移动端端文件页面样式

* style:修改防火墙页面样式

* style:手机端ssl界面和数据库界面调整
2024-09-06 10:00:39 +08:00
ssongliu e87fb7b50e fix: 备份账号 bucket 支持手动输入 (#6381)
Refs #6378
2024-09-05 13:53:48 +00:00
蘭 75612879c7 fix: 解决部分情况下缓存目录导致启动失败的问题 (#6382) 2024-09-05 09:23:47 +00:00
蘭 94e0c80d47 refactor: 调整主机监控任务时间为整分钟执行 (#6379) 2024-09-05 08:41:48 +00:00
蘭 feb6f4cbe0 fix: 概览页获取 CPU 精度问题 (#6380)
Refs #6309
2024-09-05 08:39:48 +00:00
ssongliu 1173bbf628 fix: 解决备份恢复弹窗打开后无法关闭的问题 (#6376)
Refs #6355
2024-09-05 07:11:48 +00:00
ssongliu 1cdc2e93ad fix: 解决 FreshClam 操作失败的问题 (#6374)
Refs #6372
2024-09-05 07:09:46 +00:00
see-more 3356bd8825 fix:移动端网站页面和数据库页面样式 (#6360)
* fix:移动端网站页面和数据库页面样式

* style:修改database页面样式

* style:修改databse页面手机端搜索框与按钮间距
2024-09-05 11:46:57 +08:00
ssongliu decc7a4a1e fix: 解决停止状态容器端口获取不正确的问题 (#6370)
Refs #6333
2024-09-05 03:45:46 +00:00
zhengkunwang a397cab61c fix: 解决部分情况下缓存目录导致启动失败的问题 (#6364)
Refs https://github.com/1Panel-dev/1Panel/issues/6362
2024-09-04 09:21:43 +00:00
trom 228e3d9ae3 style:优化container界面的布局 (#6348) 2024-09-03 13:46:37 +08:00
zhengkunwang 051f670453 fix: 修复域名删除导致网站访问被 WAF 拦截的问题 (#6337)
Refs https://github.com/1Panel-dev/1Panel/issues/6336
2024-09-02 10:23:37 +00:00
trom cf8aa86a67 fix:修复文件路径bug (#6313)
* fix:修复文件路径bug

* fix:修改文件路径逻辑
2024-09-02 10:35:32 +08:00
ssongliu 5b00286e65 fix: 修改 PostgreSQL 容器内部端口 (#6275)
Refs #6274
2024-08-28 08:45:22 +00:00
ssongliu 6597caaf05 fix: 解决快照套接字忽略失败的问题 (#6273) 2024-08-28 06:41:22 +00:00
ssongliu 5851124dee fix: 解决备份套接字忽略不完的问题 (#6272) 2024-08-28 05:45:22 +00:00
trom b18689a0d6 feat:显示当前ip,系统网络代理地址 (#6251) 2024-08-27 13:39:36 +08:00
蘭 a5e7f78a1f pref: 完善告警通知任务 (#6263) 2024-08-27 13:39:18 +08:00
ssongliu a1d394fbc2 fix: 解决容器端口范围太大时,编辑打开失败的问题 (#6249)
Refs #6245
2024-08-26 08:07:13 +00:00
wanghe-fit2cloud 5e82bda666 Revert commit 3d5580a: feat:显示当前ip,系统网络代理地址 2024-08-26 15:55:07 +08:00
trom 3d5580ad9d feat:显示当前ip,系统网络代理地址 (#6235) 2024-08-26 11:25:03 +08:00
ssongliu c83cfbe8b0 fix: 解决备份导入失败的问题 (#6228) 2024-08-23 14:58:59 +00:00
蘭 00cc1b62ef pref: 完善告警通知 (#6227) 2024-08-23 13:53:00 +00:00
ssongliu e782f75863 fix: 解决容器配置修改后未重启的问题 (#6225)
Refs #6226
2024-08-23 10:24:58 +00:00
zhengkunwang f5f379465f feat: 创建运行环境网站,校验 PHP 镜像是否存在 (#6220) 2024-08-23 09:37:00 +00:00
ssongliu 70a8fda449 feat: 优化数据库备份页面样式 (#6221) 2024-08-23 17:36:21 +08:00
ssongliu ca14302191 fix: 优化部分频繁打印的日志操作 (#6219) 2024-08-23 06:40:58 +00:00
ssongliu 7297c2626f fix: 优化防火墙启动时自动添加端口功能 (#6216)
Refs #6215
2024-08-23 03:54:57 +00:00
蘭 92a3982f21 feat: 告警通知功能完善 (#6211) 2024-08-22 14:22:56 +00:00
蘭 626f5efdcd style: 优化 switch 组件 small 尺寸样式 (#6210) 2024-08-22 14:20:55 +00:00
ssongliu 24e780b984 fix: 压缩增加 *.socket 排除规则 (#6196) 2024-08-21 07:12:50 +00:00
zhengkunwang 596d4aceb6 fix: 解决删除 PHP 运行环境之后,删除其他网站提示错误的问题 (#6192) 2024-08-21 02:50:50 +00:00
蘭 e770254b65 feat: 增加短信告警功能 (#6184) 2024-08-20 22:11:13 +08:00
wanghe-fit2cloud 7901c26d32 feat: 修改 snapshot 参数 2024-08-20 18:47:49 +08:00
zhengkunwang fc65f8f689 feat: 修复代码警告 (#6190) 2024-08-20 10:36:47 +00:00
zhengkunwang e0a5e14978 feat: 修改非 PHP 运行环境网站的删除提示 (#6189) 2024-08-20 09:42:47 +00:00
zhengkunwang 07562c203a feat: 优化已安装应用的参数显示 (#6188) 2024-08-20 09:40:48 +00:00
ssongliu 8187a19e6e fix: 修改版本号复制方式 (#6187) 2024-08-20 09:38:47 +00:00
zhengkunwang 32cd684307 fix: 解决路由菜单文字丢失的问题 (#6185) 2024-08-20 06:46:46 +00:00
ssongliu 3a72fc95d9 style: 处理代码警告 (#6183) 2024-08-20 03:38:47 +00:00
ssongliu 64405fba3d fix: 统一时区获取方法 (#6175) 2024-08-19 10:30:42 +00:00
zhengkunwang cc7981229c fix: 解决 redis 没有密码情况下安装应用没有关联的问题 (#6173)
Refs #6174
2024-08-19 10:06:42 +00:00
zhengkunwang 3ec208dc85 perf: 优化运行环境日志卡顿情况 (#6172) 2024-08-19 10:04:43 +00:00
ssongliu 1b92b6cbfa fix: mysql 绑定用户增加输入校验 (#6149) 2024-08-16 06:00:31 +00:00
trom 779addfcb4 fix:mysql升级到8.4后查询会出错,容器的command回显使用 text area (#6147)
* style:容器的command参数回显应该使用 text area而不是text input,且不应该过滤'\n'

* chore:prettier格式化

* fix:mysql升级到8.4后,数据库管理 - 当前状态 查询会出错

* chore:修改逻辑判断
2024-08-16 11:23:44 +08:00
ssongliu 7da5f7a884 fix: 修改镜像拉取校验规则 (#6145)
Refs #5740
2024-08-15 15:18:32 +00:00
ssongliu 34e14636d4 fix: 解决 ClamAV 卸载后定时任务无法停止的问题 (#6143)
Refs #6100
2024-08-15 15:16:30 +00:00
ssongliu aa222aac31 fix: 解决计划任务备份内容允许为空的校验问题 (#6142)
Refs #6099
2024-08-15 09:42:28 +00:00
zhengkunwang 5134caa103 fix: 解决部分应用参数数据库显示错误的问题 (#6136) 2024-08-15 07:12:28 +00:00
zhengkunwang dca3c92d72 fix: 解决 PHP 运行环境版本过期之后下载安装包失败的问题 (#6125) 2024-08-14 10:29:59 +00:00
zhengkunwang a85d5bad46 feat: 优化 mysql 版本升级 (#6119) 2024-08-13 10:48:56 +00:00
zhengkunwang 716613a4db feat: 应用升级备份保留 3 份 (#6116)
Refs https://github.com/1Panel-dev/1Panel/issues/6107
2024-08-13 07:28:55 +00:00
zhengkunwang 12eaa66bc5 style: 去掉应用列表中的更新应用列表按钮 (#6105) 2024-08-12 18:40:10 +08:00
zhengkunwang a6af97e3c1 style: 优化文件属性页面显示 (#6104)
Refs https://github.com/1Panel-dev/1Panel/issues/5855
2024-08-12 10:02:53 +00:00
zhengkunwang d561c86c68 feat: 优化网站日志在每行数据量大情况下的读取 (#6103)
Refs https://github.com/1Panel-dev/1Panel/issues/6102
2024-08-12 09:58:52 +00:00
dependabot[bot] f243d1c9b4 chore(deps): bump github.com/docker/docker (#6093) 2024-08-12 13:43:47 +08:00
trom 1cc2cc6400 feat:更新应用列表上移 #6035 (#6084) 2024-08-12 10:26:16 +08:00
trom 15f4c05772 chore:点击复制版本号(#5894) (#6077) 2024-08-09 18:25:53 +08:00
trom c9a204b7a7 fix: 搜索框加长(#6063) 回收站时间排序(#5957) hoer状态撑高界面(#5929) (#6070) 2024-08-09 10:22:29 +08:00
trom 3d0018c0bd chore:新建文件夹支持添加空格,登录页鼠标悬浮样式 (#6058) 2024-08-08 10:27:06 +08:00
zhengkunwang d639e46bc7 fix: 解决 PHP 运行环境网站删除之后没有删除应用的问题 (#6036) 2024-08-06 03:34:29 +00:00
dependabot[bot] fa633bf419 chore(deps): bump github.com/docker/docker (#5989)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 26.0.2+incompatible to 26.1.4+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v26.0.2...v26.1.4)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-31 19:05:05 +08:00
zhengkunwang 40f7ef90b3 feat: 修改反向代理默认 Connection 请求头 (#5951)
Refs https://github.com/1Panel-dev/1Panel/issues/5937
2024-07-26 07:01:40 +00:00
zhengkunwang 50efe607d4 feat: 优化泛域名申请证书手动解析值 (#5950)
Refs https://github.com/1Panel-dev/1Panel/issues/4609
2024-07-26 06:59:40 +00:00
igophper 3b057f2c27 feat: 2FA支持前一个验证码 (#5942)
Refs #5940
2024-07-26 11:15:53 +08:00
maninhill d3d018bd44 chore: Update README.md 2024-07-24 17:01:38 +08:00
maninhill 1378609aa2 chore: Update README.md 2024-07-24 16:58:59 +08:00
zhengkunwang 001c896eed fix: 解决上传文件夹权限没改的问题 (#5919) 2024-07-23 17:52:30 +08:00
ssongliu 1cf3a0143c fix: 解决系统日志解析错误的问题 (#5917) 2024-07-23 07:57:32 +00:00
zhengkunwang f04be521e5 feat: 回滚代码 (#5914) 2024-07-23 14:42:06 +08:00
ssongliu 2873632a40 pref: 移除部分废弃代码 (#5910) 2024-07-23 02:31:32 +00:00
zhengkunwang 075879b5b5 fix: 解决应用商店 Go 和 Java 安装跳转错误的问题 (#5909)
Refs https://github.com/1Panel-dev/1Panel/issues/5900
2024-07-23 02:29:32 +00:00
zhengkunwang 9b12930376 fix: 解决安装 gitea 跳转到 postgres 报错的问题 (#5908)
Refs https://github.com/1Panel-dev/1Panel/issues/5902
2024-07-23 02:27:31 +00:00
zhengkunwang 3339ba9bad fix: 解决潜在的 SQL 注入漏洞 (#5906)
影响范围:操作日志
2024-07-22 14:41:31 +00:00
zhengkunwang 68922676fc fix: 解决从网站上传的证书状态显示异常的问题 (#5869)
Refs https://github.com/1Panel-dev/1Panel/issues/5867
2024-07-19 07:41:28 +00:00
zhengkunwang dcf2afb4a6 feat: 上传文件使用父目录用户和用户组 (#5868)
Refs https://github.com/1Panel-dev/1Panel/issues/5866
2024-07-19 07:33:28 +00:00
ssongliu 16a35141dd fix: 解决病毒定时扫描校验国际化的问题 (#5856) 2024-07-18 03:27:28 +00:00
John Bro f6c334bdad feat: 计划任务备份应用/网站/数据库支持多选 (#5849) 2024-07-17 13:35:27 +00:00
蘭 cbb119117b fix: 多个文件同时计算大小,计算状态独自显示 (#5851) 2024-07-17 11:09:30 +00:00
ssongliu cae0a6118f pref: 优化远程数据库解绑提示信息 (#5850) 2024-07-17 09:53:28 +00:00
1366 changed files with 125516 additions and 161131 deletions
+1 -1
View File
@@ -47,4 +47,4 @@ body:
- type: textarea
attributes:
label: "Additional Information"
description: "If you have any additional information to provide, you can include it here (screenshots, videos, etc., are welcome)."
description: "If you have any additional information to provide, you can include it here (screenshots, videos, etc., are welcome)."
+1 -1
View File
@@ -2,4 +2,4 @@ blank_issues_enabled: false
contact_links:
- name: Questions & Discussions
url: https://github.com/1Panel-dev/1Panel/discussions
about: Raise questions about the installation, deployment, use and other aspects of the project.
about: Raise questions about the installation, deployment, use and other aspects of the project.
-1
View File
@@ -1,4 +1,3 @@
name: General PR Handling for 1Panel
on: pull_request
permissions:
+1 -1
View File
@@ -44,4 +44,4 @@ jobs:
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
ossutil-version: '1.7.18'
- name: Upload Assets to OSS
run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/v2/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force
run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force
+1 -1
View File
@@ -42,4 +42,4 @@ jobs:
rclone_config: ${{ secrets.RCLONE_CONFIG }}
- name: Upload to Cloudflare R2
run: |
rclone copy dist/ cloudflare_r2:package/v2/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --progress
rclone copy dist/ cloudflare_r2:package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --progress
+1 -1
View File
@@ -11,4 +11,4 @@ jobs:
- uses: usthe/issues-translate-action@v2.7
with:
IS_MODIFY_TITLE: true
BOT_GITHUB_TOKEN: ${{ secrets.ISSUE_TRANSLATOR_TOKEN }}
BOT_GITHUB_TOKEN: ${{ secrets.ISSUE_TRANSLATOR_TOKEN }}
+1 -1
View File
@@ -22,4 +22,4 @@ jobs:
# max_tokens: 10000
MAX_PATCH_LENGTH: 10000
IGNORE_PATTERNS: "/node_modules,*.md,/dist,/.github"
FILE_PATTERNS: "*.java,*.go,*.py,*.vue,*.ts,*.js,*.css,*.scss,*.html"
FILE_PATTERNS: "*.java,*.go,*.py,*.vue,*.ts,*.js,*.css,*.scss,*.html"
+1 -1
View File
@@ -17,4 +17,4 @@ jobs:
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUBTOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+1 -1
View File
@@ -12,4 +12,4 @@ jobs:
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
static_list: "1Panel"
force_update: true
force_update: true
+1 -1
View File
@@ -8,4 +8,4 @@ jobs:
- name: Checkout Actions Repository
uses: actions/checkout@v2
- name: Check spelling
uses: crate-ci/typos@master
uses: crate-ci/typos@master
+7 -21
View File
@@ -4,8 +4,7 @@
*.dll
*.so
*.dylib
build/1panel-agent
build/1panel-core
build/1panel
# Mac
.DS_Store
@@ -15,7 +14,6 @@ build/1panel-core
.vscode
*.project
*.factorypath
__debug*
# IntelliJ IDEA
.idea/*
@@ -24,7 +22,6 @@ __debug*
*.iml
*.ipr
# Test binary, built with `go test -c`
*.test
@@ -34,23 +31,15 @@ __debug*
# Dependency directories
/pkg/
backend/__debug_bin
core/cmd/server/__debug_bin
core/cmd/server/web/assets
core/cmd/server/web/monacoeditorwork
core/cmd/server/web/index.html
cmd/server/__debug_bin
cmd/server/web/assets
cmd/server/web/monacoeditorwork
cmd/server/web/index.html
frontend/auto-imports.d.ts
frontend/components.d.ts
frontend/src/xpack
agent/xpack
agent/router/entry_xpack.go
agent/server/init_xpack.go
agent/utils/xpack/xpack_xpack.go
core/xpack
core/router/entry_xpack.go
core/server/init_xpack.go
core/utils/xpack/xpack_xpack.go
xpack
backend/xpack
*_xpack.go
.history/
dist/
1pctl
@@ -60,6 +49,3 @@ quick_start.sh
cmd/server/fileList.txt
.fileList.txt
1Panel.code-workspace
core/.golangci.yml
agent/.golangci.yml
+5 -36
View File
@@ -9,12 +9,11 @@ before:
- chmod +x ./ci/script.sh
- ./ci/script.sh
- sed -i 's@ORIGINAL_VERSION=.*@ORIGINAL_VERSION=v{{ .Version }}@g' 1pctl
- go mod tidy
builds:
- id: agent
dir: agent
main: cmd/server/main.go
binary: 1panel-agent
- main: ./cmd/server/main.go
binary: 1panel
flags:
- -tags=xpack
- -trimpath
@@ -32,44 +31,14 @@ builds:
- arm
- ppc64le
- s390x
hooks:
post:
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
- id: core
dir: core
main: cmd/server/main.go
binary: 1panel-core
flags:
- -tags=xpack
- -trimpath
ldflags:
- -w -s
env:
- CGO_ENABLED=0
goos:
- linux
goarm:
- 7
goarch:
- amd64
- arm64
- arm
- ppc64le
- s390x
hooks:
post:
- ./ci/script.sh compress_binary {{ .Path }} {{ .Arch }}
archives:
- formats: [ 'tar.gz' ]
ids: [core, agent]
- format: tar.gz
name_template: "1panel-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{- if .Arm }}v{{ .Arm }}{{ end }}"
wrap_in_directory: true
files:
- 1pctl
- 1panel-core.service
- 1panel-agent.service
- 1panel.service
- install.sh
- GeoIP.mmdb
- lang/*
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

-3
View File
@@ -1,3 +0,0 @@
{
"ansible.python.interpreterPath": "/opt/homebrew/bin/python3"
}
+1 -1
View File
@@ -125,4 +125,4 @@ enforcement ladder](https://github.com/mozilla/diversity).
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
https://www.contributor-covenant.org/translations.
+1 -1
View File
@@ -10,7 +10,7 @@ PR are always welcome, even if they only contain small fixes like typos or a few
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot of features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
This [development guideline](https://docs.1panel.pro/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.
This [development guideline](https://docs.1panel.hk/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
+14 -29
View File
@@ -2,48 +2,33 @@ GOCMD=go
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOARCH=$(shell go env GOARCH)
GOOS=$(shell go env GOOS )
GOOS=$(shell go env GOOS)
BASE_PATH := $(shell pwd)
BUILD_PATH = $(BASE_PATH)/build
WEB_PATH=$(BASE_PATH)/frontend
ASSERT_PATH= $(BASE_PATH)/core/cmd/server/web/assets
CORE_PATH=$(BASE_PATH)/core
CORE_MAIN=$(CORE_PATH)/cmd/server/main.go
CORE_NAME=1panel-core
AGENT_PATH=$(BASE_PATH)/agent
AGENT_MAIN=$(AGENT_PATH)/cmd/server/main.go
AGENT_NAME=1panel-agent
SERVER_PATH=$(BASE_PATH)/backend
MAIN= $(BASE_PATH)/cmd/server/main.go
APP_NAME=1panel
ASSERT_PATH= $(BASE_PATH)/cmd/server/web/assets
clean_assets:
rm -rf $(ASSERT_PATH)
upx_bin:
upx $(BUILD_PATH)/$(CORE_NAME)
upx $(BUILD_PATH)/$(AGENT_NAME)
upx $(BUILD_PATH)/$(APP_NAME)
build_frontend:
cd $(WEB_PATH) && npm install && npm run build:pro
build_core_on_linux:
cd $(CORE_PATH) \
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
build_backend_on_linux:
cd $(SERVER_PATH) \
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
build_agent_on_linux:
cd $(AGENT_PATH) \
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(AGENT_NAME) $(AGENT_MAIN)
build_backend_on_darwin:
cd $(SERVER_PATH) \
&& GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
build_core_on_darwin:
cd $(CORE_PATH) \
&& GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(CORE_NAME) $(CORE_MAIN)
build_all: build_frontend build_backend_on_linux
build_agent_on_darwin:
cd $(AGENT_PATH) \
&& 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
build_on_local: clean_assets build_frontend build_core_on_darwin build_agent_on_darwin upx_bin
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
+10 -15
View File
@@ -1,7 +1,8 @@
<p align="center"><a href="https://1panel.pro"><img src="https://resource.1panel.pro/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Top-Rated Web-based Linux Server Management Tool</b><br>Best VPS control panel<br>新一代的 Linux 服务器运维管理面板</p>
<p align="center"><a href="https://1panel.hk"><img src="https://resource.1panel.hk/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Top-Rated Web-based Linux Server Management Tool</b></p>
<p align="center">
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 240px; height: auto;" /></a>
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 180px; height: auto;" /></a>
<a href="https://www.producthunt.com/posts/1panel?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-1panel" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=639696&theme=light" alt="1Panel - Top&#0045;Rated&#0032;web&#0045;based&#0032;Linux&#0032;server&#0032;management&#0032;tool | Product Hunt" style="width: 180px; height: auto;" /></a>
</p>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF" alt="License: GPL v3"></a>
@@ -33,7 +34,7 @@
1Panel is an open-source, modern web-based control panel for Linux server management.
- **Efficient Management**: Through a user-friendly web graphical interface, 1Panel enables users to effortlessly manage their Linux servers. Key features include host monitoring, file management, database administration, container management, LLMs management.
- **Efficient Management**: Through a user-friendly web graphical interface, 1Panel enables users to effortlessly manage their Linux servers. Key features include host monitoring, file management, database administration, and container management.
- **Rapid Website Deployment**: With deep integration of the popular open-source website building software WordPress, 1Panel streamlines the process of domain binding and SSL certificate configuration, all achievable with just one click.
- **Application Store**: 1Panel curates a wide range of high-quality open-source tools and applications, facilitating easy installation and updates for its users.
- **Security and Reliability**: By leveraging containerization and secure application deployment practices, 1Panel minimizes vulnerability exposure. It further enhances security through integrated firewall management and log auditing capabilities.
@@ -44,28 +45,22 @@
Execute the script below and follow the prompts to install 1Panel:
```bash
curl -sSL https://resource.1panel.pro/quick_start.sh -o quick_start.sh && bash quick_start.sh
curl -sSL https://resource.1panel.hk/quick_start.sh -o quick_start.sh && bash quick_start.sh
```
Please refer to our [documentation](https://docs.1panel.pro/quick_start/) for more details.
中国用户请使用这个 [安装脚本](https://1panel.cn/docs/installation/online_installation/),其应用数量比国际版本更丰富。
Please refer to our [documentation](https://docs.1panel.hk/quick_start/) for more details.
## Screenshot
![UI Display](https://resource.1panel.pro/img/1panel.png)
![UI Display](https://resource.1panel.hk/img/1panel.png)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)
## Pro Edition
Compared to the OSS Edition, 1Panel Pro Edition provides users with a wealth of enhanced features and technical support services. Enhanced features include WAF enhancement, website tamper protection, website monitoring, GPU monitoring, custom logo and theme color, etc. [Click to view the detailed introduction of the Pro Edition](https://1panel.pro/pricing).
## Security Information
If you discover any security issues, please refer to [SECURITY.md](/SECURITY.md).
If you discover any security issues, please contact us through: wanghe@fit2cloud.com.
## License
@@ -73,4 +68,4 @@ Licensed under The GNU General Public License version 3 (GPLv3) (the "License")
<https://www.gnu.org/licenses/gpl-3.0.html>
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
-256
View File
@@ -1,256 +0,0 @@
package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
"github.com/gin-gonic/gin"
)
// @Tags AI
// @Summary Create Ollama model
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型 [name]","formatEN":"add Ollama model [name]"}
func (b *BaseApi) CreateOllamaModel(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := aiToolService.Create(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags AI
// @Summary Rereate Ollama model
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/recreate [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型重试 [name]","formatEN":"re-add Ollama model [name]"}
func (b *BaseApi) RecreateOllamaModel(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := aiToolService.Recreate(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags AI
// @Summary Close Ollama model conn
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/close [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"关闭 Ollama 模型连接 [name]","formatEN":"close conn for Ollama model [name]"}
func (b *BaseApi) CloseOllamaModel(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := aiToolService.Close(req.Name); err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags AI
// @Summary Sync Ollama model list
// @Success 200 {array} dto.OllamaModelDropList
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/sync [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"同步 Ollama 模型列表","formatEN":"sync Ollama model list"}
func (b *BaseApi) SyncOllamaModel(c *gin.Context) {
list, err := aiToolService.Sync()
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags AI
// @Summary Page Ollama models
// @Accept json
// @Param request body dto.SearchWithPage true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/search [post]
func (b *BaseApi) SearchOllamaModel(c *gin.Context) {
var req dto.SearchWithPage
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := aiToolService.Search(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags AI
// @Summary Page Ollama models
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200 {string} details
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/load [post]
func (b *BaseApi) LoadOllamaModelDetail(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
detail, err := aiToolService.LoadDetail(req.Name)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, detail)
}
// @Tags AI
// @Summary Delete Ollama model
// @Accept json
// @Param request body dto.ForceDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"ollama_models","output_column":"name","output_value":"names"}],"formatZH":"删除 Ollama 模型 [names]","formatEN":"remove Ollama model [names]"}
func (b *BaseApi) DeleteOllamaModel(c *gin.Context) {
var req dto.ForceDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := aiToolService.Delete(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Load gpu / xpu info
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/gpu/load [get]
func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
ok, client := gpu.New()
if ok {
info, err := client.LoadGpuInfo()
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, info)
return
}
xpuOK, xpuClient := xpu.New()
if xpuOK {
info, err := xpuClient.LoadGpuInfo()
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, info)
return
}
helper.SuccessWithData(c, &common.GpuInfo{})
}
// @Tags AI
// @Summary Bind domain
// @Accept json
// @Param request body dto.OllamaBindDomain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/domain/bind [post]
func (b *BaseApi) BindDomain(c *gin.Context) {
var req dto.OllamaBindDomain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := aiToolService.BindDomain(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
// @Tags AI
// @Summary Get bind domain
// @Accept json
// @Param request body dto.OllamaBindDomainReq true "request"
// @Success 200 {object} dto.OllamaBindDomainRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/domain/get [post]
func (b *BaseApi) GetBindDomain(c *gin.Context) {
var req dto.OllamaBindDomainReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := aiToolService.GetBindDomain(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, res)
}
// Tags AI
// Summary Update bind domain
// Accept json
// Param request body dto.OllamaBindDomain true "request"
// Success 200
// Security ApiKeyAuth
// Security Timestamp
// Router /ai/domain/update [post]
func (b *BaseApi) UpdateBindDomain(c *gin.Context) {
var req dto.OllamaBindDomain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := aiToolService.UpdateBindDomain(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.Success(c)
}
-66
View File
@@ -1,66 +0,0 @@
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"
)
// @Tags App
// @Summary List Upgrade Ignored App
// @Accept json
// @Success 200 {array} model.AppIgnoreUpgrade
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /apps/ignored/detail [get]
func (b *BaseApi) ListAppIgnored(c *gin.Context) {
res, err := appIgnoreUpgradeService.List()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags App
// @Summary Ignore Upgrade App
// @Accept json
// @Param request body request.AppIgnoreUpgradeReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /apps/installed/ignore [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"忽略应用升级","formatEN":"Ignore application upgrade"}
func (b *BaseApi) IgnoreAppUpgrade(c *gin.Context) {
var req request.AppIgnoreUpgradeReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := appIgnoreUpgradeService.CreateAppIgnore(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// 写一个去掉忽略的接口
// @Tags App
// @Summary Cancel Ignore Upgrade App
// @Accept json
// @Param request body request.ReqWithID true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /apps/ignored/cancel [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"取消忽略应用升级","formatEN":"Cancel ignore application upgrade"}
func (b *BaseApi) CancelIgnoreAppUpgrade(c *gin.Context) {
var req request.ReqWithID
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := appIgnoreUpgradeService.Delete(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
-169
View File
@@ -1,169 +0,0 @@
package v2
import (
"errors"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/gin-gonic/gin"
)
// @Tags Dashboard
// @Summary Load os info
// @Accept json
// @Success 200 {object} dto.OsInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/base/os [get]
func (b *BaseApi) LoadDashboardOsInfo(c *gin.Context) {
data, err := dashboardService.LoadOsInfo()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Load app launcher
// @Accept json
// @Success 200 {Array} dto.AppLauncher
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/app/launcher [get]
func (b *BaseApi) LoadAppLauncher(c *gin.Context) {
data, err := dashboardService.LoadAppLauncher(c)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Load app launcher options
// @Accept json
// @Param request body dto.SearchByFilter true "request"
// @Success 200 {Array} dto.LauncherOption
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/app/launcher/option [post]
func (b *BaseApi) LoadAppLauncherOption(c *gin.Context) {
var req dto.SearchByFilter
if err := helper.CheckBind(&req, c); err != nil {
return
}
data, err := dashboardService.ListLauncherOption(req.Filter)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Update app Launcher
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/app/launcher/show [post]
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"首页应用 [key] => 显示:[value]","formatEN":"app launcher [key] => show: [value]"}
func (b *BaseApi) UpdateAppLauncher(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := dashboardService.ChangeShow(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Dashboard
// @Summary Load dashboard base info
// @Accept json
// @Param ioOption path string true "request"
// @Param netOption path string true "request"
// @Success 200 {object} dto.DashboardBase
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/base/:ioOption/:netOption [get]
func (b *BaseApi) LoadDashboardBaseInfo(c *gin.Context) {
ioOption, ok := c.Params.Get("ioOption")
if !ok {
helper.BadRequest(c, errors.New("error ioOption in path"))
return
}
netOption, ok := c.Params.Get("netOption")
if !ok {
helper.BadRequest(c, errors.New("error ioOption in path"))
return
}
data, err := dashboardService.LoadBaseInfo(ioOption, netOption)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Load dashboard current info for node
// @Success 200 {object} dto.NodeCurrent
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/current/node [get]
func (b *BaseApi) LoadCurrentInfoForNode(c *gin.Context) {
data := dashboardService.LoadCurrentInfoForNode()
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Load dashboard current info
// @Accept json
// @Param ioOption path string true "request"
// @Param netOption path string true "request"
// @Success 200 {object} dto.DashboardCurrent
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/current/:ioOption/:netOption [get]
func (b *BaseApi) LoadDashboardCurrentInfo(c *gin.Context) {
ioOption, ok := c.Params.Get("ioOption")
if !ok {
helper.BadRequest(c, errors.New("error ioOption in path"))
return
}
netOption, ok := c.Params.Get("netOption")
if !ok {
helper.BadRequest(c, errors.New("error netOption in path"))
return
}
data := dashboardService.LoadCurrentInfo(ioOption, netOption)
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary System restart
// @Accept json
// @Param operation path string true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/system/restart/:operation [post]
func (b *BaseApi) SystemRestart(c *gin.Context) {
operation, ok := c.Params.Get("operation")
if !ok {
helper.BadRequest(c, errors.New("error operation in path"))
return
}
if err := dashboardService.Restart(operation); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
-10
View File
@@ -1,10 +0,0 @@
package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/gin-gonic/gin"
)
func (b *BaseApi) CheckHealth(c *gin.Context) {
helper.Success(c)
}
-122
View File
@@ -1,122 +0,0 @@
package helper
import (
"context"
"fmt"
"net/http"
"strconv"
"github.com/1Panel-dev/1Panel/agent/global"
"gorm.io/gorm"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
)
func ErrorWithDetail(ctx *gin.Context, code int, msgKey string, err error) {
res := dto.Response{
Code: code,
Message: "",
}
res.Message = i18n.GetMsgWithDetail(msgKey, err.Error())
ctx.JSON(http.StatusOK, res)
ctx.Abort()
}
func InternalServer(ctx *gin.Context, err error) {
ErrorWithDetail(ctx, http.StatusInternalServerError, "ErrInternalServer", err)
}
func BadRequest(ctx *gin.Context, err error) {
ErrorWithDetail(ctx, http.StatusBadRequest, "ErrInvalidParams", err)
}
func SuccessWithData(ctx *gin.Context, data interface{}) {
if data == nil {
data = gin.H{}
}
res := dto.Response{
Code: http.StatusOK,
Data: data,
}
ctx.JSON(http.StatusOK, res)
ctx.Abort()
}
func Success(ctx *gin.Context) {
res := dto.Response{
Code: http.StatusOK,
Message: "success",
}
ctx.JSON(http.StatusOK, res)
ctx.Abort()
}
func SuccessWithMsg(ctx *gin.Context, msg string) {
res := dto.Response{
Code: http.StatusOK,
Message: msg,
}
ctx.JSON(http.StatusOK, res)
ctx.Abort()
}
func GetParamID(c *gin.Context) (uint, error) {
idParam, ok := c.Params.Get("id")
if !ok {
return 0, errors.New("error id in path")
}
intNum, _ := strconv.Atoi(idParam)
return uint(intNum), nil
}
func GetIntParamByKey(c *gin.Context, key string) (uint, error) {
idParam, ok := c.Params.Get(key)
if !ok {
return 0, fmt.Errorf("error %s in path", key)
}
intNum, _ := strconv.Atoi(idParam)
return uint(intNum), nil
}
func GetStrParamByKey(c *gin.Context, key string) (string, error) {
idParam, ok := c.Params.Get(key)
if !ok {
return "", fmt.Errorf("error %s in path", key)
}
return idParam, nil
}
func GetTxAndContext() (tx *gorm.DB, ctx context.Context) {
tx = global.DB.Begin()
ctx = context.WithValue(context.Background(), constant.DB, tx)
return
}
func CheckBindAndValidate(req interface{}, c *gin.Context) error {
if err := c.ShouldBindJSON(req); err != nil {
ErrorWithDetail(c, http.StatusBadRequest, "ErrInvalidParams", err)
return err
}
if err := global.VALID.Struct(req); err != nil {
ErrorWithDetail(c, http.StatusBadRequest, "ErrInvalidParams", err)
return err
}
return nil
}
func CheckBind(req interface{}, c *gin.Context) error {
if err := c.ShouldBindJSON(&req); err != nil {
ErrorWithDetail(c, http.StatusBadRequest, "ErrInvalidParams", err)
return err
}
return nil
}
func ErrResponse(ctx *gin.Context, code int) {
ctx.JSON(code, nil)
ctx.Abort()
}
-22
View File
@@ -1,22 +0,0 @@
package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/gin-gonic/gin"
)
// @Tags Logs
// @Summary Load system log files
// @Success 200 {array} string
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /logs/system/files [get]
func (b *BaseApi) GetSystemFiles(c *gin.Context) {
data, err := logService.ListSystemLogFile()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
-166
View File
@@ -1,166 +0,0 @@
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"
)
// @Tags McpServer
// @Summary List mcp servers
// @Accept json
// @Param request body request.McpServerSearch true "request"
// @Success 200 {object} response.McpServersRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /mcp/search [post]
func (b *BaseApi) PageMcpServers(c *gin.Context) {
var req request.McpServerSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list := mcpServerService.Page(req)
helper.SuccessWithData(c, list)
}
// @Tags McpServer
// @Summary Create mcp server
// @Accept json
// @Param request body request.McpServerCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /mcp/server [post]
func (b *BaseApi) CreateMcpServer(c *gin.Context) {
var req request.McpServerCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := mcpServerService.Create(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags McpServer
// @Summary Update mcp server
// @Accept json
// @Param request body request.McpServerUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /mcp/server/update [post]
func (b *BaseApi) UpdateMcpServer(c *gin.Context) {
var req request.McpServerUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := mcpServerService.Update(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags McpServer
// @Summary Delete mcp server
// @Accept json
// @Param request body request.McpServerDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /mcp/server/del [post]
func (b *BaseApi) DeleteMcpServer(c *gin.Context) {
var req request.McpServerDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := mcpServerService.Delete(req.ID)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags McpServer
// @Summary Operate mcp server
// @Accept json
// @Param request body request.McpServerOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /mcp/server/op [post]
func (b *BaseApi) OperateMcpServer(c *gin.Context) {
var req request.McpServerOperate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := mcpServerService.Operate(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags McpServer
// @Summary Bind Domain for mcp server
// @Accept json
// @Param request body request.McpBindDomain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /mcp/domain/bind [post]
func (b *BaseApi) BindMcpDomain(c *gin.Context) {
var req request.McpBindDomain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := mcpServerService.BindDomain(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags McpServer
// @Summary Update bind Domain for mcp server
// @Accept json
// @Param request body request.McpBindDomainUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /mcp/domain/update [post]
func (b *BaseApi) UpdateMcpBindDomain(c *gin.Context) {
var req request.McpBindDomainUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := mcpServerService.UpdateBindDomain(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags McpServer
// @Summary Get bin Domain for mcp server
// @Accept json
// @Success 200 {object} response.McpBindDomainRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /mcp/domain/get [get]
func (b *BaseApi) GetMcpBindDomain(c *gin.Context) {
res, err := mcpServerService.GetBindDomain()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
-107
View File
@@ -1,107 +0,0 @@
package v2
import (
"sort"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/net"
)
// @Tags Monitor
// @Summary Load monitor data
// @Param request body dto.MonitorSearch true "request"
// @Success 200 {array} dto.MonitorData
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/monitor/search [post]
func (b *BaseApi) LoadMonitor(c *gin.Context) {
var req dto.MonitorSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := monitorService.LoadMonitorData(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Monitor
// @Summary Clean monitor data
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/monitor/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空监控数据","formatEN":"clean monitor datas"}
func (b *BaseApi) CleanMonitor(c *gin.Context) {
if err := monitorService.CleanData(); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Monitor
// @Summary Load monitor setting
// @Success 200 {object} dto.MonitorSetting
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/monitor/setting [get]
func (b *BaseApi) LoadMonitorSetting(c *gin.Context) {
setting, err := monitorService.LoadSetting()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, setting)
}
// @Tags Monitor
// @Summary Update monitor setting
// @Param request body dto.MonitorSettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /hosts/monitor/setting/update [post]
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改默认监控网卡 [name]-[value]","formatEN":"update default monitor [name]-[value]"}
func (b *BaseApi) UpdateMonitorSetting(c *gin.Context) {
var req dto.MonitorSettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := monitorService.UpdateSetting(req.Key, req.Value); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
netStat, _ := net.IOCounters(true)
var options []string
options = append(options, "all")
for _, net := range netStat {
options = append(options, net.Name)
}
sort.Strings(options)
helper.SuccessWithData(c, options)
}
func (b *BaseApi) GetIOOptions(c *gin.Context) {
diskStat, _ := disk.IOCounters()
var options []string
options = append(options, "all")
for _, net := range diskStat {
options = append(options, net.Name)
}
sort.Strings(options)
helper.SuccessWithData(c, options)
}
-537
View File
@@ -1,537 +0,0 @@
package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/gin-gonic/gin"
)
// @Tags Runtime
// @Summary List runtimes
// @Accept json
// @Param request body request.RuntimeSearch true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/search [post]
func (b *BaseApi) SearchRuntimes(c *gin.Context) {
var req request.RuntimeSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, items, err := runtimeService.Page(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: items,
})
}
// @Tags Runtime
// @Summary Create runtime
// @Accept json
// @Param request body request.RuntimeCreate true "request"
// @Success 200 {object} model.Runtime
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建运行环境 [name]","formatEN":"Create runtime [name]"}
func (b *BaseApi) CreateRuntime(c *gin.Context) {
var req request.RuntimeCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
ssl, err := runtimeService.Create(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, ssl)
}
// @Tags Website
// @Summary Delete runtime
// @Accept json
// @Param request body request.RuntimeDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除运行环境 [name]","formatEN":"Delete runtime [name]"}
func (b *BaseApi) DeleteRuntime(c *gin.Context) {
var req request.RuntimeDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := runtimeService.Delete(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Website
// @Summary Delete runtime
// @Accept json
// @Success 200 {array} dto.AppResource
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /installed/delete/check/:id [get]
func (b *BaseApi) DeleteRuntimeCheck(c *gin.Context) {
runTimeId, err := helper.GetIntParamByKey(c, "id")
if err != nil {
helper.BadRequest(c, err)
return
}
checkData, err := runtimeService.DeleteCheck(runTimeId)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, checkData)
}
// @Tags Runtime
// @Summary Update runtime
// @Accept json
// @Param request body request.RuntimeUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/update [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新运行环境 [name]","formatEN":"Update runtime [name]"}
func (b *BaseApi) UpdateRuntime(c *gin.Context) {
var req request.RuntimeUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := runtimeService.Update(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Get runtime
// @Accept json
// @Param id path string true "request"
// @Success 200 {object} response.RuntimeDTO
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/:id [get]
func (b *BaseApi) GetRuntime(c *gin.Context) {
id, err := helper.GetIntParamByKey(c, "id")
if err != nil {
helper.BadRequest(c, err)
return
}
res, err := runtimeService.Get(id)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Runtime
// @Summary Get Node package scripts
// @Accept json
// @Param request body request.NodePackageReq true "request"
// @Success 200 {array} response.PackageScripts
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/node/package [post]
func (b *BaseApi) GetNodePackageRunScript(c *gin.Context) {
var req request.NodePackageReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := runtimeService.GetNodePackageRunScript(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Runtime
// @Summary Operate runtime
// @Accept json
// @Param request body request.RuntimeOperate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/operate [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"操作运行环境 [name]","formatEN":"Operate runtime [name]"}
func (b *BaseApi) OperateRuntime(c *gin.Context) {
var req request.RuntimeOperate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := runtimeService.OperateRuntime(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Get Node modules
// @Accept json
// @Param request body request.NodeModuleReq true "request"
// @Success 200 {array} response.NodeModule
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/node/modules [post]
func (b *BaseApi) GetNodeModules(c *gin.Context) {
var req request.NodeModuleReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := runtimeService.GetNodeModules(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Runtime
// @Summary Operate Node modules
// @Accept json
// @Param request body request.NodeModuleReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/node/modules/operate [post]
func (b *BaseApi) OperateNodeModules(c *gin.Context) {
var req request.NodeModuleOperateReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := runtimeService.OperateNodeModules(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Sync runtime status
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/sync [post]
func (b *BaseApi) SyncStatus(c *gin.Context) {
err := runtimeService.SyncRuntimeStatus()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Get php runtime extension
// @Accept json
// @Param id path string true "request"
// @Success 200 {object} response.PHPExtensionRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/:id/extensions [get]
func (b *BaseApi) GetRuntimeExtension(c *gin.Context) {
id, err := helper.GetIntParamByKey(c, "id")
if err != nil {
helper.BadRequest(c, err)
return
}
res, err := runtimeService.GetPHPExtensions(id)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Runtime
// @Summary Install php extension
// @Accept json
// @Param request body request.PHPExtensionInstallReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/extensions/install [post]
func (b *BaseApi) InstallPHPExtension(c *gin.Context) {
var req request.PHPExtensionInstallReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := runtimeService.InstallPHPExtension(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary UnInstall php extension
// @Accept json
// @Param request body request.PHPExtensionInstallReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/extensions/uninstall [post]
func (b *BaseApi) UnInstallPHPExtension(c *gin.Context) {
var req request.PHPExtensionInstallReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := runtimeService.UnInstallPHPExtension(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Load php runtime conf
// @Accept json
// @Param id path integer true "request"
// @Success 200 {object} response.PHPConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/config/:id [get]
func (b *BaseApi) GetPHPConfig(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.BadRequest(c, err)
return
}
data, err := runtimeService.GetPHPConfig(id)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Runtime
// @Summary Update runtime php conf
// @Accept json
// @Param request body request.PHPConfigUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/config [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"[domain] PHP 配置修改","formatEN":"[domain] PHP conf update"}
func (b *BaseApi) UpdatePHPConfig(c *gin.Context) {
var req request.PHPConfigUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := runtimeService.UpdatePHPConfig(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Update php conf file
// @Accept json
// @Param request body request.PHPFileUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/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":"php 配置修改 [domain]","formatEN":"Nginx conf update [domain]"}
func (b *BaseApi) UpdatePHPFile(c *gin.Context) {
var req request.PHPFileUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := runtimeService.UpdatePHPConfigFile(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Get php conf file
// @Accept json
// @Param request body request.PHPFileReq true "request"
// @Success 200 {object} response.FileInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/file [post]
func (b *BaseApi) GetPHPConfigFile(c *gin.Context) {
var req request.PHPFileReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := runtimeService.GetPHPConfigFile(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Runtime
// @Summary Update fpm config
// @Accept json
// @Param request body request.FPMConfig true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/fpm/config [post]
func (b *BaseApi) UpdateFPMConfig(c *gin.Context) {
var req request.FPMConfig
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := runtimeService.UpdateFPMConfig(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Get fpm config
// @Accept json
// @Param id path integer true "request"
// @Success 200 {object} request.FPMConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/fpm/config/:id [get]
func (b *BaseApi) GetFPMConfig(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.BadRequest(c, err)
return
}
data, err := runtimeService.GetFPMConfig(id)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Runtime
// @Summary Get supervisor process
// @Accept json
// @Param id path integer true "request"
// @Success 200 {array} response.SupervisorProcessConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/supervisor/process/:id [get]
func (b *BaseApi) GetSupervisorProcess(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.BadRequest(c, err)
return
}
data, err := runtimeService.GetSupervisorProcess(id)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Runtime
// @Summary Operate supervisor process
// @Accept json
// @Param request body request.PHPSupervisorProcessConfig true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/supervisor/process/operate [post]
func (b *BaseApi) OperateSupervisorProcess(c *gin.Context) {
var req request.PHPSupervisorProcessConfig
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
err := runtimeService.OperateSupervisorProcess(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Operate supervisor process file
// @Accept json
// @Param request body request.PHPSupervisorProcessFileReq true "request"
// @Success 200 {string} content
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/supervisor/process/file/operate [post]
func (b *BaseApi) OperateSupervisorProcessFile(c *gin.Context) {
var req request.PHPSupervisorProcessFileReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := runtimeService.OperateSupervisorProcessFile(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, res)
}
// @Tags Runtime
// @Summary Update PHP container config
// @Accept json
// @Param request body request.PHPContainerUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/container/update [post]
func (b *BaseApi) UpdatePHPContainer(c *gin.Context) {
var req request.PHPContainerConfig
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := runtimeService.UpdatePHPContainer(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags Runtime
// @Summary Get PHP container config
// @Accept json
// @Param id path integer true "request"
// @Success 200 {object} response.PHPContainerConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/php/container/:id [get]
func (b *BaseApi) GetPHPContainerConfig(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.BadRequest(c, err)
return
}
data, err := runtimeService.GetPHPContainerConfig(id)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
-161
View File
@@ -1,161 +0,0 @@
package v2
import (
"encoding/json"
"os"
"os/user"
"path"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
)
// @Tags System Setting
// @Summary Load system setting info
// @Success 200 {object} dto.SettingInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/search [post]
func (b *BaseApi) GetSettingInfo(c *gin.Context) {
setting, err := settingService.GetSettingInfo()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, setting)
}
// @Tags System Setting
// @Summary Load system available status
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/search/available [get]
func (b *BaseApi) GetSystemAvailable(c *gin.Context) {
helper.Success(c)
}
// @Tags System Setting
// @Summary Update system setting
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/update [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改系统配置 [key] => [value]","formatEN":"update system setting [key] => [value]"}
func (b *BaseApi) UpdateSetting(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := settingService.Update(req.Key, req.Value); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
// @Tags System Setting
// @Summary Load local backup dir
// @Success 200 {string} path
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/basedir [get]
func (b *BaseApi) LoadBaseDir(c *gin.Context) {
helper.SuccessWithData(c, global.Dir.DataDir)
}
func (b *BaseApi) CheckLocalConn(c *gin.Context) {
_, err := loadLocalConn()
helper.SuccessWithData(c, err == nil)
}
// @Tags System Setting
// @Summary Check local conn info
// @Success 200 {bool} isOk
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/ssh/check/info [post]
func (b *BaseApi) CheckLocalConnByInfo(c *gin.Context) {
var req dto.SSHConnData
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
helper.SuccessWithData(c, settingService.TestConnByInfo(req))
}
// @Tags System Setting
// @Summary Save local conn info
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/ssh [post]
func (b *BaseApi) SaveLocalConnInfo(c *gin.Context) {
var req dto.SSHConnData
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
helper.SuccessWithData(c, settingService.SaveConnInfo(req))
}
func loadLocalConn() (*ssh.SSHClient, error) {
itemPath := ""
currentInfo, _ := user.Current()
if len(currentInfo.HomeDir) == 0 {
itemPath = "/root/.ssh/id_ed25519_1panel"
} else {
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
}
if _, err := os.Stat(itemPath); err != nil {
_ = sshService.GenerateSSH(dto.GenerateSSH{EncryptionMode: "ed25519", Name: "_1panel"})
}
privateKey, _ := os.ReadFile(itemPath)
connWithKey := ssh.ConnInfo{
Addr: "127.0.0.1",
User: "root",
Port: 22,
AuthMode: "key",
PrivateKey: privateKey,
}
client, err := ssh.NewClient(connWithKey)
if err == nil {
return client, nil
}
connInfoInDB, err := settingService.GetSSHInfo()
if err != nil {
return nil, err
}
if len(connInfoInDB) == 0 {
return nil, errors.New("no such ssh conn info in db!")
}
var connInDB ssh.ConnInfo
if err := json.Unmarshal([]byte(connInfoInDB), &connInDB); err != nil {
return nil, err
}
return ssh.NewClient(connInDB)
}
// @Tags System Setting
// @Summary Load system setting by key
// @Param key path string true "key"
// @Success 200 {object} dto.SettingInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /settings/get/{key} [get]
func (b *BaseApi) GetSettingByKey(c *gin.Context) {
key := c.Param("key")
if len(key) == 0 {
helper.BadRequest(c, errors.New("key is empty"))
return
}
value := settingService.GetSettingByKey(key)
helper.SuccessWithData(c, value)
}
-46
View File
@@ -1,46 +0,0 @@
package v2
import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/gin-gonic/gin"
)
// @Tags TaskLog
// @Summary Page task logs
// @Accept json
// @Param request body dto.SearchTaskLogReq true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /logs/tasks/search [post]
func (b *BaseApi) PageTasks(c *gin.Context) {
var req dto.SearchTaskLogReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := taskService.Page(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags TaskLog
// @Summary Get the number of executing tasks
// @Success 200 {object} int64
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /logs/tasks/executing/count [get]
func (b *BaseApi) CountExecutingTasks(c *gin.Context) {
count, err := taskService.CountExecutingTask()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, count)
}
-46
View File
@@ -1,46 +0,0 @@
package dto
import "time"
type OllamaModelInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Size string `json:"size"`
From string `json:"from"`
LogFileExist bool `json:"logFileExist"`
Status string `json:"status"`
Message string `json:"message"`
CreatedAt time.Time `json:"createdAt"`
}
type OllamaModelDropList struct {
ID uint `json:"id"`
Name string `json:"name"`
}
type OllamaModelName struct {
Name string `json:"name"`
TaskID string `json:"taskID"`
}
type OllamaBindDomain struct {
Domain string `json:"domain" validate:"required"`
AppInstallID uint `json:"appInstallID" validate:"required"`
SSLID uint `json:"sslID"`
WebsiteID uint `json:"websiteID"`
IPList string `json:"ipList"`
}
type OllamaBindDomainReq struct {
AppInstallID uint `json:"appInstallID" validate:"required"`
}
type OllamaBindDomainRes struct {
Domain string `json:"domain"`
SSLID uint `json:"sslID"`
AllowIPs []string `json:"allowIPs"`
WebsiteID uint `json:"websiteID"`
ConnUrl string `json:"connUrl"`
AcmeAccountID uint `json:"acmeAccountID"`
}
-120
View File
@@ -1,120 +0,0 @@
package dto
import (
"time"
)
type BackupOperate struct {
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type" validate:"required"`
IsPublic bool `json:"isPublic"`
Bucket string `json:"bucket"`
AccessKey string `json:"accessKey"`
Credential string `json:"credential"`
BackupPath string `json:"backupPath"`
Vars string `json:"vars" validate:"required"`
RememberAuth bool `json:"rememberAuth"`
}
type BackupInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
IsPublic bool `json:"isPublic"`
Bucket string `json:"bucket"`
AccessKey string `json:"accessKey"`
Credential string `json:"credential"`
BackupPath string `json:"backupPath"`
Vars string `json:"vars"`
CreatedAt time.Time `json:"createdAt"`
RememberAuth bool `json:"rememberAuth"`
}
type ForBuckets struct {
Type string `json:"type" validate:"required"`
AccessKey string `json:"accessKey"`
Credential string `json:"credential" validate:"required"`
Vars string `json:"vars" validate:"required"`
}
type SyncFromMaster struct {
Name string `json:"name" validate:"required"`
Operation string `json:"operation" validate:"required,oneof=create delete update"`
Data string `json:"data"`
}
type BackupOption struct {
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
IsPublic bool `json:"isPublic"`
}
type CommonBackup struct {
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
Name string `json:"name"`
DetailName string `json:"detailName"`
Secret string `json:"secret"`
TaskID string `json:"taskID"`
FileName string `json:"fileName"`
Description string `json:"description"`
}
type CommonRecover struct {
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
Name string `json:"name"`
DetailName string `json:"detailName"`
File string `json:"file"`
Secret string `json:"secret"`
TaskID string `json:"taskID"`
BackupRecordID uint `json:"backupRecordID"`
}
type RecordSearch struct {
PageInfo
Type string `json:"type" validate:"required"`
Name string `json:"name"`
DetailName string `json:"detailName"`
}
type RecordSearchByCronjob struct {
PageInfo
CronjobID uint `json:"cronjobID" validate:"required"`
}
type BackupRecords struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
AccountType string `json:"accountType"`
AccountName string `json:"accountName"`
DownloadAccountID uint `json:"downloadAccountID"`
FileDir string `json:"fileDir"`
FileName string `json:"fileName"`
Description string `json:"description"`
}
type DownloadRecord struct {
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
FileDir string `json:"fileDir" validate:"required"`
FileName string `json:"fileName" validate:"required"`
}
type SearchForSize struct {
PageInfo
Type string `json:"type" validate:"required"`
Name string `json:"name"`
DetailName string `json:"detailName"`
Info string `json:"info"`
CronjobID uint `json:"cronjobID"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
}
type RecordFileSize struct {
ID uint `json:"id"`
Name string `json:"name"`
Size int64 `json:"size"`
}
-139
View File
@@ -1,139 +0,0 @@
package dto
import (
"time"
)
type PageCronjob struct {
PageInfo
Info string `json:"info"`
OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
}
type CronjobSpec struct {
Spec string `json:"spec" validate:"required"`
}
type CronjobOperate struct {
ID uint `json:"id"`
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
SpecCustom bool `json:"specCustom"`
Spec string `json:"spec" validate:"required"`
Executor string `json:"executor"`
ScriptMode string `json:"scriptMode"`
Script string `json:"script"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
User string `json:"user"`
ScriptID uint `json:"scriptID"`
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBType string `json:"dbType"`
DBName string `json:"dbName"`
URL string `json:"url"`
IsDir bool `json:"isDir"`
SourceDir string `json:"sourceDir"`
SourceAccountIDs string `json:"sourceAccountIDs"`
DownloadAccountID uint `json:"downloadAccountID"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
RetryTimes int `json:"retryTimes" validate:"number,min=0"`
Timeout uint `json:"timeout" validate:"number,min=1"`
Secret string `json:"secret"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
}
type CronjobUpdateStatus struct {
ID uint `json:"id" validate:"required"`
Status string `json:"status" validate:"required"`
}
type CronjobDownload struct {
RecordID uint `json:"recordID" validate:"required"`
BackupAccountID uint `json:"backupAccountID" validate:"required"`
}
type CronjobClean struct {
IsDelete bool `json:"isDelete"`
CleanData bool `json:"cleanData"`
CronjobID uint `json:"cronjobID" validate:"required"`
CleanRemoteData bool `json:"cleanRemoteData"`
}
type CronjobBatchDelete struct {
CleanData bool `json:"cleanData"`
CleanRemoteData bool `json:"cleanRemoteData"`
IDs []uint `json:"ids" validate:"required"`
}
type CronjobInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
SpecCustom bool `json:"specCustom"`
Spec string `json:"spec"`
Executor string `json:"executor"`
ScriptMode string `json:"scriptMode"`
Script string `json:"script"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
User string `json:"user"`
ScriptID uint `json:"scriptID"`
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBType string `json:"dbType"`
DBName string `json:"dbName"`
URL string `json:"url"`
IsDir bool `json:"isDir"`
SourceDir string `json:"sourceDir"`
RetainCopies int `json:"retainCopies"`
RetryTimes int `json:"retryTimes"`
Timeout uint `json:"timeout"`
SourceAccounts []string `json:"sourceAccounts"`
DownloadAccount string `json:"downloadAccount"`
SourceAccountIDs string `json:"sourceAccountIDs"`
DownloadAccountID uint `json:"downloadAccountID"`
LastRecordStatus string `json:"lastRecordStatus"`
LastRecordTime string `json:"lastRecordTime"`
Status string `json:"status"`
Secret string `json:"secret"`
AlertCount uint `json:"alertCount"`
}
type ScriptOptions struct {
ID uint `json:"id"`
Name string `json:"name"`
}
type SearchRecord struct {
PageInfo
CronjobID int `json:"cronjobID"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Status string `json:"status"`
}
type Record struct {
ID uint `json:"id"`
TaskID string `json:"taskID"`
StartTime string `json:"startTime"`
Records string `json:"records"`
Status string `json:"status"`
Message string `json:"message"`
TargetPath string `json:"targetPath"`
Interval int `json:"interval"`
File string `json:"file"`
}
-29
View File
@@ -1,29 +0,0 @@
package dto
type GroupCreate struct {
ID uint `json:"id"`
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
}
type GroupSearch struct {
Type string `json:"type" validate:"required"`
}
type GroupUpdate struct {
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type" validate:"required"`
IsDefault bool `json:"isDefault"`
}
type GroupInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
IsDefault bool `json:"isDefault"`
}
type OperateByType struct {
Type string `json:"type"`
}
-15
View File
@@ -1,15 +0,0 @@
package dto
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
)
type SearchTaskLogReq struct {
Status string `json:"status"`
Type string `json:"type"`
PageInfo
}
type TaskDTO struct {
model.Task
}
@@ -1,7 +0,0 @@
package request
type AppIgnoreUpgradeReq struct {
AppID uint `json:"appID" validate:"required"`
AppDetailID uint `json:"appDetailID"`
Scope string `json:"scope" validate:"required,oneof=all version"`
}
-5
View File
@@ -1,5 +0,0 @@
package request
type ReqWithID struct {
ID uint `json:"id" validate:"required"`
}
-47
View File
@@ -1,47 +0,0 @@
package request
import "github.com/1Panel-dev/1Panel/agent/app/dto"
type McpServerSearch struct {
dto.PageInfo
Name string `json:"name"`
Sync bool `json:"sync"`
}
type McpServerCreate struct {
Name string `json:"name" validate:"required"`
Command string `json:"command" validate:"required"`
Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"`
Port int `json:"port" validate:"required"`
ContainerName string `json:"containerName"`
BaseURL string `json:"baseUrl"`
SsePath string `json:"ssePath"`
HostIP string `json:"hostIP"`
}
type McpServerUpdate struct {
ID uint `json:"id" validate:"required"`
McpServerCreate
}
type McpServerDelete struct {
ID uint `json:"id" validate:"required"`
}
type McpServerOperate struct {
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
}
type McpBindDomain struct {
Domain string `json:"domain" validate:"required"`
SSLID uint `json:"sslID"`
IPList string `json:"ipList"`
}
type McpBindDomainUpdate struct {
WebsiteID uint `json:"websiteID" validate:"required"`
SSLID uint `json:"sslID"`
IPList string `json:"ipList"`
}
-133
View File
@@ -1,133 +0,0 @@
package request
import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
)
type RuntimeSearch struct {
dto.PageInfo
Type string `json:"type"`
Name string `json:"name"`
Status string `json:"status"`
}
type RuntimeCreate struct {
AppDetailID uint `json:"appDetailId"`
Name string `json:"name"`
Params map[string]interface{} `json:"params"`
Resource string `json:"resource"`
Image string `json:"image"`
Type string `json:"type"`
Version string `json:"version"`
Source string `json:"source"`
CodeDir string `json:"codeDir"`
NodeConfig
}
type NodeConfig struct {
Install bool `json:"install"`
Clean bool `json:"clean"`
ExposedPorts []ExposedPort `json:"exposedPorts"`
Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"`
}
type Environment struct {
Key string `json:"key"`
Value string `json:"value"`
}
type Volume struct {
Source string `json:"source"`
Target string `json:"target"`
}
type ExposedPort struct {
HostPort int `json:"hostPort"`
ContainerPort int `json:"containerPort"`
HostIP string `json:"hostIP"`
}
type RuntimeDelete struct {
ID uint `json:"id"`
ForceDelete bool `json:"forceDelete"`
}
type RuntimeUpdate struct {
Name string `json:"name"`
ID uint `json:"id"`
Params map[string]interface{} `json:"params"`
Image string `json:"image"`
Version string `json:"version"`
Rebuild bool `json:"rebuild"`
Source string `json:"source"`
CodeDir string `json:"codeDir"`
NodeConfig
}
type NodePackageReq struct {
CodeDir string `json:"codeDir"`
}
type RuntimeOperate struct {
Operate string `json:"operate"`
ID uint `json:"ID"`
}
type NodeModuleOperateReq struct {
Operate string `json:"operate" validate:"oneof=install uninstall update"`
ID uint `json:"ID" validate:"required"`
Module string `json:"module"`
PkgManager string `json:"pkgManager" validate:"oneof=npm yarn"`
}
type NodeModuleReq struct {
ID uint `json:"ID" validate:"required"`
}
type PHPExtensionInstallReq struct {
ID uint `json:"ID" validate:"required"`
Name string `json:"name" validate:"required"`
TaskID string `json:"taskID"`
}
type PHPConfigUpdate struct {
ID uint `json:"id" validate:"required"`
Params map[string]string `json:"params"`
Scope string `json:"scope" validate:"required"`
DisableFunctions []string `json:"disableFunctions"`
UploadMaxSize string `json:"uploadMaxSize"`
}
type PHPFileUpdate struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
Content string `json:"content" validate:"required"`
}
type PHPFileReq struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
}
type FPMConfig struct {
ID uint `json:"id" validate:"required"`
Params map[string]interface{} `json:"params" validate:"required"`
}
type PHPSupervisorProcessConfig struct {
ID uint `json:"id" validate:"required"`
SupervisorProcessConfig
}
type PHPSupervisorProcessFileReq struct {
ID uint `json:"id" validate:"required"`
SupervisorProcessFileReq
}
type PHPContainerConfig struct {
ID uint `json:"id" validate:"required"`
ContainerName string `json:"containerName"`
ExposedPorts []ExposedPort `json:"exposedPorts"`
Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"`
}
-1
View File
@@ -1 +0,0 @@
package request
@@ -1,10 +0,0 @@
package response
type AppIgnoreUpgradeDTO struct {
ID uint `json:"ID"`
AppID uint `json:"appID"`
AppDetailID uint `json:"appDetailID"`
Scope string `json:"scope"`
Version string `json:"version"`
Icon string `json:"icon"`
}
-26
View File
@@ -1,26 +0,0 @@
package response
import (
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/model"
)
type McpServersRes struct {
Items []McpServerDTO `json:"items"`
Total int64 `json:"total"`
}
type McpServerDTO struct {
model.McpServer
Environments []request.Environment `json:"environments"`
Volumes []request.Volume `json:"volumes"`
}
type McpBindDomainRes struct {
Domain string `json:"domain"`
SSLID uint `json:"sslID"`
AcmeAccountID uint `json:"acmeAccountID"`
AllowIPs []string `json:"allowIPs"`
WebsiteID uint `json:"websiteID"`
ConnUrl string `json:"connUrl"`
}
-78
View File
@@ -1,78 +0,0 @@
package response
import (
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/model"
)
type RuntimeDTO struct {
ID uint `json:"id"`
Name string `json:"name"`
Resource string `json:"resource"`
AppDetailID uint `json:"appDetailID"`
AppID uint `json:"appID"`
Source string `json:"source"`
Status string `json:"status"`
Type string `json:"type"`
Image string `json:"image"`
Params map[string]interface{} `json:"params"`
Message string `json:"message"`
Version string `json:"version"`
CreatedAt time.Time `json:"createdAt"`
CodeDir string `json:"codeDir"`
AppParams []AppParam `json:"appParams"`
Port string `json:"port"`
Path string `json:"path"`
ExposedPorts []request.ExposedPort `json:"exposedPorts"`
Environments []request.Environment `json:"environments"`
Volumes []request.Volume `json:"volumes"`
ContainerStatus string `json:"containerStatus"`
Container string `json:"container"`
}
type PackageScripts struct {
Name string `json:"name"`
Script string `json:"script"`
}
func NewRuntimeDTO(runtime model.Runtime) RuntimeDTO {
return RuntimeDTO{
ID: runtime.ID,
Name: runtime.Name,
Resource: runtime.Resource,
AppDetailID: runtime.AppDetailID,
Status: runtime.Status,
Type: runtime.Type,
Image: runtime.Image,
Message: runtime.Message,
CreatedAt: runtime.CreatedAt,
CodeDir: runtime.CodeDir,
Version: runtime.Version,
Port: runtime.Port,
Path: runtime.GetPath(),
Container: runtime.ContainerName,
}
}
type NodeModule struct {
Name string `json:"name"`
Version string `json:"version"`
License string `json:"license"`
Description string `json:"description"`
}
type SupportExtension struct {
Name string `json:"name"`
Description string `json:"description"`
Installed bool `json:"installed"`
Check string `json:"check"`
Versions []string `json:"versions"`
File string `json:"file"`
}
type PHPExtensionRes struct {
Extensions []string `json:"extensions"`
SupportExtensions []SupportExtension `json:"supportExtensions"`
}
-1
View File
@@ -1 +0,0 @@
package response
-82
View File
@@ -1,82 +0,0 @@
package dto
type SettingInfo struct {
DockerSockPath string `json:"dockerSockPath"`
SystemVersion string `json:"systemVersion"`
SystemIP string `json:"systemIP"`
LocalTime string `json:"localTime"`
TimeZone string `json:"timeZone"`
NtpSite string `json:"ntpSite"`
DefaultNetwork string `json:"defaultNetwork"`
LastCleanTime string `json:"lastCleanTime"`
LastCleanSize string `json:"lastCleanSize"`
LastCleanData string `json:"lastCleanData"`
MonitorStatus string `json:"monitorStatus"`
MonitorInterval string `json:"monitorInterval"`
MonitorStoreDays string `json:"monitorStoreDays"`
AppStoreVersion string `json:"appStoreVersion"`
AppStoreLastModified string `json:"appStoreLastModified"`
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
FileRecycleBin string `json:"fileRecycleBin"`
SnapshotIgnore string `json:"snapshotIgnore"`
}
type SettingUpdate struct {
Key string `json:"key" validate:"required"`
Value string `json:"value"`
}
type SyncTime struct {
NtpSite string `json:"ntpSite" validate:"required"`
}
type CleanData struct {
SystemClean []CleanTree `json:"systemClean"`
UploadClean []CleanTree `json:"uploadClean"`
DownloadClean []CleanTree `json:"downloadClean"`
SystemLogClean []CleanTree `json:"systemLogClean"`
ContainerClean []CleanTree `json:"containerClean"`
}
type CleanTree struct {
ID string `json:"id"`
Label string `json:"label"`
Children []CleanTree `json:"children"`
Type string `json:"type"`
Name string `json:"name"`
Size uint64 `json:"size"`
IsCheck bool `json:"isCheck"`
IsRecommend bool `json:"isRecommend"`
}
type Clean struct {
TreeType string `json:"treeType"`
Name string `json:"name"`
Size uint64 `json:"size"`
}
type SSHConnData struct {
Addr string `json:"addr" validate:"required"`
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
User string `json:"user" validate:"required"`
AuthMode string `json:"authMode" validate:"oneof=password key"`
Password string `json:"password"`
PrivateKey string `json:"privateKey"`
PassPhrase string `json:"passPhrase"`
}
type SystemProxy struct {
Type string `json:"type"`
URL string `json:"url"`
Port string `json:"port"`
User string `json:"user"`
Password string `json:"password"`
}
-99
View File
@@ -1,99 +0,0 @@
package dto
import "time"
type PageSnapshot struct {
PageInfo
Info string `json:"info"`
OrderBy string `json:"orderBy" validate:"required,oneof=name createdAt"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
}
type SnapshotCreate struct {
ID uint `json:"id"`
Name string `json:"name"`
TaskID string `json:"taskID"`
SourceAccountIDs string `json:"sourceAccountIDs" validate:"required"`
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
Description string `json:"description" validate:"max=256"`
Secret string `json:"secret"`
InterruptStep string `json:"interruptStep"`
AppData []DataTree `json:"appData"`
BackupData []DataTree `json:"backupData"`
PanelData []DataTree `json:"panelData"`
WithMonitorData bool `json:"withMonitorData"`
WithLoginLog bool `json:"withLoginLog"`
WithOperationLog bool `json:"withOperationLog"`
WithSystemLog bool `json:"withSystemLog"`
WithTaskLog bool `json:"withTaskLog"`
}
type SnapshotData struct {
AppData []DataTree `json:"appData"`
BackupData []DataTree `json:"backupData"`
PanelData []DataTree `json:"panelData"`
WithMonitorData bool `json:"withMonitorData"`
WithLoginLog bool `json:"withLoginLog"`
WithOperationLog bool `json:"withOperationLog"`
WithSystemLog bool `json:"withSystemLog"`
WithTaskLog bool `json:"withTaskLog"`
}
type DataTree struct {
ID string `json:"id"`
Label string `json:"label"`
Key string `json:"key"`
Name string `json:"name"`
Size uint64 `json:"size"`
IsCheck bool `json:"isCheck"`
IsDisable bool `json:"isDisable"`
Path string `json:"path"`
RelationItemID string `json:"relationItemID"`
Children []DataTree `json:"children"`
}
type SnapshotRecover struct {
IsNew bool `json:"isNew"`
ReDownload bool `json:"reDownload"`
ID uint `json:"id" validate:"required"`
TaskID string `json:"taskID"`
Secret string `json:"secret"`
}
type SnapshotBatchDelete struct {
DeleteWithFile bool `json:"deleteWithFile"`
Ids []uint `json:"ids" validate:"required"`
}
type SnapshotImport struct {
BackupAccountID uint `json:"backupAccountID"`
Names []string `json:"names"`
Description string `json:"description" validate:"max=256"`
}
type SnapshotInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Description string `json:"description" validate:"max=256"`
SourceAccounts []string `json:"sourceAccounts"`
DownloadAccount string `json:"downloadAccount"`
Status string `json:"status"`
Message string `json:"message"`
CreatedAt time.Time `json:"createdAt"`
Version string `json:"version"`
Size int64 `json:"size"`
TaskID string `json:"taskID"`
TaskRecoverID string `json:"taskRecoverID"`
TaskRollbackID string `json:"taskRollbackID"`
InterruptStep string `json:"interruptStep"`
RecoverStatus string `json:"recoverStatus"`
RecoverMessage string `json:"recoverMessage"`
LastRecoveredAt string `json:"lastRecoveredAt"`
RollbackStatus string `json:"rollbackStatus"`
RollbackMessage string `json:"rollbackMessage"`
LastRollbackedAt string `json:"lastRollbackedAt"`
}
-11
View File
@@ -1,11 +0,0 @@
package model
type OllamaModel struct {
BaseModel
Name string `json:"name"`
Size string `json:"size"`
From string `json:"from"`
Status string `json:"status"`
Message string `json:"message"`
}
-76
View File
@@ -1,76 +0,0 @@
package model
import (
"encoding/json"
"github.com/gin-gonic/gin"
"path/filepath"
"strings"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
)
type App struct {
BaseModel
Name string `json:"name" gorm:"not null"`
Key string `json:"key" gorm:"not null;"`
ShortDescZh string `json:"shortDescZh" yaml:"shortDescZh"`
ShortDescEn string `json:"shortDescEn" yaml:"shortDescEn"`
Description string `json:"description"`
Icon string `json:"icon"`
Type string `json:"type" gorm:"not null"`
Status string `json:"status" gorm:"not null"`
Required string `json:"required"`
CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"`
Limit int `json:"limit" gorm:"not null"`
Website string `json:"website" gorm:"not null"`
Github string `json:"github" gorm:"not null"`
Document string `json:"document" gorm:"not null"`
Recommend int `json:"recommend" gorm:"not null"`
Resource string `json:"resource" gorm:"not null;default:remote"`
ReadMe string `json:"readMe"`
LastModified int `json:"lastModified"`
Architectures string `json:"architectures"`
MemoryRequired int `json:"memoryRequired"`
GpuSupport bool `json:"gpuSupport"`
RequiredPanelVersion float64 `json:"requiredPanelVersion"`
Details []AppDetail `json:"-" gorm:"-:migration"`
TagsKey []string `json:"tags" yaml:"tags" gorm:"-"`
AppTags []AppTag `json:"-" gorm:"-:migration"`
}
func (i *App) IsLocalApp() bool {
return i.Resource == constant.ResourceLocal
}
func (i *App) GetAppResourcePath() string {
if i.IsLocalApp() {
//这里要去掉本地应用的local前缀
return filepath.Join(global.Dir.LocalAppResourceDir, strings.TrimPrefix(i.Key, "local"))
}
return filepath.Join(global.Dir.RemoteAppResourceDir, i.Key)
}
func getLang(c *gin.Context) string {
lang := c.GetHeader("Accept-Language")
if lang == "" {
lang = "en"
}
return lang
}
func (i *App) GetDescription(ctx *gin.Context) string {
var translations = make(map[string]string)
_ = json.Unmarshal([]byte(i.Description), &translations)
lang := strings.ToLower(getLang(ctx))
if desc, ok := translations[lang]; ok && desc != "" {
return desc
}
if lang == "zh" && i.ShortDescZh != "" {
return i.ShortDescZh
}
if desc, ok := translations["en"]; ok && desc != "" {
return desc
}
return i.ShortDescEn
}
-15
View File
@@ -1,15 +0,0 @@
package model
type AppDetail struct {
BaseModel
AppId uint `json:"appId" gorm:"not null"`
Version string `json:"version" gorm:"not null"`
Params string `json:"-"`
DockerCompose string `json:"dockerCompose"`
Status string `json:"status" gorm:"not null"`
LastVersion string `json:"lastVersion"`
LastModified int `json:"lastModified"`
DownloadUrl string `json:"downloadUrl"`
DownloadCallBackUrl string `json:"downloadCallBackUrl"`
Update bool `json:"update"`
}
-8
View File
@@ -1,8 +0,0 @@
package model
type AppIgnoreUpgrade struct {
BaseModel
AppID uint `json:"appID"`
AppDetailID uint `json:"appDetailID"`
Scope string `json:"scope"`
}
-51
View File
@@ -1,51 +0,0 @@
package model
import (
"path"
"strings"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
)
type AppInstall struct {
BaseModel
Name string `json:"name" gorm:"not null;UNIQUE"`
AppId uint `json:"appId" gorm:"not null"`
AppDetailId uint `json:"appDetailId" gorm:"not null"`
Version string `json:"version" gorm:"not null"`
Param string `json:"param"`
Env string `json:"env"`
DockerCompose string `json:"dockerCompose" `
Status string `json:"status" gorm:"not null"`
Description string `json:"description"`
Message string `json:"message"`
ContainerName string `json:"containerName" gorm:"not null"`
ServiceName string `json:"serviceName" gorm:"not null"`
HttpPort int `json:"httpPort"`
HttpsPort int `json:"httpsPort"`
WebUI string `json:"webUI"`
Favorite bool `json:"favorite"`
App App `json:"app" gorm:"-:migration"`
}
func (i *AppInstall) GetPath() string {
return path.Join(i.GetAppPath(), i.Name)
}
func (i *AppInstall) GetComposePath() string {
return path.Join(i.GetAppPath(), i.Name, "docker-compose.yml")
}
func (i *AppInstall) GetEnvPath() string {
return path.Join(i.GetAppPath(), i.Name, ".env")
}
func (i *AppInstall) GetAppPath() string {
if i.App.Resource == constant.AppResourceLocal {
return path.Join(global.Dir.LocalAppInstallDir, strings.TrimPrefix(i.App.Key, constant.AppResourceLocal))
} else {
return path.Join(global.Dir.AppInstallDir, i.App.Key)
}
}
-10
View File
@@ -1,10 +0,0 @@
package model
type AppInstallResource struct {
BaseModel
AppInstallId uint `json:"appInstallId" gorm:"not null;"`
LinkId uint `json:"linkId" gorm:"not null;"`
ResourceId uint `json:"resourceId"`
Key string `json:"key" gorm:"not null"`
From string `json:"from" gorm:"not null;default:local"`
}
-6
View File
@@ -1,6 +0,0 @@
package model
type AppLauncher struct {
BaseModel
Key string `json:"key"`
}
-7
View File
@@ -1,7 +0,0 @@
package model
type AppTag struct {
BaseModel
AppId uint `json:"appId" gorm:"not null"`
TagId uint `json:"tagId" gorm:"not null"`
}
-31
View File
@@ -1,31 +0,0 @@
package model
type BackupAccount struct {
BaseModel
Name string `gorm:"not null;default:''" json:"name"`
Type string `gorm:"not null;default:''" json:"type"`
IsPublic bool `json:"isPublic"`
Bucket string `json:"bucket"`
AccessKey string `json:"accessKey"`
Credential string `json:"credential"`
BackupPath string `json:"backupPath"`
Vars string `json:"vars"`
RememberAuth bool `json:"rememberAuth"`
}
type BackupRecord struct {
BaseModel
From string `json:"from"`
CronjobID uint `json:"cronjobID"`
SourceAccountIDs string `json:"sourceAccountIDs"`
DownloadAccountID uint `json:"downloadAccountID"`
Type string `gorm:"not null;default:''" json:"type"`
Name string `gorm:"not null;default:''" json:"name"`
DetailName string `json:"detailName"`
FileDir string `json:"fileDir"`
FileName string `json:"fileName"`
Description string `json:"description"`
}
-14
View File
@@ -1,14 +0,0 @@
package model
type Clam struct {
BaseModel
Name string `gorm:"not null" json:"name"`
Status string `json:"status"`
Path string `gorm:"not null" json:"path"`
InfectedStrategy string `json:"infectedStrategy"`
InfectedDir string `json:"infectedDir"`
Spec string `json:"spec"`
EntryID int `json:"entryID"`
Description string `json:"description"`
}
-16
View File
@@ -1,16 +0,0 @@
package model
type ComposeTemplate struct {
BaseModel
Name string `gorm:"not null;unique" json:"name"`
Description string `json:"description"`
Content string `json:"content"`
}
type Compose struct {
BaseModel
Name string `json:"name"`
Path string `json:"path"`
}
-64
View File
@@ -1,64 +0,0 @@
package model
import (
"time"
)
// BackupAccounts ---> SourceAccountIDs
// BackupAccounts ---> DownloadAccountID
type Cronjob struct {
BaseModel
Name string `gorm:"not null" json:"name"`
Type string `gorm:"not null" json:"type"`
SpecCustom bool `json:"specCustom"`
Spec string `gorm:"not null" json:"spec"`
Executor string `json:"executor"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
ScriptMode string `json:"scriptMode"`
Script string `json:"script"`
User string `json:"user"`
ScriptID uint `json:"scriptID"`
Website string `json:"website"`
AppID string `json:"appID"`
DBType string `json:"dbType"`
DBName string `json:"dbName"`
URL string `json:"url"`
IsDir bool `json:"isDir"`
SourceDir string `json:"sourceDir"`
ExclusionRules string `json:"exclusionRules"`
SourceAccountIDs string `json:"sourceAccountIDs"`
DownloadAccountID uint `json:"downloadAccountID"`
RetryTimes uint `json:"retryTimes"`
Timeout uint `json:"timeout"`
RetainCopies uint64 `json:"retainCopies"`
Status string `json:"status"`
EntryIDs string `json:"entryIDs"`
Records []JobRecords `json:"records"`
Secret string `json:"secret"`
}
type JobRecords struct {
BaseModel
CronjobID uint `json:"cronjobID"`
TaskID string `json:"taskID"`
StartTime time.Time `json:"startTime"`
Interval float64 `json:"interval"`
Records string `json:"records"`
FromLocal bool `json:"source"`
File string `json:"file"`
Status string `json:"status"`
Message string `json:"message"`
}
type ScriptLibrary struct {
BaseModel
Name string `json:"name" gorm:"not null;"`
Script string `json:"script" gorm:"not null;"`
}
-22
View File
@@ -1,22 +0,0 @@
package model
type Database struct {
BaseModel
AppInstallID uint `json:"appInstallID"`
Name string `json:"name" gorm:"not null;unique"`
Type string `json:"type" gorm:"not null"`
Version string `json:"version" gorm:"not null"`
From string `json:"from" gorm:"not null"`
Address string `json:"address" gorm:"not null"`
Port uint `json:"port" gorm:"not null"`
Username string `json:"username"`
Password string `json:"password"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
ClientKey string `json:"clientKey"`
ClientCert string `json:"clientCert"`
SkipVerify bool `json:"skipVerify"`
Description string `json:"description"`
}
-14
View File
@@ -1,14 +0,0 @@
package model
type DatabaseMysql struct {
BaseModel
Name string `json:"name" gorm:"not null"`
From string `json:"from" gorm:"not null;default:local"`
MysqlName string `json:"mysqlName" gorm:"not null"`
Format string `json:"format" gorm:"not null"`
Username string `json:"username" gorm:"not null"`
Password string `json:"password" gorm:"not null"`
Permission string `json:"permission" gorm:"not null"`
IsDelete bool `json:"isDelete"`
Description string `json:"description"`
}
-14
View File
@@ -1,14 +0,0 @@
package model
type DatabasePostgresql struct {
BaseModel
Name string `json:"name" gorm:"not null"`
From string `json:"from" gorm:"not null;default:local"`
PostgresqlName string `json:"postgresqlName" gorm:"not null"`
Format string `json:"format" gorm:"not null"`
Username string `json:"username" gorm:"not null"`
Password string `json:"password" gorm:"not null"`
SuperUser bool `json:"superUser"`
IsDelete bool `json:"isDelete"`
Description string `json:"description"`
}
-10
View File
@@ -1,10 +0,0 @@
package model
type Favorite struct {
BaseModel
Name string `gorm:"not null;" json:"name" `
Path string `gorm:"not null;unique" json:"path"`
Type string `json:"type"`
IsDir bool `json:"isDir"`
IsTxt bool `json:"isTxt"`
}
-21
View File
@@ -1,21 +0,0 @@
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"`
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"`
}
-11
View File
@@ -1,11 +0,0 @@
package model
type Ftp struct {
BaseModel
User string `gorm:"not null" json:"user"`
Password string `gorm:"not null" json:"password"`
Status string `gorm:"not null" json:"status"`
Path string `gorm:"not null" json:"path"`
Description string `gorm:"not null" json:"description"`
}
-8
View File
@@ -1,8 +0,0 @@
package model
type Group struct {
BaseModel
IsDefault bool `json:"isDefault"`
Name string `json:"name"`
Type string `json:"type"`
}
-15
View File
@@ -1,15 +0,0 @@
package model
type ImageRepo struct {
BaseModel
Name string `gorm:"not null" json:"name"`
DownloadUrl string `json:"downloadUrl"`
Protocol string `json:"protocol"`
Username string `json:"username"`
Password string `json:"password"`
Auth bool `json:"auth"`
Status string `json:"status"`
Message string `json:"message"`
}
-18
View File
@@ -1,18 +0,0 @@
package model
type McpServer struct {
BaseModel
Name string `json:"name"`
DockerCompose string `json:"dockerCompose"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
Message string `json:"message"`
Port int `json:"port"`
Status string `json:"status"`
Env string `json:"env"`
BaseURL string `json:"baseUrl"`
SsePath string `json:"ssePath"`
WebsiteID int `json:"websiteID"`
Dir string `json:"dir"`
HostIP string `json:"hostIP"`
}
-29
View File
@@ -1,29 +0,0 @@
package model
type MonitorBase struct {
BaseModel
Cpu float64 `json:"cpu"`
LoadUsage float64 `json:"loadUsage"`
CpuLoad1 float64 `json:"cpuLoad1"`
CpuLoad5 float64 `json:"cpuLoad5"`
CpuLoad15 float64 `json:"cpuLoad15"`
Memory float64 `json:"memory"`
}
type MonitorIO struct {
BaseModel
Name string `json:"name"`
Read uint64 `json:"read"`
Write uint64 `json:"write"`
Count uint64 `json:"count"`
Time uint64 `json:"time"`
}
type MonitorNetwork struct {
BaseModel
Name string `json:"name"`
Up float64 `json:"up"`
Down float64 `json:"down"`
}
-50
View File
@@ -1,50 +0,0 @@
package model
import (
"path"
"github.com/1Panel-dev/1Panel/agent/global"
)
type Runtime struct {
BaseModel
Name string `gorm:"not null" json:"name"`
AppDetailID uint `json:"appDetailID"`
Image string `json:"image"`
WorkDir string `json:"workDir"`
DockerCompose string `json:"dockerCompose"`
Env string `json:"env"`
Params string `json:"params"`
Version string `gorm:"not null" json:"version"`
Type string `gorm:"not null" json:"type"`
Status string `gorm:"not null" json:"status"`
Resource string `gorm:"not null" json:"resource"`
Port string `json:"port"`
Message string `json:"message"`
CodeDir string `json:"codeDir"`
ContainerName string `json:"containerName"`
}
func (r *Runtime) GetComposePath() string {
return path.Join(r.GetPath(), "docker-compose.yml")
}
func (r *Runtime) GetEnvPath() string {
return path.Join(r.GetPath(), ".env")
}
func (r *Runtime) GetPath() string {
return path.Join(global.Dir.RuntimeDir, r.Type, r.Name)
}
func (r *Runtime) GetFPMPath() string {
return path.Join(global.Dir.RuntimeDir, r.Type, r.Name, "conf", "php-fpm.conf")
}
func (r *Runtime) GetPHPPath() string {
return path.Join(global.Dir.RuntimeDir, r.Type, r.Name, "conf", "php.ini")
}
func (r *Runtime) GetLogPath() string {
return path.Join(r.GetPath(), "build.log")
}
-17
View File
@@ -1,17 +0,0 @@
package model
type Setting struct {
BaseModel
Key string `json:"key" gorm:"not null;"`
Value string `json:"value"`
About string `json:"about"`
}
type NodeInfo struct {
Scope string `json:"scope"`
BaseDir string `json:"baseDir"`
NodePort uint `json:"nodePort"`
Version string `json:"version"`
ServerCrt string `json:"serverCrt"`
ServerKey string `json:"serverKey"`
}
-32
View File
@@ -1,32 +0,0 @@
package model
type Snapshot struct {
BaseModel
Name string `json:"name" gorm:"not null;unique"`
Secret string `json:"secret"`
Description string `json:"description"`
SourceAccountIDs string `json:"sourceAccountIDs"`
DownloadAccountID uint `json:"downloadAccountID"`
Status string `json:"status"`
Message string `json:"message"`
Version string `json:"version"`
TaskID string `json:"taskID"`
TaskRecoverID string `json:"taskRecoverID"`
TaskRollbackID string `json:"taskRollbackID"`
AppData string `json:"appData"`
PanelData string `json:"panelData"`
BackupData string `json:"backupData"`
WithMonitorData bool `json:"withMonitorData"`
WithLoginLog bool `json:"withLoginLog"`
WithOperationLog bool `json:"withOperationLog"`
WithSystemLog bool `json:"withSystemLog"`
WithTaskLog bool `json:"withTaskLog"`
InterruptStep string `json:"interruptStep"`
RecoverStatus string `json:"recoverStatus"`
RecoverMessage string `json:"recoverMessage"`
RollbackStatus string `json:"rollbackStatus"`
RollbackMessage string `json:"rollbackMessage"`
}
-9
View File
@@ -1,9 +0,0 @@
package model
type Tag struct {
BaseModel
Key string `json:"key" gorm:"not null"`
Name string `json:"name" gorm:"not null"`
Sort int `json:"sort" gorm:"not null;default:1"`
Translations string `json:"translations"`
}
-18
View File
@@ -1,18 +0,0 @@
package model
import "time"
type Task struct {
ID string `gorm:"primarykey;" json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Operate string `json:"operate"`
LogFile string `json:"logFile"`
Status string `json:"status"`
ErrorMsg string `json:"errorMsg"`
OperationLogID uint `json:"operationLogID"`
ResourceID uint `json:"resourceID"`
CurrentStep string `json:"currentStep"`
EndAt time.Time `json:"endAt"`
CreatedAt time.Time `json:"createdAt"`
}
-46
View File
@@ -1,46 +0,0 @@
package model
import "time"
type Website struct {
BaseModel
Protocol string `gorm:"not null" json:"protocol"`
PrimaryDomain string `gorm:"not null" json:"primaryDomain"`
Type string `gorm:"not null" json:"type"`
Alias string `gorm:"not null" json:"alias"`
Remark string `json:"remark"`
Status string `gorm:"not null" json:"status"`
HttpConfig string `gorm:"not null" json:"httpConfig"`
ExpireDate time.Time `json:"expireDate"`
Proxy string `json:"proxy"`
ProxyType string `json:"proxyType"`
SiteDir string `json:"siteDir"`
ErrorLog bool `json:"errorLog"`
AccessLog bool `json:"accessLog"`
DefaultServer bool `json:"defaultServer"`
IPV6 bool `json:"IPV6"`
Rewrite string `json:"rewrite"`
WebsiteGroupID uint `json:"webSiteGroupId"`
WebsiteSSLID uint `json:"webSiteSSLId"`
RuntimeID uint `json:"runtimeID"`
AppInstallID uint `json:"appInstallId"`
FtpID uint `json:"ftpId"`
ParentWebsiteID uint `json:"parentWebsiteID"`
User string `json:"user"`
Group string `json:"group"`
DbType string `json:"dbType"`
DbID uint `json:"dbID"`
Favorite bool `json:"favorite"`
Domains []WebsiteDomain `json:"domains" gorm:"-:migration"`
WebsiteSSL WebsiteSSL `json:"webSiteSSL" gorm:"-:migration"`
}
func (w Website) TableName() string {
return "websites"
}
-12
View File
@@ -1,12 +0,0 @@
package model
type WebsiteDnsAccount struct {
BaseModel
Name string `gorm:"not null" json:"name"`
Type string `gorm:"not null" json:"type"`
Authorization string `gorm:"not null" json:"-"`
}
func (w WebsiteDnsAccount) TableName() string {
return "website_dns_accounts"
}
-13
View File
@@ -1,13 +0,0 @@
package model
type WebsiteDomain struct {
BaseModel
WebsiteID uint `gorm:"column:website_id;not null;" json:"websiteId"`
Domain string `gorm:"not null" json:"domain"`
SSL bool `json:"ssl"`
Port int `json:"port"`
}
func (w WebsiteDomain) TableName() string {
return "website_domains"
}
-69
View File
@@ -1,69 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
)
type AiRepo struct{}
type IAiRepo interface {
Get(opts ...DBOption) (model.OllamaModel, error)
List(opts ...DBOption) ([]model.OllamaModel, error)
Page(limit, offset int, opts ...DBOption) (int64, []model.OllamaModel, error)
Create(cronjob *model.OllamaModel) error
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
}
func NewIAiRepo() IAiRepo {
return &AiRepo{}
}
func (u *AiRepo) Get(opts ...DBOption) (model.OllamaModel, error) {
var item model.OllamaModel
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&item).Error
return item, err
}
func (u *AiRepo) List(opts ...DBOption) ([]model.OllamaModel, error) {
var list []model.OllamaModel
db := global.DB.Model(&model.OllamaModel{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&list).Error
return list, err
}
func (u *AiRepo) Page(page, size int, opts ...DBOption) (int64, []model.OllamaModel, error) {
var list []model.OllamaModel
db := global.DB.Model(&model.OllamaModel{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&list).Error
return count, list, err
}
func (u *AiRepo) Create(item *model.OllamaModel) error {
return global.DB.Create(item).Error
}
func (u *AiRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.OllamaModel{}).Where("id = ?", id).Updates(vars).Error
}
func (u *AiRepo) Delete(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.OllamaModel{}).Error
}
-41
View File
@@ -1,41 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
"gorm.io/gorm"
)
type AppIgnoreUpgradeRepo struct {
}
type IAppIgnoreUpgradeRepo interface {
WithScope(scope string) DBOption
List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error)
Create(appIgnoreUpgrade *model.AppIgnoreUpgrade) error
Delete(opts ...DBOption) error
}
func NewIAppIgnoreUpgradeRepo() IAppIgnoreUpgradeRepo {
return &AppIgnoreUpgradeRepo{}
}
func (a AppIgnoreUpgradeRepo) WithScope(scope string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("scope = ?", scope)
}
}
func (a AppIgnoreUpgradeRepo) List(opts ...DBOption) ([]model.AppIgnoreUpgrade, error) {
var appIgnoreUpgradeList []model.AppIgnoreUpgrade
err := getDb(opts...).Find(&appIgnoreUpgradeList).Error
return appIgnoreUpgradeList, err
}
func (a AppIgnoreUpgradeRepo) Create(appIgnoreUpgrade *model.AppIgnoreUpgrade) error {
return global.DB.Create(appIgnoreUpgrade).Error
}
func (a AppIgnoreUpgradeRepo) Delete(opts ...DBOption) error {
return getDb(opts...).Delete(&model.AppIgnoreUpgrade{}).Error
}
-59
View File
@@ -1,59 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
)
type LauncherRepo struct{}
type ILauncherRepo interface {
Get(opts ...DBOption) (model.AppLauncher, error)
ListName(opts ...DBOption) ([]string, error)
Create(launcher *model.AppLauncher) error
Save(launcher *model.AppLauncher) error
Delete(opts ...DBOption) error
}
func NewILauncherRepo() ILauncherRepo {
return &LauncherRepo{}
}
func (u *LauncherRepo) Get(opts ...DBOption) (model.AppLauncher, error) {
var launcher model.AppLauncher
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&launcher).Error
return launcher, err
}
func (u *LauncherRepo) ListName(opts ...DBOption) ([]string, error) {
var ops []model.AppLauncher
db := global.DB.Model(&model.AppLauncher{})
for _, opt := range opts {
db = opt(db)
}
_ = db.Find(&ops).Error
var names []string
for i := 0; i < len(ops); i++ {
names = append(names, ops[i].Key)
}
return names, nil
}
func (u *LauncherRepo) Create(launcher *model.AppLauncher) error {
return global.DB.Create(launcher).Error
}
func (u *LauncherRepo) Save(launcher *model.AppLauncher) error {
return global.DB.Save(launcher).Error
}
func (u *LauncherRepo) Delete(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.AppLauncher{}).Error
}
-78
View File
@@ -1,78 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
"gorm.io/gorm"
)
type GroupRepo struct{}
type IGroupRepo interface {
Get(opts ...DBOption) (model.Group, error)
GetList(opts ...DBOption) ([]model.Group, error)
Create(group *model.Group) error
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
CancelDefault(groupType string) error
WithByDefault(isDefault bool) DBOption
WithByWebsiteDefault() DBOption
}
func NewIGroupRepo() IGroupRepo {
return &GroupRepo{}
}
func (g *GroupRepo) WithByDefault(isDefault bool) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("is_default = ?", isDefault)
}
}
func (g *GroupRepo) Get(opts ...DBOption) (model.Group, error) {
var group model.Group
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&group).Error
return group, err
}
func (g *GroupRepo) GetList(opts ...DBOption) ([]model.Group, error) {
var groups []model.Group
db := global.DB.Model(&model.Group{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&groups).Error
return groups, err
}
func (g *GroupRepo) Create(group *model.Group) error {
return global.DB.Create(group).Error
}
func (g *GroupRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.Group{}).Where("id = ?", id).Updates(vars).Error
}
func (g *GroupRepo) Delete(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.Group{}).Error
}
func (g *GroupRepo) WithByWebsiteDefault() DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("is_default = ? AND type = ?", 1, "website")
}
}
func (g *GroupRepo) CancelDefault(groupType string) error {
return global.DB.Model(&model.Group{}).
Where("is_default = ? AND type = ?", 1, groupType).
Updates(map[string]interface{}{"is_default": 0}).Error
}
-65
View File
@@ -1,65 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
)
type HostRepo struct{}
type IHostRepo interface {
GetFirewallRecord(opts ...DBOption) (model.Firewall, error)
ListFirewallRecord() ([]model.Firewall, error)
SaveFirewallRecord(firewall *model.Firewall) error
DeleteFirewallRecordByID(id uint) error
DeleteFirewallRecord(fType, port, protocol, address, strategy string) error
}
func NewIHostRepo() IHostRepo {
return &HostRepo{}
}
func (h *HostRepo) GetFirewallRecord(opts ...DBOption) (model.Firewall, error) {
var firewall model.Firewall
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&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
}
return datas, nil
}
func (h *HostRepo) SaveFirewallRecord(firewall *model.Firewall) error {
if firewall.ID != 0 {
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
}
}
return global.DB.Save(firewall).Error
}
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
}
-56
View File
@@ -1,56 +0,0 @@
package repo
import "github.com/1Panel-dev/1Panel/agent/app/model"
type McpServerRepo struct {
}
type IMcpServerRepo interface {
Page(page, size int, opts ...DBOption) (int64, []model.McpServer, error)
GetFirst(opts ...DBOption) (*model.McpServer, error)
Create(mcpServer *model.McpServer) error
Save(mcpServer *model.McpServer) error
DeleteBy(opts ...DBOption) error
List(opts ...DBOption) ([]model.McpServer, error)
}
func NewIMcpServerRepo() IMcpServerRepo {
return &McpServerRepo{}
}
func (m McpServerRepo) Page(page, size int, opts ...DBOption) (int64, []model.McpServer, error) {
var servers []model.McpServer
db := getDb(opts...).Model(&model.McpServer{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&servers).Error
return count, servers, err
}
func (m McpServerRepo) GetFirst(opts ...DBOption) (*model.McpServer, error) {
var mcpServer model.McpServer
if err := getDb(opts...).First(&mcpServer).Error; err != nil {
return nil, err
}
return &mcpServer, nil
}
func (m McpServerRepo) List(opts ...DBOption) ([]model.McpServer, error) {
var mcpServers []model.McpServer
if err := getDb(opts...).Find(&mcpServers).Error; err != nil {
return nil, err
}
return mcpServers, nil
}
func (m McpServerRepo) Create(mcpServer *model.McpServer) error {
return getDb().Create(mcpServer).Error
}
func (m McpServerRepo) Save(mcpServer *model.McpServer) error {
return getDb().Save(mcpServer).Error
}
func (m McpServerRepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.McpServer{}).Error
}
-74
View File
@@ -1,74 +0,0 @@
package repo
import (
"time"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
)
type MonitorRepo struct{}
type IMonitorRepo interface {
GetBase(opts ...DBOption) ([]model.MonitorBase, error)
GetIO(opts ...DBOption) ([]model.MonitorIO, error)
GetNetwork(opts ...DBOption) ([]model.MonitorNetwork, error)
CreateMonitorBase(model model.MonitorBase) error
BatchCreateMonitorIO(ioList []model.MonitorIO) error
BatchCreateMonitorNet(ioList []model.MonitorNetwork) error
DelMonitorBase(timeForDelete time.Time) error
DelMonitorIO(timeForDelete time.Time) error
DelMonitorNet(timeForDelete time.Time) error
}
func NewIMonitorRepo() IMonitorRepo {
return &MonitorRepo{}
}
func (u *MonitorRepo) GetBase(opts ...DBOption) ([]model.MonitorBase, error) {
var data []model.MonitorBase
db := global.MonitorDB
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&data).Error
return data, err
}
func (u *MonitorRepo) GetIO(opts ...DBOption) ([]model.MonitorIO, error) {
var data []model.MonitorIO
db := global.MonitorDB
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&data).Error
return data, err
}
func (u *MonitorRepo) GetNetwork(opts ...DBOption) ([]model.MonitorNetwork, error) {
var data []model.MonitorNetwork
db := global.MonitorDB
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&data).Error
return data, err
}
func (u *MonitorRepo) CreateMonitorBase(model model.MonitorBase) error {
return global.MonitorDB.Create(&model).Error
}
func (u *MonitorRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
}
func (u *MonitorRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
}
func (u *MonitorRepo) DelMonitorBase(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
}
func (u *MonitorRepo) DelMonitorIO(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
}
func (u *MonitorRepo) DelMonitorNet(timeForDelete time.Time) error {
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
}
-80
View File
@@ -1,80 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/global"
)
type ScriptRepo struct{}
type IScriptRepo interface {
Get(opts ...DBOption) (model.ScriptLibrary, error)
List(opts ...DBOption) ([]model.ScriptLibrary, error)
SyncAll(data []model.ScriptLibrary) error
}
func NewIScriptRepo() IScriptRepo {
return &ScriptRepo{}
}
func (u *ScriptRepo) Get(opts ...DBOption) (model.ScriptLibrary, error) {
var script model.ScriptLibrary
db := global.DB
if global.IsMaster {
db = global.CoreDB
}
for _, opt := range opts {
db = opt(db)
}
err := db.First(&script).Error
return script, err
}
func (u *ScriptRepo) List(opts ...DBOption) ([]model.ScriptLibrary, error) {
var ops []model.ScriptLibrary
if global.IsMaster {
db := global.CoreDB.Model(&model.ScriptLibrary{})
for _, opt := range opts {
db = opt(db)
}
err := db.Where("is_interactive = ?", false).Find(&ops).Error
return ops, err
}
db := global.DB.Model(&model.ScriptLibrary{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&ops).Error
return ops, err
}
func (u *ScriptRepo) SyncAll(data []model.ScriptLibrary) error {
tx := global.DB.Begin()
var oldScripts []model.ScriptLibrary
_ = tx.Where("1 = 1").Find(&oldScripts).Error
oldScriptMap := make(map[string]uint)
for _, item := range oldScripts {
oldScriptMap[item.Name] = item.ID
}
for _, item := range data {
if val, ok := oldScriptMap[item.Name]; ok {
item.ID = val
delete(oldScriptMap, item.Name)
} else {
item.ID = 0
}
if err := tx.Model(model.ScriptLibrary{}).Where("id = ?", item.ID).Save(&item).Error; err != nil {
tx.Rollback()
return err
}
}
for _, val := range oldScriptMap {
if err := tx.Where("id = ?", val).Delete(&model.ScriptLibrary{}).Error; err != nil {
tx.Rollback()
return err
}
}
tx.Commit()
return nil
}
-125
View File
@@ -1,125 +0,0 @@
package repo
import (
"context"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/app/model"
"gorm.io/gorm"
)
type TaskRepo struct {
}
type ITaskRepo interface {
Save(ctx context.Context, task *model.Task) error
GetFirst(opts ...DBOption) (model.Task, error)
Page(page, size int, opts ...DBOption) (int64, []model.Task, error)
Update(ctx context.Context, task *model.Task) error
UpdateRunningTaskToFailed() error
CountExecutingTask() (int64, error)
Delete(opts ...DBOption) error
DeleteAll() error
WithByID(id string) DBOption
WithResourceID(id uint) DBOption
WithOperate(taskOperate string) DBOption
WithByStatus(status string) DBOption
}
func NewITaskRepo() ITaskRepo {
return &TaskRepo{}
}
func getTaskDb(opts ...DBOption) *gorm.DB {
db := global.TaskDB
for _, opt := range opts {
db = opt(db)
}
return db
}
func getTaskTx(ctx context.Context, opts ...DBOption) *gorm.DB {
tx, ok := ctx.Value(constant.DB).(*gorm.DB)
if ok {
for _, opt := range opts {
tx = opt(tx)
}
return tx
}
return getTaskDb(opts...)
}
func (t TaskRepo) WithByID(id string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("id = ?", id)
}
}
func (t TaskRepo) WithOperate(taskOperate string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("operate = ?", taskOperate)
}
}
func (t TaskRepo) WithResourceID(id uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("resource_id = ?", id)
}
}
func (t TaskRepo) WithByStatus(status string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("status = ?", status)
}
}
func (t TaskRepo) Save(ctx context.Context, task *model.Task) error {
return getTaskTx(ctx).Save(&task).Error
}
func (t TaskRepo) GetFirst(opts ...DBOption) (model.Task, error) {
var task model.Task
db := getTaskDb(opts...).Model(&model.Task{})
if err := db.First(&task).Error; err != nil {
return task, err
}
return task, nil
}
func (t TaskRepo) Page(page, size int, opts ...DBOption) (int64, []model.Task, error) {
var tasks []model.Task
db := getTaskDb(opts...).Model(&model.Task{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&tasks).Error
return count, tasks, err
}
func (t TaskRepo) Update(ctx context.Context, task *model.Task) error {
return getTaskTx(ctx).Save(&task).Error
}
func (t TaskRepo) UpdateRunningTaskToFailed() error {
return getTaskDb(t.WithByStatus(constant.StatusExecuting)).Model(&model.Task{}).Updates(map[string]interface{}{"status": constant.StatusFailed, "error_msg": "1Panel restart causes failure"}).Error
}
func (t TaskRepo) CountExecutingTask() (int64, error) {
var count int64
err := getTaskDb(t.WithByStatus(constant.StatusExecuting)).Model(&model.Task{}).Count(&count).Error
return count, err
}
func (t TaskRepo) Delete(opts ...DBOption) error {
db := global.TaskDB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.Task{}).Error
}
func (t TaskRepo) DeleteAll() error {
return global.TaskDB.Where("1 = 1").Delete(&model.Task{}).Error
}
-398
View File
@@ -1,398 +0,0 @@
package service
import (
"context"
"fmt"
"os"
"path"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"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/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/jinzhu/copier"
)
type AIToolService struct{}
type IAIToolService interface {
Search(search dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error)
Create(req dto.OllamaModelName) error
Close(name string) error
Recreate(req dto.OllamaModelName) error
Delete(req dto.ForceDelete) error
Sync() ([]dto.OllamaModelDropList, error)
LoadDetail(name string) (string, error)
BindDomain(req dto.OllamaBindDomain) error
GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error)
UpdateBindDomain(req dto.OllamaBindDomain) error
}
func NewIAIToolService() IAIToolService {
return &AIToolService{}
}
func (u *AIToolService) Search(req dto.SearchWithPage) (int64, []dto.OllamaModelInfo, error) {
var options []repo.DBOption
if len(req.Info) != 0 {
options = append(options, repo.WithByLikeName(req.Info))
}
total, list, err := aiRepo.Page(req.Page, req.PageSize, options...)
if err != nil {
return 0, nil, err
}
var dtoLists []dto.OllamaModelInfo
for _, itemModel := range list {
var item dto.OllamaModelInfo
if err := copier.Copy(&item, &itemModel); err != nil {
return 0, nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
taskModel, _ := taskRepo.GetFirst(taskRepo.WithResourceID(item.ID), repo.WithByType(task.TaskScopeAI))
if len(taskModel.ID) != 0 {
item.LogFileExist = true
}
dtoLists = append(dtoLists, item)
}
return total, dtoLists, err
}
func (u *AIToolService) LoadDetail(name string) (string, error) {
if cmd.CheckIllegal(name) {
return "", buserr.New("ErrCmdIllegal")
}
containerName, err := LoadContainerName()
if err != nil {
return "", err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama show %s", containerName, name)
if err != nil {
return "", err
}
return stdout, err
}
func (u *AIToolService) Create(req dto.OllamaModelName) error {
if cmd.CheckIllegal(req.Name) {
return buserr.New("ErrCmdIllegal")
}
modelInfo, _ := aiRepo.Get(repo.WithByName(req.Name))
if modelInfo.ID != 0 {
return buserr.New("ErrRecordExist")
}
containerName, err := LoadContainerName()
if err != nil {
return err
}
info := model.OllamaModel{
Name: req.Name,
From: "local",
Status: constant.StatusWaiting,
}
if err := aiRepo.Create(&info); err != nil {
return err
}
taskItem, err := task.NewTaskWithOps(fmt.Sprintf("ollama-model-%s", req.Name), task.TaskPull, task.TaskScopeAI, req.TaskID, info.ID)
if err != nil {
global.LOG.Errorf("new task for exec shell failed, err: %v", err)
return err
}
go func() {
taskItem.AddSubTask(i18n.GetWithName("OllamaModelPull", req.Name), func(t *task.Task) error {
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithTimeout(time.Hour))
return cmdMgr.Run("docker", "exec", containerName, "ollama", "pull", info.Name)
}, nil)
taskItem.AddSubTask(i18n.GetWithName("OllamaModelSize", req.Name), func(t *task.Task) error {
itemSize, err := loadModelSize(info.Name, containerName)
if len(itemSize) != 0 {
_ = aiRepo.Update(info.ID, map[string]interface{}{"status": constant.StatusSuccess, "size": itemSize})
} else {
_ = aiRepo.Update(info.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
}
return nil
}, nil)
if err := taskItem.Execute(); err != nil {
_ = aiRepo.Update(info.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
}
}()
return nil
}
func (u *AIToolService) Close(name string) error {
if cmd.CheckIllegal(name) {
return buserr.New("ErrCmdIllegal")
}
containerName, err := LoadContainerName()
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)
}
return nil
}
func (u *AIToolService) Recreate(req dto.OllamaModelName) error {
if cmd.CheckIllegal(req.Name) {
return buserr.New("ErrCmdIllegal")
}
modelInfo, _ := aiRepo.Get(repo.WithByName(req.Name))
if modelInfo.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
containerName, err := LoadContainerName()
if err != nil {
return err
}
if err := aiRepo.Update(modelInfo.ID, map[string]interface{}{"status": constant.StatusWaiting, "from": "local"}); err != nil {
return err
}
taskItem, err := task.NewTaskWithOps(fmt.Sprintf("ollama-model-%s", req.Name), task.TaskPull, task.TaskScopeAI, req.TaskID, modelInfo.ID)
if err != nil {
global.LOG.Errorf("new task for exec shell failed, err: %v", err)
return err
}
go func() {
taskItem.AddSubTask(i18n.GetWithName("OllamaModelPull", req.Name), func(t *task.Task) error {
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem), cmd.WithTimeout(time.Hour))
return cmdMgr.Run("docker", "exec", containerName, "ollama", "pull", req.Name)
}, nil)
taskItem.AddSubTask(i18n.GetWithName("OllamaModelSize", req.Name), func(t *task.Task) error {
itemSize, err := loadModelSize(modelInfo.Name, containerName)
if len(itemSize) != 0 {
_ = aiRepo.Update(modelInfo.ID, map[string]interface{}{"status": constant.StatusSuccess, "size": itemSize})
} else {
_ = aiRepo.Update(modelInfo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
}
return nil
}, nil)
if err := taskItem.Execute(); err != nil {
_ = aiRepo.Update(modelInfo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
}
}()
return nil
}
func (u *AIToolService) Delete(req dto.ForceDelete) error {
ollamaList, _ := aiRepo.List(repo.WithByIDs(req.IDs))
if len(ollamaList) == 0 {
return buserr.New("ErrRecordNotFound")
}
containerName, err := LoadContainerName()
if err != nil && !req.ForceDelete {
return err
}
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)
}
}
_ = aiRepo.Delete(repo.WithByID(item.ID))
logItem := path.Join(global.Dir.DataDir, "log", "AITools", item.Name)
_ = os.Remove(logItem)
}
return nil
}
func (u *AIToolService) Sync() ([]dto.OllamaModelDropList, error) {
containerName, err := LoadContainerName()
if err != nil {
return nil, err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama list", containerName)
if err != nil {
return nil, err
}
var list []model.OllamaModel
lines := strings.Split(stdout, "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 5 {
continue
}
if parts[0] == "NAME" {
continue
}
list = append(list, model.OllamaModel{Name: parts[0], Size: parts[2] + " " + parts[3]})
}
listInDB, _ := aiRepo.List()
var dropList []dto.OllamaModelDropList
for _, itemModel := range listInDB {
isExit := false
for i := 0; i < len(list); i++ {
if list[i].Name == itemModel.Name {
_ = aiRepo.Update(itemModel.ID, map[string]interface{}{"status": constant.StatusSuccess, "message": "", "size": list[i].Size})
list = append(list[:i], list[(i+1):]...)
isExit = true
break
}
}
if !isExit && itemModel.Status != constant.StatusWaiting {
_ = aiRepo.Update(itemModel.ID, map[string]interface{}{"status": constant.StatusDeleted, "message": "not exist", "size": ""})
dropList = append(dropList, dto.OllamaModelDropList{ID: itemModel.ID, Name: itemModel.Name})
continue
}
}
for _, item := range list {
item.Status = constant.StatusSuccess
item.From = "remote"
_ = aiRepo.Create(&item)
}
return dropList, nil
}
func (u *AIToolService) BindDomain(req dto.OllamaBindDomain) error {
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
if nginxInstall.ID == 0 {
return buserr.New("ErrOpenrestyInstall")
}
var (
ipList []string
err error
)
if len(req.IPList) > 0 {
ipList, err = common.HandleIPList(req.IPList)
if err != nil {
return err
}
}
createWebsiteReq := request.WebsiteCreate{
Domains: []request.WebsiteDomain{{Domain: req.Domain, Port: 80}},
Alias: strings.ToLower(req.Domain),
Type: constant.Deployment,
AppType: constant.InstalledApp,
AppInstallID: req.AppInstallID,
}
if req.SSLID > 0 {
createWebsiteReq.WebsiteSSLID = req.SSLID
createWebsiteReq.EnableSSL = true
}
res, _ := NewIGroupService().GetDefault()
createWebsiteReq.WebsiteGroupID = res.ID
websiteService := NewIWebsiteService()
if err = websiteService.CreateWebsite(createWebsiteReq); err != nil {
return err
}
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(strings.ToLower(req.Domain)))
if err != nil {
return err
}
if len(ipList) > 0 {
if err = ConfigAllowIPs(ipList, website); err != nil {
return err
}
}
if err = ConfigAIProxy(website); err != nil {
return err
}
return nil
}
func (u *AIToolService) GetBindDomain(req dto.OllamaBindDomainReq) (*dto.OllamaBindDomainRes, error) {
install, err := appInstallRepo.GetFirst(repo.WithByID(req.AppInstallID))
if err != nil {
return nil, err
}
res := &dto.OllamaBindDomainRes{}
website, _ := websiteRepo.GetFirst(websiteRepo.WithAppInstallId(install.ID))
if website.ID == 0 {
return res, nil
}
res.WebsiteID = website.ID
res.Domain = website.PrimaryDomain
if website.WebsiteSSLID > 0 {
res.SSLID = website.WebsiteSSLID
ssl, _ := websiteSSLRepo.GetFirst(repo.WithByID(website.WebsiteSSLID))
res.AcmeAccountID = ssl.AcmeAccountID
}
res.ConnUrl = fmt.Sprintf("%s://%s", strings.ToLower(website.Protocol), website.PrimaryDomain)
res.AllowIPs = GetAllowIps(website)
return res, nil
}
func (u *AIToolService) UpdateBindDomain(req dto.OllamaBindDomain) error {
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
if nginxInstall.ID == 0 {
return buserr.New("ErrOpenrestyInstall")
}
var (
ipList []string
err error
)
if len(req.IPList) > 0 {
ipList, err = common.HandleIPList(req.IPList)
if err != nil {
return err
}
}
websiteService := NewIWebsiteService()
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
if err != nil {
return err
}
if err = ConfigAllowIPs(ipList, website); err != nil {
return err
}
if req.SSLID > 0 {
sslReq := request.WebsiteHTTPSOp{
WebsiteID: website.ID,
Enable: true,
Type: constant.SSLExisted,
WebsiteSSLID: req.SSLID,
HttpConfig: constant.HTTPToHTTPS,
}
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
return err
}
return nil
}
if website.WebsiteSSLID > 0 && req.SSLID == 0 {
sslReq := request.WebsiteHTTPSOp{
WebsiteID: website.ID,
Enable: false,
}
if _, err = websiteService.OpWebsiteHTTPS(context.Background(), sslReq); err != nil {
return err
}
}
return nil
}
func LoadContainerName() (string, error) {
ollamaBaseInfo, err := appInstallRepo.LoadBaseInfo("ollama", "")
if err != nil {
return "", fmt.Errorf("ollama service is not found, err: %v", err)
}
if ollamaBaseInfo.Status != constant.StatusRunning {
return "", fmt.Errorf("container %s of ollama is not running, please check and retry!", ollamaBaseInfo.ContainerName)
}
return ollamaBaseInfo.ContainerName, nil
}
func loadModelSize(name string, containerName string) (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s ollama list | grep %s", containerName, name)
if err != nil {
return "", err
}
lines := strings.Split(stdout, "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) < 5 {
continue
}
return parts[2] + " " + parts[3], nil
}
return "", fmt.Errorf("no such model %s in ollama list, std: %s", name, stdout)
}
File diff suppressed because it is too large Load Diff
-76
View File
@@ -1,76 +0,0 @@
package service
import (
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type AppIgnoreUpgradeService struct {
}
type IAppIgnoreUpgradeService interface {
List() ([]response.AppIgnoreUpgradeDTO, error)
CreateAppIgnore(req request.AppIgnoreUpgradeReq) error
Delete(req request.ReqWithID) error
}
func NewIAppIgnoreUpgradeService() IAppIgnoreUpgradeService {
return AppIgnoreUpgradeService{}
}
func (a AppIgnoreUpgradeService) List() ([]response.AppIgnoreUpgradeDTO, error) {
var res []response.AppIgnoreUpgradeDTO
ignores, err := appIgnoreUpgradeRepo.List()
if err != nil {
return nil, err
}
for _, ignore := range ignores {
dto := response.AppIgnoreUpgradeDTO{
ID: ignore.ID,
AppID: ignore.AppID,
AppDetailID: ignore.AppDetailID,
Scope: ignore.Scope,
}
app, err := appRepo.GetFirst(repo.WithByID(ignore.AppID))
if errors.Is(err, gorm.ErrRecordNotFound) {
_ = appIgnoreUpgradeRepo.Delete(repo.WithByID(ignore.ID))
continue
}
dto.Icon = app.Icon
if ignore.Scope == "version" {
appDetail, err := appDetailRepo.GetFirst(repo.WithByID(ignore.AppDetailID))
if errors.Is(err, gorm.ErrRecordNotFound) {
_ = appIgnoreUpgradeRepo.Delete(repo.WithByID(ignore.ID))
continue
}
dto.Version = appDetail.Version
}
res = append(res, dto)
}
return res, nil
}
func (a AppIgnoreUpgradeService) CreateAppIgnore(req request.AppIgnoreUpgradeReq) error {
appIgnoreUpgrade := model.AppIgnoreUpgrade{
AppID: req.AppID,
Scope: req.Scope,
}
if req.Scope == "version" {
appIgnoreUpgrade.AppDetailID = req.AppDetailID
}
if req.Scope == "all" {
_ = appIgnoreUpgradeRepo.Delete(appInstallRepo.WithAppId(req.AppID))
}
if err := appIgnoreUpgradeRepo.Create(&appIgnoreUpgrade); err != nil {
return err
}
return nil
}
func (a AppIgnoreUpgradeService) Delete(req request.ReqWithID) error {
return appIgnoreUpgradeRepo.Delete(repo.WithByID(req.ID))
}
File diff suppressed because it is too large Load Diff
-553
View File
@@ -1,553 +0,0 @@
package service
import (
"bufio"
"encoding/base64"
"encoding/json"
"fmt"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage"
"github.com/1Panel-dev/1Panel/agent/utils/cloud_storage/client"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/jinzhu/copier"
)
type BackupService struct{}
type IBackupService interface {
CheckUsed(name string, isPublic bool) error
LoadBackupOptions() ([]dto.BackupOption, error)
SearchWithPage(search dto.SearchPageWithType) (int64, interface{}, error)
Create(backupDto dto.BackupOperate) error
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
Update(req dto.BackupOperate) error
Delete(id uint) error
RefreshToken(req dto.OperateByID) error
GetLocalDir() (string, error)
MysqlBackup(db dto.CommonBackup) error
PostgresqlBackup(db dto.CommonBackup) error
MysqlRecover(db dto.CommonRecover) error
PostgresqlRecover(db dto.CommonRecover) error
MysqlRecoverByUpload(req dto.CommonRecover) error
PostgresqlRecoverByUpload(req dto.CommonRecover) error
RedisBackup(db dto.CommonBackup) error
RedisRecover(db dto.CommonRecover) error
WebsiteBackup(db dto.CommonBackup) error
WebsiteRecover(req dto.CommonRecover) error
AppBackup(db dto.CommonBackup) (*model.BackupRecord, error)
AppRecover(req dto.CommonRecover) error
}
func NewIBackupService() IBackupService {
return &BackupService{}
}
func (u *BackupService) GetLocalDir() (string, error) {
account, err := backupRepo.Get(repo.WithByType(constant.Local))
if err != nil {
return "", err
}
return account.BackupPath, nil
}
func (u *BackupService) SearchWithPage(req dto.SearchPageWithType) (int64, interface{}, error) {
options := []repo.DBOption{repo.WithOrderBy("created_at desc")}
if len(req.Type) != 0 {
options = append(options, repo.WithByType(req.Type))
}
if len(req.Info) != 0 {
options = append(options, repo.WithByType(req.Info))
}
count, accounts, err := backupRepo.Page(req.Page, req.PageSize, options...)
if err != nil {
return 0, nil, err
}
var data []dto.BackupInfo
for _, account := range accounts {
var item dto.BackupInfo
if err := copier.Copy(&item, &account); err != nil {
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
}
if item.Type != constant.Sftp && item.Type != constant.Local {
item.BackupPath = path.Join("/", strings.TrimPrefix(item.BackupPath, "/"))
}
if !item.RememberAuth {
item.AccessKey = ""
item.Credential = ""
if account.Type == constant.Sftp {
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(item.Vars), &varMap); err != nil {
continue
}
delete(varMap, "passPhrase")
itemVars, _ := json.Marshal(varMap)
item.Vars = string(itemVars)
}
} else {
item.AccessKey, _ = encrypt.StringDecryptWithBase64(item.AccessKey)
item.Credential, _ = encrypt.StringDecryptWithBase64(item.Credential)
}
if account.Type == constant.OneDrive || account.Type == constant.ALIYUN || account.Type == constant.GoogleDrive {
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(item.Vars), &varMap); err != nil {
continue
}
delete(varMap, "refresh_token")
delete(varMap, "drive_id")
itemVars, _ := json.Marshal(varMap)
item.Vars = string(itemVars)
}
data = append(data, item)
}
return count, data, nil
}
func (u *BackupService) Create(req dto.BackupOperate) error {
if req.Type == constant.Local {
return buserr.New("ErrBackupLocalCreate")
}
if req.Type != constant.Sftp {
req.BackupPath = strings.TrimPrefix(req.BackupPath, "/")
}
backup, _ := backupRepo.Get(repo.WithByName(req.Name))
if backup.ID != 0 {
return buserr.New("ErrRecordExist")
}
if err := copier.Copy(&backup, &req); err != nil {
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
itemAccessKey, err := base64.StdEncoding.DecodeString(backup.AccessKey)
if err != nil {
return err
}
backup.AccessKey = string(itemAccessKey)
itemCredential, err := base64.StdEncoding.DecodeString(backup.Credential)
if err != nil {
return err
}
backup.Credential = string(itemCredential)
if req.Type == constant.OneDrive || req.Type == constant.GoogleDrive || req.Type == constant.ALIYUN {
if err := loadRefreshTokenByCode(&backup); err != nil {
return err
}
}
if req.Type != constant.Local {
isOk, err := u.checkBackupConn(&backup)
if err != nil || !isOk {
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
}
}
backup.AccessKey, err = encrypt.StringEncrypt(backup.AccessKey)
if err != nil {
return err
}
backup.Credential, err = encrypt.StringEncrypt(backup.Credential)
if err != nil {
return err
}
if err := backupRepo.Create(&backup); err != nil {
return err
}
return nil
}
func (u *BackupService) GetBuckets(req dto.ForBuckets) ([]interface{}, error) {
itemAccessKey, err := base64.StdEncoding.DecodeString(req.AccessKey)
if err != nil {
return nil, err
}
req.AccessKey = string(itemAccessKey)
itemCredential, err := base64.StdEncoding.DecodeString(req.Credential)
if err != nil {
return nil, err
}
req.Credential = string(itemCredential)
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(req.Vars), &varMap); err != nil {
return nil, err
}
switch req.Type {
case constant.Sftp, constant.WebDAV:
varMap["username"] = req.AccessKey
varMap["password"] = req.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
varMap["accessKey"] = req.AccessKey
varMap["secretKey"] = req.Credential
}
client, err := cloud_storage.NewCloudStorageClient(req.Type, varMap)
if err != nil {
return nil, err
}
return client.ListBuckets()
}
func (u *BackupService) Delete(id uint) error {
backup, _ := backupRepo.Get(repo.WithByID(id))
if backup.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
if backup.Type == constant.Local {
return buserr.New("ErrBackupLocalDelete")
}
if err := u.CheckUsed(backup.Name, false); err != nil {
return err
}
return backupRepo.Delete(repo.WithByID(id))
}
func (u *BackupService) Update(req dto.BackupOperate) error {
backup, _ := backupRepo.Get(repo.WithByID(req.ID))
if backup.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
if req.Type != constant.Sftp && req.Type != constant.Local && req.BackupPath != "/" {
req.BackupPath = strings.TrimPrefix(req.BackupPath, "/")
}
var newBackup model.BackupAccount
if err := copier.Copy(&newBackup, &req); err != nil {
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
itemAccessKey, err := base64.StdEncoding.DecodeString(newBackup.AccessKey)
if err != nil {
return err
}
newBackup.AccessKey = string(itemAccessKey)
itemCredential, err := base64.StdEncoding.DecodeString(newBackup.Credential)
if err != nil {
return err
}
newBackup.Credential = string(itemCredential)
if backup.Type == constant.Local {
if err := changeLocalBackup(backup.BackupPath, newBackup.BackupPath); err != nil {
return err
}
global.Dir.LocalBackupDir = newBackup.BackupPath
}
if newBackup.Type == constant.OneDrive || newBackup.Type == constant.GoogleDrive || newBackup.Type == constant.ALIYUN {
if err := loadRefreshTokenByCode(&newBackup); err != nil {
return err
}
}
if backup.Type != constant.Local {
isOk, err := u.checkBackupConn(&newBackup)
if err != nil || !isOk {
return buserr.WithMap("ErrBackupCheck", map[string]interface{}{"err": err.Error()}, err)
}
newBackup.AccessKey, err = encrypt.StringEncrypt(newBackup.AccessKey)
if err != nil {
return err
}
newBackup.Credential, err = encrypt.StringEncrypt(newBackup.Credential)
if err != nil {
return err
}
}
newBackup.ID = backup.ID
newBackup.CreatedAt = backup.CreatedAt
newBackup.UpdatedAt = backup.UpdatedAt
if err := backupRepo.Save(&newBackup); err != nil {
return err
}
return nil
}
func (u *BackupService) RefreshToken(req dto.OperateByID) error {
backup, _ := backupRepo.Get(repo.WithByID(req.ID))
if backup.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
return fmt.Errorf("failed to refresh %s - %s token, please retry, err: %v", backup.Type, backup.Name, err)
}
var (
refreshToken string
err error
)
switch backup.Type {
case constant.OneDrive:
refreshToken, err = client.RefreshToken("refresh_token", "refreshToken", varMap)
case constant.GoogleDrive:
refreshToken, err = client.RefreshGoogleToken("refresh_token", "refreshToken", varMap)
case constant.ALIYUN:
refreshToken, err = client.RefreshALIToken(varMap)
}
if err != nil {
varMap["refresh_status"] = constant.StatusFailed
varMap["refresh_msg"] = err.Error()
return fmt.Errorf("failed to refresh %s-%s token, please retry, err: %v", backup.Type, backup.Name, err)
}
varMap["refresh_status"] = constant.StatusSuccess
varMap["refresh_time"] = time.Now().Format(constant.DateTimeLayout)
varMap["refresh_token"] = refreshToken
varsItem, _ := json.Marshal(varMap)
backup.Vars = string(varsItem)
return backupRepo.Save(&backup)
}
func (u *BackupService) checkBackupConn(backup *model.BackupAccount) (bool, error) {
client, err := newClient(backup, false)
if err != nil {
return false, err
}
fileItem := path.Join(global.Dir.BaseDir, "1panel/tmp/test/1panel")
if _, err := os.Stat(path.Dir(fileItem)); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(fileItem), os.ModePerm); err != nil {
return false, err
}
}
file, err := os.OpenFile(fileItem, os.O_WRONLY|os.O_CREATE, constant.FilePerm)
if err != nil {
return false, err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString("1Panel 备份账号测试文件。\n")
_, _ = write.WriteString("1Panel 備份賬號測試文件。\n")
_, _ = write.WriteString("1Panel Backs up account test files.\n")
_, _ = write.WriteString("1Panelアカウントのテストファイルをバックアップします。\n")
write.Flush()
targetPath := path.Join(backup.BackupPath, "test/1panel")
if backup.Type != constant.Sftp && backup.Type != constant.Local && targetPath != "/" {
targetPath = strings.TrimPrefix(targetPath, "/")
}
if _, err := client.Upload(fileItem, targetPath); err != nil {
return false, err
}
_, _ = client.Delete(path.Join(backup.BackupPath, "test/1panel"))
return true, nil
}
func (u *BackupService) LoadBackupOptions() ([]dto.BackupOption, error) {
accounts, err := backupRepo.List(repo.WithOrderBy("created_at desc"))
if err != nil {
return nil, err
}
var data []dto.BackupOption
for _, account := range accounts {
var item dto.BackupOption
if err := copier.Copy(&item, &account); err != nil {
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
}
data = append(data, item)
}
return data, nil
}
func (u *BackupService) CheckUsed(name string, isPublic bool) error {
account, _ := backupRepo.Get(repo.WithByName(name), backupRepo.WithByPublic(isPublic))
if account.ID == 0 {
return nil
}
cronjobs, _ := cronjobRepo.List()
for _, job := range cronjobs {
if job.DownloadAccountID == account.ID {
return buserr.New("ErrBackupInUsed")
}
ids := strings.Split(job.SourceAccountIDs, ",")
for _, idItem := range ids {
if idItem == fmt.Sprintf("%v", account.ID) {
return buserr.New("ErrBackupInUsed")
}
}
}
return nil
}
func NewBackupClientWithID(id uint) (*model.BackupAccount, cloud_storage.CloudStorageClient, error) {
account, _ := backupRepo.Get(repo.WithByID(id))
backClient, err := newClient(&account, true)
if err != nil {
return nil, nil, err
}
return &account, backClient, nil
}
type backupClientHelper struct {
id uint
accountType string
name string
backupPath string
client cloud_storage.CloudStorageClient
}
func NewBackupClientMap(ids []string) (map[string]backupClientHelper, error) {
var accounts []model.BackupAccount
var idItems []uint
for i := 0; i < len(ids); i++ {
item, _ := strconv.Atoi(ids[i])
idItems = append(idItems, uint(item))
}
accounts, _ = backupRepo.List(repo.WithByIDs(idItems))
clientMap := make(map[string]backupClientHelper)
for _, item := range accounts {
backClient, err := newClient(&item, true)
if err != nil {
return nil, err
}
clientMap[fmt.Sprintf("%v", item.ID)] = backupClientHelper{
client: backClient,
name: item.Name,
backupPath: item.BackupPath,
accountType: item.Type,
id: item.ID,
}
}
return clientMap, nil
}
func newClient(account *model.BackupAccount, isEncrypt bool) (cloud_storage.CloudStorageClient, error) {
varMap := make(map[string]interface{})
if len(account.Vars) != 0 {
if err := json.Unmarshal([]byte(account.Vars), &varMap); err != nil {
return nil, err
}
}
varMap["bucket"] = account.Bucket
varMap["backupPath"] = account.BackupPath
if isEncrypt {
account.AccessKey, _ = encrypt.StringDecrypt(account.AccessKey)
account.Credential, _ = encrypt.StringDecrypt(account.Credential)
}
switch account.Type {
case constant.Sftp, constant.WebDAV:
varMap["username"] = account.AccessKey
varMap["password"] = account.Credential
case constant.OSS, constant.S3, constant.MinIo, constant.Cos, constant.Kodo:
varMap["accessKey"] = account.AccessKey
varMap["secretKey"] = account.Credential
case constant.UPYUN:
varMap["operator"] = account.AccessKey
varMap["password"] = account.Credential
}
client, err := cloud_storage.NewCloudStorageClient(account.Type, varMap)
if err != nil {
return nil, err
}
return client, nil
}
func loadRefreshTokenByCode(backup *model.BackupAccount) error {
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
return fmt.Errorf("unmarshal backup vars failed, err: %v", err)
}
refreshToken := ""
var err error
if backup.Type == constant.GoogleDrive {
refreshToken, err = client.RefreshGoogleToken("authorization_code", "refreshToken", varMap)
if err != nil {
return err
}
}
if backup.Type == constant.OneDrive {
refreshToken, err = client.RefreshToken("authorization_code", "refreshToken", varMap)
if err != nil {
return err
}
}
if backup.Type != constant.ALIYUN {
delete(varMap, "code")
varMap["refresh_token"] = refreshToken
}
varMap["refresh_status"] = constant.StatusSuccess
varMap["refresh_time"] = time.Now().Format(constant.DateTimeLayout)
itemVars, err := json.Marshal(varMap)
if err != nil {
return fmt.Errorf("json marshal var map failed, err: %v", err)
}
backup.Vars = string(itemVars)
return nil
}
func loadBackupNamesByID(accountIDs string, downloadID uint) ([]string, string, error) {
accountIDList := strings.Split(accountIDs, ",")
var ids []uint
for _, item := range accountIDList {
if len(item) != 0 {
itemID, _ := strconv.Atoi(item)
ids = append(ids, uint(itemID))
}
}
list, err := backupRepo.List(repo.WithByIDs(ids))
if err != nil {
return nil, "", err
}
var accounts []string
var downloadAccount string
for _, item := range list {
accounts = append(accounts, item.Name)
if item.ID == downloadID {
downloadAccount = item.Name
}
}
return accounts, downloadAccount, nil
}
func changeLocalBackup(oldPath, newPath string) error {
fileOp := files.NewFileOp()
if fileOp.Stat(path.Join(oldPath, "app")) {
if err := fileOp.CopyDir(path.Join(oldPath, "app"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "database")) {
if err := fileOp.CopyDir(path.Join(oldPath, "database"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "directory")) {
if err := fileOp.CopyDir(path.Join(oldPath, "directory"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "system_snapshot")) {
if err := fileOp.CopyDir(path.Join(oldPath, "system_snapshot"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "website")) {
if err := fileOp.CopyDir(path.Join(oldPath, "website"), newPath); err != nil {
return err
}
}
if fileOp.Stat(path.Join(oldPath, "log")) {
if err := fileOp.CopyDir(path.Join(oldPath, "log"), newPath); err != nil {
return err
}
}
_ = fileOp.RmRf(path.Join(oldPath, "app"))
_ = fileOp.RmRf(path.Join(oldPath, "database"))
_ = fileOp.RmRf(path.Join(oldPath, "directory"))
_ = fileOp.RmRf(path.Join(oldPath, "system_snapshot"))
_ = fileOp.RmRf(path.Join(oldPath, "website"))
_ = fileOp.RmRf(path.Join(oldPath, "log"))
return nil
}
-401
View File
@@ -1,401 +0,0 @@
package service
import (
"context"
"encoding/json"
"fmt"
"io/fs"
"os"
"path"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"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) AppBackup(req dto.CommonBackup) (*model.BackupRecord, error) {
app, err := appRepo.GetFirst(appRepo.WithKey(req.Name))
if err != nil {
return nil, err
}
install, err := appInstallRepo.GetFirst(repo.WithByName(req.DetailName), appInstallRepo.WithAppId(app.ID))
if err != nil {
return nil, err
}
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName)
backupDir := path.Join(global.Dir.LocalBackupDir, itemDir)
fileName := req.FileName
if req.FileName == "" {
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
}
if req.TaskID != "" {
go func() {
_, _ = backupApp()
}()
} else {
return backupApp()
}
return nil, nil
}
func (u *BackupService) AppRecover(req dto.CommonRecover) error {
app, err := appRepo.GetFirst(appRepo.WithKey(req.Name))
if err != nil {
return err
}
install, err := appInstallRepo.GetFirst(repo.WithByName(req.DetailName), appInstallRepo.WithAppId(app.ID))
if err != nil {
return err
}
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return buserr.WithName("ErrFileNotFound", req.File)
}
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)
}
}()
return nil
}
func backupDatabaseWithTask(parentTask *task.Task, resourceKey, tmpDir, name string, databaseID uint) error {
switch resourceKey {
case constant.AppMysql, constant.AppMariaDB:
db, err := mysqlRepo.Get(repo.WithByID(databaseID))
if err != nil {
return err
}
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 {
return err
}
parentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
case constant.AppPostgresql:
db, err := postgresqlRepo.Get(repo.WithByID(databaseID))
if err != nil {
return err
}
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 {
return err
}
parentTask.LogSuccess(task.GetTaskName(db.Name, task.TaskBackup, task.TaskScopeDatabase))
}
return nil
}
func handleAppBackup(install *model.AppInstall, parentTask *task.Task, 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)
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()
}
func handleAppRecover(install *model.AppInstall, 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(install.Name, task.TaskRecover, task.TaskScopeApp, taskID, install.ID)
if err != nil {
return err
}
}
recoverApp := func(t *task.Task) error {
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 {
t.Log(fmt.Sprintf("backup app %s for rollback before recover failed, err: %v", install.Name, err))
}
}
if err := fileOp.TarGzExtractPro(recoverFile, path.Dir(recoverFile), secret); err != nil {
return err
}
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
defer func() {
_, _ = compose.Up(install.GetComposePath())
_ = os.RemoveAll(strings.ReplaceAll(recoverFile, ".tar.gz", ""))
}()
if !fileOp.Stat(tmpPath+"/app.json") || !fileOp.Stat(tmpPath+"/app.tar.gz") {
return errors.New(i18n.GetMsgByKey("AppBackupFileIncomplete"))
}
var oldInstall model.AppInstall
appJson, err := os.ReadFile(tmpPath + "/app.json")
if err != nil {
return err
}
if err := json.Unmarshal(appJson, &oldInstall); err != nil {
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
}
if oldInstall.App.Key != install.App.Key || oldInstall.Name != install.Name {
return errors.New(i18n.GetMsgByKey("AppAttributesNotMatch"))
}
newEnvFile := ""
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
for _, resource := range resources {
var database model.Database
switch resource.From {
case constant.AppResourceRemote:
database, err = databaseRepo.Get(repo.WithByID(resource.LinkId))
if err != nil {
return err
}
case constant.AppResourceLocal:
resourceApp, err := appInstallRepo.GetFirst(repo.WithByID(resource.LinkId))
if err != nil {
return err
}
database, err = databaseRepo.Get(databaseRepo.WithAppInstallID(resourceApp.ID), repo.WithByType(resource.Key), repo.WithByFrom(constant.AppResourceLocal), repo.WithByName(resourceApp.Name))
if err != nil {
return err
}
}
switch database.Type {
case constant.AppPostgresql:
db, err := postgresqlRepo.Get(repo.WithByID(resource.ResourceId))
if err != nil {
return err
}
taskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)
t.LogStart(taskName)
if err := handlePostgresqlRecover(dto.CommonRecover{
Name: database.Name,
DetailName: db.Name,
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
}, parentTask, true); err != nil {
t.LogFailedWithErr(taskName, err)
return err
}
t.LogSuccess(taskName)
case constant.AppMysql, constant.AppMariaDB:
db, err := mysqlRepo.Get(repo.WithByID(resource.ResourceId))
if err != nil {
return err
}
newDB, envMap, err := reCreateDB(db.ID, database, oldInstall.Env)
if err != nil {
return err
}
oldHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", envMap["PANEL_DB_HOST"].(string))
newHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", database.Address)
oldInstall.Env = strings.ReplaceAll(oldInstall.Env, oldHost, newHost)
envMap["PANEL_DB_HOST"] = database.Address
newEnvFile, err = coverEnvJsonToStr(oldInstall.Env)
if err != nil {
return err
}
_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{"resource_id": newDB.ID}, repo.WithByID(resource.ID))
taskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)
t.LogStart(taskName)
if err := handleMysqlRecover(dto.CommonRecover{
Name: newDB.MysqlName,
DetailName: newDB.Name,
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, install.Name),
}, parentTask, true, ""); err != nil {
t.LogFailedWithErr(taskName, err)
return err
}
t.LogSuccess(taskName)
}
}
appDir := install.GetPath()
backPath := fmt.Sprintf("%s_bak", appDir)
_ = fileOp.Rename(appDir, backPath)
_ = fileOp.CreateDir(appDir, constant.DirPerm)
deCompressName := i18n.GetWithName("DeCompressFile", "app.tar.gz")
t.LogStart(deCompressName)
if err := fileOp.TarGzExtractPro(tmpPath+"/app.tar.gz", install.GetAppPath(), ""); err != nil {
t.LogFailedWithErr(deCompressName, err)
_ = fileOp.DeleteDir(appDir)
_ = fileOp.Rename(backPath, appDir)
return err
}
t.LogSuccess(deCompressName)
_ = fileOp.DeleteDir(backPath)
if len(newEnvFile) != 0 {
envPath := fmt.Sprintf("%s/%s/.env", install.GetAppPath(), install.Name)
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
_, _ = file.WriteString(newEnvFile)
}
oldInstall.ID = install.ID
oldInstall.Status = constant.StatusRunning
oldInstall.AppId = install.AppId
oldInstall.AppDetailId = install.AppDetailId
oldInstall.App.ID = install.AppId
if err := appInstallRepo.Save(context.Background(), &oldInstall); err != nil {
global.LOG.Errorf("save db app install failed, err: %v", err)
return err
}
isOk = true
return nil
}
rollBackApp := func(t *task.Task) {
if isRollback {
return
}
if !isOk {
t.Log(i18n.GetMsgByKey("RecoverFailedStartRollBack"))
if err := handleAppRecover(install, 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(install.Name, task.TaskRecover, task.TaskScopeApp), recoverApp, rollBackApp)
if parentTask != nil {
return recoverApp(parentTask)
}
return recoverTask.Execute()
}
func doAppBackup(install *model.AppInstall, parentTask *task.Task, backupDir, fileName, excludes, secret string) error {
fileOp := files.NewFileOp()
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
if !fileOp.Stat(tmpDir) {
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
return fmt.Errorf("mkdir %s failed, err: %v", backupDir, err)
}
}
defer func() {
_ = os.RemoveAll(tmpDir)
}()
remarkInfo, _ := json.Marshal(install)
remarkInfoPath := fmt.Sprintf("%s/app.json", tmpDir)
if err := fileOp.SaveFile(remarkInfoPath, string(remarkInfo), fs.ModePerm); err != nil {
return err
}
appPath := install.GetPath()
if err := fileOp.TarGzCompressPro(true, appPath, path.Join(tmpDir, "app.tar.gz"), "", excludes); err != nil {
return err
}
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
for _, resource := range resources {
if err := backupDatabaseWithTask(parentTask, resource.Key, tmpDir, install.Name, resource.ResourceId); err != nil {
return err
}
}
parentTask.LogStart(i18n.GetMsgByKey("CompressDir"))
if err := fileOp.TarGzCompressPro(true, tmpDir, path.Join(backupDir, fileName), secret, ""); err != nil {
return err
}
parentTask.Log(i18n.GetWithName("CompressFileSuccess", fileName))
return nil
}
func reCreateDB(dbID uint, database model.Database, oldEnv string) (*model.DatabaseMysql, map[string]interface{}, error) {
mysqlService := NewIMysqlService()
ctx := context.Background()
_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, Database: database.Name, Type: database.Type, DeleteBackup: false, ForceDelete: true})
envMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(oldEnv), &envMap); err != nil {
return nil, envMap, err
}
oldName, _ := envMap["PANEL_DB_NAME"].(string)
oldUser, _ := envMap["PANEL_DB_USER"].(string)
oldPassword, _ := envMap["PANEL_DB_USER_PASSWORD"].(string)
createDB, err := mysqlService.Create(context.Background(), dto.MysqlDBCreate{
Name: oldName,
From: database.From,
Database: database.Name,
Format: "utf8mb4",
Username: oldUser,
Password: oldPassword,
Permission: "%",
})
cronjobs, _ := cronjobRepo.List(cronjobRepo.WithByDbName(fmt.Sprintf("%v", dbID)))
for _, job := range cronjobs {
_ = cronjobRepo.Update(job.ID, map[string]interface{}{"db_name": fmt.Sprintf("%v", createDB.ID)})
}
if err != nil {
return nil, envMap, err
}
return createDB, envMap, nil
}
-245
View File
@@ -1,245 +0,0 @@
package service
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"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/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/mysql/client"
)
func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
itemDir := fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName)
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,
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
}
func (u *BackupService) MysqlRecover(req dto.CommonRecover) error {
if err := handleMysqlRecover(req, nil, false, req.TaskID); err != nil {
return err
}
return nil
}
func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
file := req.File
fileName := path.Base(req.File)
if strings.HasSuffix(fileName, ".tar.gz") {
fileNameItem := time.Now().Format(constant.DateTimeSlimLayout)
dstDir := fmt.Sprintf("%s/%s", path.Dir(req.File), fileNameItem)
fileOp := files.NewFileOp()
if !fileOp.Stat(dstDir) {
if err := fileOp.CreateDir(dstDir, os.ModePerm); err != nil {
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
}
}
if err := fileOp.TarGzExtractPro(req.File, dstDir, ""); err != nil {
_ = os.RemoveAll(dstDir)
return err
}
global.LOG.Infof("decompress file %s successful, now start to check test.sql is exist", req.File)
hasTestSql := false
_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if !info.IsDir() && info.Name() == "test.sql" {
hasTestSql = true
file = path
fileName = "test.sql"
}
return nil
})
if !hasTestSql {
_ = os.RemoveAll(dstDir)
return fmt.Errorf("no such file named test.sql in %s", fileName)
}
defer func() {
_ = os.RemoveAll(dstDir)
}()
}
req.File = path.Dir(file) + "/" + fileName
if err := handleMysqlRecover(req, nil, false, req.TaskID); 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 {
var (
err error
backupTask *task.Task
)
backupTask = parentTask
dbInfo, err := mysqlRepo.Get(repo.WithByName(db.Name), mysqlRepo.WithByMysqlName(db.Database))
if err != nil {
return err
}
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)
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)
if parentTask != nil {
return itemHandler()
}
return backupTask.Execute()
}
func handleMysqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool, taskID string) error {
var (
err error
itemTask *task.Task
)
itemTask = parentTask
dbInfo, err := mysqlRepo.Get(repo.WithByName(req.DetailName), mysqlRepo.WithByMysqlName(req.Name))
if err != nil {
return err
}
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)
if err != nil {
return err
}
}
recoverDatabase := func(t *task.Task) error {
isOk := false
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return buserr.WithName("ErrFileNotFound", req.File)
}
dbInfo, err := mysqlRepo.Get(repo.WithByName(req.DetailName), mysqlRepo.WithByMysqlName(req.Name))
if err != nil {
return err
}
cli, version, err := LoadMysqlClientByFrom(req.Name)
if err != nil {
return err
}
if !isRollback {
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format(constant.DateTimeSlimLayout)))
if err := cli.Backup(client.BackupInfo{
Name: req.DetailName,
Type: req.Type,
Version: version,
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)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
Type: req.Type,
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 {
global.LOG.Infof("rollback mysql db %s from %s successful", req.DetailName, rollbackFile)
}
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
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
}
isOk = true
return nil
}
itemTask.AddSubTask(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil)
if parentTask != nil {
return recoverDatabase(parentTask)
}
return itemTask.Execute()
}
func doMysqlBackup(db DatabaseHelper, targetDir, fileName string) error {
dbInfo, err := mysqlRepo.Get(repo.WithByName(db.Name), mysqlRepo.WithByMysqlName(db.Database))
if err != nil {
return err
}
cli, version, err := LoadMysqlClientByFrom(db.Database)
if err != nil {
return err
}
backupInfo := client.BackupInfo{
Name: db.Name,
Type: db.DBType,
Version: version,
Format: dbInfo.Format,
TargetDir: targetDir,
FileName: fileName,
Timeout: 300,
}
return cli.Backup(backupInfo)
}
-223
View File
@@ -1,223 +0,0 @@
package service
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"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/task"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/postgresql/client"
)
func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
itemDir := fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName)
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,
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
}
func (u *BackupService) PostgresqlRecover(req dto.CommonRecover) error {
if err := handlePostgresqlRecover(req, nil, false); err != nil {
return err
}
return nil
}
func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
file := req.File
fileName := path.Base(req.File)
if strings.HasSuffix(fileName, ".tar.gz") {
fileNameItem := time.Now().Format(constant.DateTimeSlimLayout)
dstDir := fmt.Sprintf("%s/%s", path.Dir(req.File), fileNameItem)
fileOp := files.NewFileOp()
if !fileOp.Stat(dstDir) {
if err := fileOp.CreateDir(dstDir, os.ModePerm); err != nil {
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
}
}
if err := fileOp.TarGzExtractPro(req.File, dstDir, ""); err != nil {
_ = os.RemoveAll(dstDir)
return err
}
global.LOG.Infof("decompress file %s successful, now start to check test.sql is exist", req.File)
hasTestSql := false
_ = filepath.Walk(dstDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if !info.IsDir() && info.Name() == "test.sql" {
hasTestSql = true
file = path
fileName = "test.sql"
}
return nil
})
if !hasTestSql {
_ = os.RemoveAll(dstDir)
return fmt.Errorf("no such file named test.sql in %s", fileName)
}
defer func() {
_ = os.RemoveAll(dstDir)
}()
}
req.File = path.Dir(file) + "/" + fileName
if err := handlePostgresqlRecover(req, nil, false); err != nil {
return err
}
global.LOG.Info("recover from uploads successful!")
return nil
}
func handlePostgresqlBackup(db DatabaseHelper, parentTask *task.Task, targetDir, fileName, taskID string) error {
var (
err error
backupTask *task.Task
)
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)
if err != nil {
return err
}
}
itemHandler := func() error { return doPostgresqlgBackup(db, targetDir, fileName) }
backupTask.AddSubTask(task.GetTaskName(itemName, task.TaskBackup, task.TaskScopeWebsite), func(task *task.Task) error { return itemHandler() }, nil)
if parentTask != nil {
return itemHandler()
}
return backupTask.Execute()
}
func handlePostgresqlRecover(req dto.CommonRecover, parentTask *task.Task, isRollback bool) error {
var (
err error
itemTask *task.Task
)
dbInfo, err := postgresqlRepo.Get(repo.WithByName(req.DetailName), postgresqlRepo.WithByPostgresqlName(req.Name))
if err != nil {
return err
}
itemTask = parentTask
if parentTask == nil {
itemTask, err = task.NewTaskWithOps("Redis", task.TaskRecover, task.TaskScopeDatabase, req.TaskID, dbInfo.ID)
if err != nil {
return err
}
}
recoverDatabase := func(t *task.Task) error {
isOk := false
fileOp := files.NewFileOp()
if !fileOp.Stat(req.File) {
return buserr.WithName("ErrFileNotFound", req.File)
}
cli, err := LoadPostgresqlClientByFrom(req.Name)
if err != nil {
return err
}
defer cli.Close()
if !isRollback {
rollbackFile := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format(constant.DateTimeSlimLayout)))
if err := cli.Backup(client.BackupInfo{
Name: req.DetailName,
TargetDir: path.Dir(rollbackFile),
FileName: path.Base(rollbackFile),
Timeout: 300,
}); err != nil {
return fmt.Errorf("backup postgresql db %s for rollback before recover failed, err: %v", req.DetailName, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
SourceFile: rollbackFile,
Timeout: 300,
}); err != nil {
global.LOG.Errorf("rollback postgresql db %s from %s failed, err: %v", req.DetailName, rollbackFile, err)
} else {
global.LOG.Infof("rollback postgresql db %s from %s successful", req.DetailName, rollbackFile)
}
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
if err := cli.Recover(client.RecoverInfo{
Name: req.DetailName,
SourceFile: req.File,
Username: dbInfo.Username,
Timeout: 300,
}); err != nil {
global.LOG.Errorf("recover postgresql db %s from %s failed, err: %v", req.DetailName, req.File, err)
return err
}
isOk = true
return nil
}
itemTask.AddSubTask(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil)
if parentTask != nil {
return recoverDatabase(parentTask)
}
return itemTask.Execute()
}
func doPostgresqlgBackup(db DatabaseHelper, targetDir, fileName string) error {
cli, err := LoadPostgresqlClientByFrom(db.Database)
if err != nil {
return err
}
defer cli.Close()
backupInfo := pgclient.BackupInfo{
Name: db.Name,
TargetDir: targetDir,
FileName: fileName,
Timeout: 300,
}
return cli.Backup(backupInfo)
}
-276
View File
@@ -1,276 +0,0 @@
package service
import (
"context"
"fmt"
"os"
"path"
"strings"
"sync"
"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/global"
"github.com/jinzhu/copier"
)
type BackupRecordService struct{}
type IBackupRecordService interface {
SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error)
SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error)
DownloadRecord(info dto.DownloadRecord) (string, error)
DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error
BatchDeleteRecord(ids []uint) error
ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error)
ListFiles(req dto.OperateByID) []string
LoadRecordSize(req dto.SearchForSize) ([]dto.RecordFileSize, error)
UpdateDescription(req dto.UpdateDescription) error
}
func NewIBackupRecordService() IBackupRecordService {
return &BackupRecordService{}
}
func (u *BackupRecordService) SearchRecordsWithPage(search dto.RecordSearch) (int64, []dto.BackupRecords, error) {
total, records, err := backupRepo.PageRecord(
search.Page, search.PageSize,
repo.WithOrderBy("created_at desc"),
repo.WithByName(search.Name),
repo.WithByType(search.Type),
repo.WithByDetailName(search.DetailName),
)
if err != nil {
return 0, nil, err
}
accounts, _ := backupRepo.List()
var data []dto.BackupRecords
for _, record := range records {
var item dto.BackupRecords
if err := copier.Copy(&item, &record); err != nil {
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
}
for _, account := range accounts {
if account.ID == record.DownloadAccountID {
item.DownloadAccountID = account.ID
item.AccountName = account.Name
item.AccountType = account.Type
break
}
}
data = append(data, item)
}
return total, data, err
}
func (u *BackupRecordService) SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error) {
total, records, err := backupRepo.PageRecord(
search.Page, search.PageSize,
repo.WithOrderBy("created_at desc"),
backupRepo.WithByCronID(search.CronjobID),
)
if err != nil {
return 0, nil, err
}
accounts, _ := backupRepo.List()
var data []dto.BackupRecords
for _, record := range records {
var item dto.BackupRecords
if err := copier.Copy(&item, &record); err != nil {
global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err)
}
for _, account := range accounts {
if account.ID == record.DownloadAccountID {
item.DownloadAccountID = account.ID
item.AccountName = account.Name
item.AccountType = account.Type
break
}
}
data = append(data, item)
}
return total, data, err
}
func (u *BackupRecordService) DownloadRecord(info dto.DownloadRecord) (string, error) {
account, client, err := NewBackupClientWithID(info.DownloadAccountID)
if err != nil {
return "", fmt.Errorf("new cloud storage client failed, err: %v", err)
}
if account.Type == "LOCAL" {
return path.Join(global.Dir.LocalBackupDir, info.FileDir, info.FileName), nil
}
targetPath := fmt.Sprintf("%s/download/%s/%s", global.Dir.DataDir, info.FileDir, info.FileName)
if _, err := os.Stat(path.Dir(targetPath)); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(targetPath), os.ModePerm); err != nil {
global.LOG.Errorf("mkdir %s failed, err: %v", path.Dir(targetPath), err)
}
}
srcPath := fmt.Sprintf("%s/%s", info.FileDir, info.FileName)
if len(account.BackupPath) != 0 {
srcPath = path.Join(account.BackupPath, srcPath)
}
if exist, _ := client.Exist(srcPath); exist {
isOK, err := client.Download(srcPath, targetPath)
if !isOK || err != nil {
return "", fmt.Errorf("cloud storage download failed, err: %v", err)
}
}
return targetPath, nil
}
func (u *BackupRecordService) DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error {
if !withDeleteFile {
return backupRepo.DeleteRecord(context.Background(), repo.WithByType(backupType), repo.WithByName(name), repo.WithByDetailName(detailName))
}
records, err := backupRepo.ListRecord(repo.WithByType(backupType), repo.WithByName(name), repo.WithByDetailName(detailName))
if err != nil {
return err
}
for _, record := range records {
backup, client, err := NewBackupClientWithID(record.DownloadAccountID)
if err != nil {
global.LOG.Errorf("new client for backup account failed, err: %v", err)
continue
}
if _, err = client.Delete(path.Join(backup.BackupPath, record.FileDir, record.FileName)); err != nil {
global.LOG.Errorf("remove file %s failed, err: %v", path.Join(record.FileDir, record.FileName), err)
}
_ = backupRepo.DeleteRecord(context.Background(), repo.WithByID(record.ID))
}
return nil
}
func (u *BackupRecordService) BatchDeleteRecord(ids []uint) error {
records, err := backupRepo.ListRecord(repo.WithByIDs(ids))
if err != nil {
return err
}
for _, record := range records {
backup, client, err := NewBackupClientWithID(record.DownloadAccountID)
if err != nil {
global.LOG.Errorf("new client for backup account failed, err: %v", err)
continue
}
if _, err = client.Delete(path.Join(backup.BackupPath, record.FileDir, record.FileName)); err != nil {
global.LOG.Errorf("remove file %s failed, err: %v", path.Join(record.FileDir, record.FileName), err)
}
}
return backupRepo.DeleteRecord(context.Background(), repo.WithByIDs(ids))
}
func (u *BackupRecordService) ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error) {
records, err := backupRepo.ListRecord(
repo.WithOrderBy("created_at asc"),
repo.WithByName(name),
repo.WithByType("app"),
backupRepo.WithFileNameStartWith(fileName),
backupRepo.WithByDetailName(detailName),
)
if err != nil {
return nil, err
}
return records, err
}
func (u *BackupRecordService) ListFiles(req dto.OperateByID) []string {
backupItem, client, err := NewBackupClientWithID(req.ID)
if err != nil {
return []string{}
}
prefix := "system_snapshot"
if len(backupItem.BackupPath) != 0 {
prefix = path.Join(backupItem.BackupPath, prefix)
}
files, err := client.ListObjects(prefix)
if err != nil {
global.LOG.Debugf("load files failed, err: %v", err)
return []string{}
}
var datas []string
for _, file := range files {
fileName := path.Base(file)
if len(file) != 0 && (strings.HasPrefix(fileName, "1panel-v2.") || strings.HasPrefix(fileName, "snapshot-1panel-v2.")) {
datas = append(datas, path.Base(file))
}
}
return datas
}
type backupSizeHelper struct {
ID uint `json:"id"`
DownloadID uint `json:"downloadID"`
FilePath string `json:"filePath"`
Size uint `json:"size"`
}
func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.RecordFileSize, error) {
var list []backupSizeHelper
switch req.Type {
case "snapshot":
_, records, err := snapshotRepo.Page(req.Page, req.PageSize, repo.WithByLikeName(req.Info), repo.WithOrderRuleBy(req.OrderBy, req.Order))
if err != nil {
return nil, err
}
for _, item := range records {
list = append(list, backupSizeHelper{ID: item.ID, DownloadID: item.DownloadAccountID, FilePath: fmt.Sprintf("system_snapshot/%s.tar.gz", item.Name)})
}
case "cronjob":
_, records, err := backupRepo.PageRecord(req.Page, req.PageSize, repo.WithOrderBy("created_at desc"), backupRepo.WithByCronID(req.CronjobID))
if err != nil {
return nil, err
}
for _, item := range records {
list = append(list, backupSizeHelper{ID: item.ID, DownloadID: item.DownloadAccountID, FilePath: path.Join(item.FileDir, item.FileName)})
}
default:
_, records, err := backupRepo.PageRecord(
req.Page, req.PageSize,
repo.WithOrderBy("created_at desc"),
repo.WithByName(req.Name),
repo.WithByType(req.Type),
repo.WithByDetailName(req.DetailName),
)
if err != nil {
return nil, err
}
for _, item := range records {
list = append(list, backupSizeHelper{ID: item.ID, DownloadID: item.DownloadAccountID, FilePath: path.Join(item.FileDir, item.FileName)})
}
}
recordMap := make(map[uint]struct{})
var recordIds []string
for _, record := range list {
if _, ok := recordMap[record.DownloadID]; !ok {
recordMap[record.DownloadID] = struct{}{}
recordIds = append(recordIds, fmt.Sprintf("%v", record.DownloadID))
}
}
clientMap, err := NewBackupClientMap(recordIds)
if err != nil {
return nil, err
}
var datas []dto.RecordFileSize
var wg sync.WaitGroup
for i := 0; i < len(list); i++ {
datas = append(datas, dto.RecordFileSize{ID: list[i].ID})
if val, ok := clientMap[fmt.Sprintf("%v", list[i].DownloadID)]; ok {
wg.Add(1)
go func(index int) {
datas[index].Size, _ = val.client.Size(path.Join(val.backupPath, list[i].FilePath))
wg.Done()
}(i)
}
}
wg.Wait()
return datas, nil
}
func (u *BackupRecordService) UpdateDescription(req dto.UpdateDescription) error {
return backupRepo.UpdateRecordByMap(req.ID, map[string]interface{}{"description": req.Description})
}
-233
View File
@@ -1,233 +0,0 @@
package service
import (
"fmt"
"os"
"path"
"strings"
"time"
"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/cmd"
"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 {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
if err != nil {
return err
}
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
if err != nil {
return err
}
global.LOG.Infof("appendonly in redis conf is %s", appendonly)
timeNow := time.Now().Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
fileName := fmt.Sprintf("%s.rdb", timeNow)
if appendonly == "yes" {
if strings.HasPrefix(redisInfo.Version, "6.") {
fileName = fmt.Sprintf("%s.aof", timeNow)
} else {
fileName = fmt.Sprintf("%s.tar.gz", timeNow)
}
}
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",
Name: req.Name,
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
}
func (u *BackupService) RedisRecover(req dto.CommonRecover) error {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
if err != nil {
return err
}
global.LOG.Infof("recover redis from backup file %s", req.File)
if err := handleRedisRecover(redisInfo, nil, req.File, false, req.Secret, req.TaskID); err != nil {
return err
}
return nil
}
func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, 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)
if err != nil {
return err
}
}
backupDatabase := func(t *task.Task) error {
fileOp := files.NewFileOp()
if !fileOp.Stat(backupDir) {
if err := os.MkdirAll(backupDir, os.ModePerm); err != nil {
return fmt.Errorf("mkdir %s failed, err: %v", backupDir, err)
}
}
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 strings.HasSuffix(fileName, ".tar.gz") {
redisDataDir := fmt.Sprintf("%s/%s/%s/data/appendonlydir", global.Dir.AppInstallDir, "redis", redisInfo.Name)
if err := fileOp.TarGzCompressPro(true, redisDataDir, path.Join(backupDir, fileName), secret, ""); err != nil {
return err
}
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(string(stdout1))
}
return nil
}
stdout1, err1 := cmd.RunDefaultWithStdoutBashCf("docker cp %s:/data/dump.rdb %s/%s", redisInfo.ContainerName, backupDir, fileName)
if err1 != nil {
return errors.New(string(stdout1))
}
return nil
}
itemTask.AddSubTask(i18n.GetMsgByKey("TaskBackup"), backupDatabase, nil)
if parentTask != nil {
return backupDatabase(parentTask)
}
return itemTask.Execute()
}
func handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recoverFile string, isRollback bool, secret, taskID string) error {
var (
err error
itemTask *task.Task
)
itemTask = parentTask
if parentTask == nil {
itemTask, err = task.NewTaskWithOps("Redis", task.TaskRecover, task.TaskScopeDatabase, taskID, redisInfo.ID)
if err != nil {
return err
}
}
recoverDatabase := func(t *task.Task) error {
fileOp := files.NewFileOp()
if !fileOp.Stat(recoverFile) {
return buserr.WithName("ErrFileNotFound", recoverFile)
}
appendonly, err := configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly")
if err != nil {
return err
}
if appendonly == "yes" {
if strings.HasPrefix(redisInfo.Version, "6.") && !strings.HasSuffix(recoverFile, ".aof") {
return buserr.New("ErrTypeOfRedis")
}
if strings.HasPrefix(redisInfo.Version, "7.") && !strings.HasSuffix(recoverFile, ".tar.gz") {
return buserr.New("ErrTypeOfRedis")
}
} else {
if !strings.HasSuffix(recoverFile, ".rdb") {
return buserr.New("ErrTypeOfRedis")
}
}
global.LOG.Infof("appendonly in redis conf is %s", appendonly)
isOk := false
if !isRollback {
suffix := "rdb"
if appendonly == "yes" {
if strings.HasPrefix(redisInfo.Version, "6.") {
suffix = "aof"
} else {
suffix = "tar.gz"
}
}
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 {
return fmt.Errorf("backup database %s for rollback before recover failed, err: %v", redisInfo.Name, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := handleRedisRecover(redisInfo, itemTask, rollbackFile, true, secret, ""); err != nil {
global.LOG.Errorf("rollback redis from %s failed, err: %v", rollbackFile, err)
return
}
global.LOG.Infof("rollback redis from %s successful", rollbackFile)
_ = os.RemoveAll(rollbackFile)
} else {
_ = os.RemoveAll(rollbackFile)
}
}()
}
composeDir := fmt.Sprintf("%s/redis/%s", global.Dir.AppInstallDir, redisInfo.Name)
if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil {
return err
}
if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "7.") {
redisDataDir := fmt.Sprintf("%s/%s/%s/data", global.Dir.AppInstallDir, "redis", redisInfo.Name)
if err := fileOp.TarGzExtractPro(recoverFile, redisDataDir, secret); err != nil {
return err
}
} else {
itemName := "dump.rdb"
if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "6.") {
itemName = "appendonly.aof"
}
input, err := os.ReadFile(recoverFile)
if err != nil {
return err
}
if err = os.WriteFile(composeDir+"/data/"+itemName, input, 0640); err != nil {
return err
}
}
if _, err := compose.Up(composeDir + "/docker-compose.yml"); err != nil {
return err
}
isOk = true
return nil
}
itemTask.AddSubTask(i18n.GetMsgByKey("TaskRecover"), recoverDatabase, nil)
if parentTask != nil {
return recoverDatabase(parentTask)
}
return itemTask.Execute()
}
-353
View File
@@ -1,353 +0,0 @@
package service
import (
"context"
"encoding/json"
"fmt"
"io/fs"
"os"
"path"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"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"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
)
func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))
if err != nil {
return err
}
timeNow := time.Now().Format(constant.DateTimeSlimLayout)
itemDir := fmt.Sprintf("website/%s", website.Alias)
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
}
}()
return nil
}
func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
website, err := websiteRepo.GetFirst(websiteRepo.WithAlias(req.DetailName))
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)
}
}()
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
}
recoverTask.AddSubTask(task.GetTaskName(website.PrimaryDomain, task.TaskRecover, task.TaskScopeWebsite), func(t *task.Task) error {
isOk := false
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 {
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))
if err = fileOp.TarGzExtractPro(recoverFile, path.Dir(recoverFile), secret); err != nil {
return err
}
defer func() {
_ = os.RemoveAll(tmpPath)
}()
var oldWebsite model.Website
websiteJson, err := os.ReadFile(tmpPath + "/website.json")
if err != nil {
return err
}
if err = json.Unmarshal(websiteJson, &oldWebsite); err != nil {
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
}
if err = checkValidOfWebsite(&oldWebsite, website); err != nil {
t.Log(i18n.GetWithName("ErrCheckValid", err.Error()))
return err
}
temPathWithName := tmpPath + "/" + website.Alias
if !fileOp.Stat(tmpPath+"/website.json") || !fileOp.Stat(temPathWithName+".conf") || !fileOp.Stat(temPathWithName+".web.tar.gz") {
return buserr.WithDetail("ErrBackupExist", ".conf or .web.tar.gz", nil)
}
if website.Type == constant.Deployment {
if !fileOp.Stat(temPathWithName + ".app.tar.gz") {
return buserr.WithDetail("ErrBackupExist", ".app.tar.gz", nil)
}
}
nginxInfo, err := appInstallRepo.LoadBaseInfo(constant.AppOpenresty, "")
if err != nil {
return err
}
if err = fileOp.CopyFile(fmt.Sprintf("%s/%s.conf", tmpPath, website.Alias), GetOpenrestyDir(SiteConfDir)); err != nil {
return err
}
switch website.Type {
case constant.Deployment:
app, err := appInstallRepo.GetFirst(repo.WithByID(website.AppInstallID))
if err != nil {
return err
}
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:
runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))
if err != nil {
return err
}
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)
if oldWebsite.DbID > 0 {
if err := recoverWebsiteDatabase(t, oldWebsite.DbID, oldWebsite.DbType, tmpPath, website.Alias); err != nil {
return err
}
}
case constant.Static:
if oldWebsite.DbID > 0 {
if err := recoverWebsiteDatabase(t, oldWebsite.DbID, oldWebsite.DbType, tmpPath, website.Alias); err != nil {
return err
}
}
}
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)
}
oldWebsite.ID = website.ID
if err := websiteRepo.SaveWithoutCtx(&oldWebsite); err != nil {
return err
}
isOk = true
return nil
}, nil)
return recoverTask.Execute()
}
func handleWebsiteBackup(website *model.Website, parentTask *task.Task, 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)
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()
}
func doWebsiteBackup(website *model.Website, parentTask *task.Task, backupDir, fileName, excludes, secret string) error {
fileOp := files.NewFileOp()
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
if !fileOp.Stat(tmpDir) {
if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
return fmt.Errorf("mkdir %s failed, err: %v", backupDir, err)
}
}
defer func() {
_ = os.RemoveAll(tmpDir)
}()
remarkInfo, _ := json.Marshal(website)
if err := fileOp.SaveFile(tmpDir+"/website.json", string(remarkInfo), fs.ModePerm); err != nil {
return err
}
nginxConfFile := GetSitePath(*website, SiteConf)
if err := fileOp.CopyFile(nginxConfFile, tmpDir); err != nil {
return err
}
parentTask.Log(i18n.GetMsgByKey("BackupNginxConfig"))
switch website.Type {
case constant.Deployment:
app, err := appInstallRepo.GetFirst(repo.WithByID(website.AppInstallID))
if err != nil {
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 {
return err
}
parentTask.LogSuccess(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeApp))
case constant.Runtime:
runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))
if err != nil {
return err
}
parentTask.LogStart(task.GetTaskName(runtime.Name, task.TaskBackup, task.TaskScopeRuntime))
if err = handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias), excludes, ""); err != nil {
return err
}
parentTask.LogSuccess(task.GetTaskName(runtime.Name, task.TaskBackup, task.TaskScopeRuntime))
if website.DbID > 0 {
if err = backupDatabaseWithTask(parentTask, website.DbType, tmpDir, website.Alias, website.DbID); err != nil {
return err
}
}
case constant.Static:
if website.DbID > 0 {
if err := backupDatabaseWithTask(parentTask, website.DbType, tmpDir, website.Alias, website.DbID); err != nil {
return err
}
}
}
websiteDir := GetSitePath(*website, SiteDir)
parentTask.LogStart(i18n.GetMsgByKey("CompressDir"))
if err := fileOp.TarGzCompressPro(true, websiteDir, path.Join(tmpDir, fmt.Sprintf("%s.web.tar.gz", website.Alias)), "", excludes); err != nil {
return err
}
if err := fileOp.TarGzCompressPro(true, tmpDir, path.Join(backupDir, fileName), secret, ""); err != nil {
return err
}
parentTask.Log(i18n.GetWithName("CompressFileSuccess", fileName))
return nil
}
func checkValidOfWebsite(oldWebsite, website *model.Website) error {
if oldWebsite.Alias != website.Alias || oldWebsite.Type != website.Type {
return buserr.WithDetail("ErrBackupMatch", fmt.Sprintf("oldName: %s, oldType: %v", oldWebsite.Alias, oldWebsite.Type), nil)
}
if oldWebsite.AppInstallID != 0 {
_, err := appInstallRepo.GetFirst(repo.WithByID(oldWebsite.AppInstallID))
if err != nil {
return buserr.WithDetail("ErrBackupMatch", "app", nil)
}
}
if oldWebsite.RuntimeID != 0 {
if _, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(oldWebsite.RuntimeID)); err != nil {
return buserr.WithDetail("ErrBackupMatch", "runtime", nil)
}
}
if oldWebsite.WebsiteSSLID != 0 {
if _, err := websiteSSLRepo.GetFirst(repo.WithByID(oldWebsite.WebsiteSSLID)); err != nil {
return buserr.WithDetail("ErrBackupMatch", "ssl", nil)
}
}
return nil
}
func recoverWebsiteDatabase(t *task.Task, dbID uint, dbType, tmpPath, websiteKey string) error {
switch dbType {
case constant.AppPostgresql:
db, err := postgresqlRepo.Get(repo.WithByID(dbID))
if err != nil {
return err
}
taskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)
t.LogStart(taskName)
if err := handlePostgresqlRecover(dto.CommonRecover{
Name: db.PostgresqlName,
DetailName: db.Name,
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, websiteKey),
}, t, true); err != nil {
t.LogFailedWithErr(taskName, err)
return err
}
t.LogSuccess(taskName)
case constant.AppMysql, constant.AppMariaDB:
db, err := mysqlRepo.Get(repo.WithByID(dbID))
if err != nil {
return err
}
taskName := task.GetTaskName(db.Name, task.TaskRecover, task.TaskScopeDatabase)
t.LogStart(taskName)
if err := handleMysqlRecover(dto.CommonRecover{
Name: db.MysqlName,
DetailName: db.Name,
File: fmt.Sprintf("%s/%s.sql.gz", tmpPath, websiteKey),
}, t, true, ""); err != nil {
t.LogFailedWithErr(taskName, err)
return err
}
t.LogSuccess(taskName)
}
return nil
}
-453
View File
@@ -1,453 +0,0 @@
package service
import (
"context"
"fmt"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/pkg/errors"
)
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
var apps []model.AppInstall
if cronjob.AppID == "all" {
apps, _ = appInstallRepo.ListBy(context.Background())
} else {
appIds := strings.Split(cronjob.AppID, ",")
var idItems []uint
for i := 0; i < len(appIds); i++ {
itemID, _ := strconv.Atoi(appIds[i])
idItems = append(idItems, uint(itemID))
}
appItems, err := appInstallRepo.ListBy(context.Background(), repo.WithByIDs(idItems))
if err != nil {
return err
}
apps = appItems
}
if len(apps) == 0 {
return errors.New("no such app in database!")
}
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
}
for _, app := range apps {
taskItem.AddSubTaskWithOps(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
var record model.BackupRecord
record.From = "cronjob"
record.Type = "app"
record.CronjobID = cronjob.ID
record.Name = app.App.Key
record.DetailName = app.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
if err := doAppBackup(&app, task, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
return err
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
return nil
}
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
webs := loadWebsForJob(cronjob)
if len(webs) == 0 {
return errors.New("no such website in database!")
}
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
}
for _, web := range webs {
taskItem.AddSubTaskWithOps(task.GetTaskName(web.Alias, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
var record model.BackupRecord
record.From = "cronjob"
record.Type = "website"
record.CronjobID = cronjob.ID
record.Name = web.Alias
record.DetailName = web.Alias
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("website/%s", web.Alias))
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.Alias, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
if err := doWebsiteBackup(&web, taskItem, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
return err
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
return nil
}
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
dbs := loadDbsForJob(cronjob)
if len(dbs) == 0 {
return errors.New("no such db in database!")
}
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
}
for _, dbInfo := range dbs {
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.From = "cronjob"
record.Type = dbInfo.DBType
record.CronjobID = cronjob.ID
record.Name = dbInfo.Database
record.DetailName = dbInfo.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name))
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
if err := doMysqlBackup(dbInfo, backupDir, record.FileName); err != nil {
return err
}
} else {
if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName); err != nil {
return err
}
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
return err
}
record.FileDir = path.Dir(downloadPath)
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
return nil
}
func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
taskItem.AddSubTaskWithOps(task.GetTaskName(cronjob.SourceDir, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
}
fileName := fmt.Sprintf("%s.tar.gz", startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name))
fileOp := files.NewFileOp()
if cronjob.IsDir {
taskItem.Logf("Dir: %s, Excludes: %s", cronjob.SourceDir, cronjob.ExclusionRules)
if err := fileOp.TarGzCompressPro(true, cronjob.SourceDir, path.Join(backupDir, fileName), cronjob.Secret, cronjob.ExclusionRules); err != nil {
return err
}
} else {
taskItem.Logf("Files: %s", cronjob.SourceDir)
fileLists := strings.Split(cronjob.SourceDir, ",")
if err := fileOp.TarGzFilesWithCompressPro(fileLists, path.Join(backupDir, fileName), cronjob.Secret); err != nil {
return err
}
}
var record model.BackupRecord
record.From = "cronjob"
record.Type = "directory"
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, fileName))
if err != nil {
taskItem.LogFailedWithErr("Upload backup file", err)
return err
}
record.FileDir = path.Dir(downloadPath)
record.FileName = fileName
if err := backupRepo.CreateRecord(&record); err != nil {
taskItem.LogFailedWithErr("Save record", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
return nil
}
func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.Time, taskItem *task.Task) error {
taskItem.AddSubTaskWithOps(task.GetTaskName(i18n.GetMsgByKey("BackupSystemLog"), task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
}
nameItem := startTime.Format(constant.DateTimeSlimLayout) + common.RandStrAndNum(5)
fileName := fmt.Sprintf("system_log_%s.tar.gz", nameItem)
backupDir := path.Join(global.Dir.TmpDir, "log", nameItem)
if err := handleBackupLogs(taskItem, backupDir, fileName, cronjob.Secret); err != nil {
return err
}
var record model.BackupRecord
record.From = "cronjob"
record.Type = "log"
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(path.Dir(backupDir), fileName))
if err != nil {
taskItem.LogFailedWithErr("Upload backup file", err)
return err
}
record.FileDir = path.Dir(downloadPath)
record.FileName = fileName
if err := backupRepo.CreateRecord(&record); err != nil {
taskItem.LogFailedWithErr("Save record", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
return nil
}
func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.JobRecords, taskItem *task.Task) error {
accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ","))
if err != nil {
return err
}
itemData, err := NewISnapshotService().LoadSnapshotData()
if err != nil {
return err
}
var record model.BackupRecord
record.From = "cronjob"
record.Type = "snapshot"
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
record.FileDir = "system_snapshot"
versionItem, _ := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
scope := "core"
if !global.IsMaster {
scope = "agent"
}
req := dto.SnapshotCreate{
Name: fmt.Sprintf("snapshot-1panel-%s-%s-linux-%s-%s", scope, versionItem.Value, loadOs(), jobRecord.StartTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)),
Secret: cronjob.Secret,
TaskID: jobRecord.TaskID,
SourceAccountIDs: record.SourceAccountIDs,
DownloadAccountID: cronjob.DownloadAccountID,
AppData: itemData.AppData,
PanelData: itemData.PanelData,
BackupData: itemData.BackupData,
WithMonitorData: true,
WithLoginLog: true,
WithOperationLog: true,
WithSystemLog: true,
WithTaskLog: true,
}
if err := NewISnapshotService().SnapshotCreate(taskItem, req, jobRecord.ID, cronjob.RetryTimes, cronjob.Timeout); err != nil {
return err
}
record.FileName = req.Name + ".tar.gz"
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
u.removeExpiredBackup(cronjob, accountMap, record)
return nil
}
type DatabaseHelper struct {
ID uint
DBType string
Database string
Name string
}
func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
var dbs []DatabaseHelper
if cronjob.DBName == "all" {
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
databaseService := NewIDatabaseService()
mysqlItems, _ := databaseService.LoadItems(cronjob.DBType)
for _, mysql := range mysqlItems {
dbs = append(dbs, DatabaseHelper{
ID: mysql.ID,
DBType: cronjob.DBType,
Database: mysql.Name,
Name: mysql.Name,
})
}
} else {
pgItems, _ := postgresqlRepo.List()
for _, pg := range pgItems {
dbs = append(dbs, DatabaseHelper{
ID: pg.ID,
DBType: cronjob.DBType,
Database: pg.PostgresqlName,
Name: pg.Name,
})
}
}
return dbs
}
dbNames := strings.Split(cronjob.DBName, ",")
for _, name := range dbNames {
itemID, _ := strconv.Atoi(name)
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
mysqlItem, _ := mysqlRepo.Get(repo.WithByID(uint(itemID)))
dbs = append(dbs, DatabaseHelper{
ID: mysqlItem.ID,
DBType: cronjob.DBType,
Database: mysqlItem.MysqlName,
Name: mysqlItem.Name,
})
} else {
pgItem, _ := postgresqlRepo.Get(repo.WithByID(uint(itemID)))
dbs = append(dbs, DatabaseHelper{
ID: pgItem.ID,
DBType: cronjob.DBType,
Database: pgItem.PostgresqlName,
Name: pgItem.Name,
})
}
}
return dbs
}
func loadWebsForJob(cronjob model.Cronjob) []model.Website {
var weblist []model.Website
if cronjob.Website == "all" {
weblist, _ = websiteRepo.List()
return weblist
}
websites := strings.Split(cronjob.Website, ",")
var idItems []uint
for i := 0; i < len(websites); i++ {
itemID, _ := strconv.Atoi(websites[i])
idItems = append(idItems, uint(itemID))
}
weblist, _ = websiteRepo.GetBy(repo.WithByIDs(idItems))
return weblist
}
func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret string) error {
fileOp := files.NewFileOp()
websites, err := websiteRepo.List()
if err != nil {
return err
}
if len(websites) != 0 {
webItem := GetOpenrestyDir(SitesRootDir)
for _, website := range websites {
taskItem.Logf("%s Website logs %s...", i18n.GetMsgByKey("TaskBackup"), website.Alias)
dirItem := path.Join(targetDir, "website", website.Alias)
if _, err := os.Stat(dirItem); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dirItem, os.ModePerm); err != nil {
return err
}
}
itemDir := path.Join(webItem, website.Alias, "log")
logFiles, _ := os.ReadDir(itemDir)
if len(logFiles) != 0 {
for i := 0; i < len(logFiles); i++ {
if !logFiles[i].IsDir() {
_ = fileOp.CopyFile(path.Join(itemDir, logFiles[i].Name()), dirItem)
}
}
}
itemDir2 := path.Join(global.Dir.LocalBackupDir, "log/website", website.Alias)
logFiles2, _ := os.ReadDir(itemDir2)
if len(logFiles2) != 0 {
for i := 0; i < len(logFiles2); i++ {
if !logFiles2[i].IsDir() {
_ = fileOp.CopyFile(path.Join(itemDir2, logFiles2[i].Name()), dirItem)
}
}
}
}
}
systemDir := path.Join(targetDir, "system")
if _, err := os.Stat(systemDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(systemDir, os.ModePerm); err != nil {
return err
}
}
taskItem.Logf("%s System logs...", i18n.GetMsgByKey("TaskBackup"))
systemLogFiles, _ := os.ReadDir(global.Dir.LogDir)
if len(systemLogFiles) != 0 {
for i := 0; i < len(systemLogFiles); i++ {
if !systemLogFiles[i].IsDir() {
_ = fileOp.CopyFile(path.Join(global.Dir.LogDir, systemLogFiles[i].Name()), systemDir)
}
}
}
taskItem.Logf("%s SSH logs...", i18n.GetMsgByKey("TaskBackup"))
loginLogFiles, _ := os.ReadDir("/var/log")
loginDir := path.Join(targetDir, "login")
if _, err := os.Stat(loginDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(loginDir, os.ModePerm); err != nil {
return err
}
}
if len(loginLogFiles) != 0 {
for i := 0; i < len(loginLogFiles); i++ {
if !loginLogFiles[i].IsDir() && (strings.HasPrefix(loginLogFiles[i].Name(), "secure") || strings.HasPrefix(loginLogFiles[i].Name(), "auth.log")) {
_ = fileOp.CopyFile(path.Join("/var/log", loginLogFiles[i].Name()), loginDir)
}
}
}
taskItem.Log("backup ssh log successful!")
if err := fileOp.TarGzCompressPro(true, targetDir, path.Join(path.Dir(targetDir), fileName), secret, ""); err != nil {
return err
}
defer func() {
_ = os.RemoveAll(targetDir)
}()
return nil
}
-325
View File
@@ -1,325 +0,0 @@
package service
import (
"context"
"fmt"
"os"
pathUtils "path"
"strings"
"time"
"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/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/files"
"github.com/1Panel-dev/1Panel/agent/utils/ntp"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
)
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
record := cronjobRepo.StartRecords(cronjob.ID, "", cronjob.Type)
go func() {
taskItem, err := task.NewTaskWithOps(fmt.Sprintf("cronjob-%s", cronjob.Name), task.TaskHandle, task.TaskScopeCronjob, record.TaskID, cronjob.ID)
if err != nil {
global.LOG.Errorf("new task for exec shell failed, err: %v", err)
return
}
err = u.loadTask(cronjob, &record, taskItem)
if cronjob.Type == "snapshot" {
if err != nil {
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
if len(taskItem.ID) == 0 {
record.TaskID = ""
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
handleCronJobAlert(cronjob)
return
}
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
return
}
if err != nil {
global.LOG.Debugf("preper to handle cron job [%s] %s failed, err: %v", cronjob.Type, cronjob.Name, err)
record.TaskID = ""
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
return
}
if err := taskItem.Execute(); err != nil {
taskItem, _ := taskRepo.GetFirst(taskRepo.WithByID(record.TaskID))
if len(taskItem.ID) == 0 {
record.TaskID = ""
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
handleCronJobAlert(cronjob)
} else {
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
}
}()
}
func (u *CronjobService) loadTask(cronjob *model.Cronjob, record *model.JobRecords, taskItem *task.Task) error {
var err error
switch cronjob.Type {
case "shell":
if cronjob.ScriptMode == "library" {
scriptItem, _ := scriptRepo.Get(repo.WithByID(cronjob.ScriptID))
if scriptItem.ID == 0 {
return fmt.Errorf("load script from db failed, err: %v", err)
}
cronjob.Script = scriptItem.Script
cronjob.ScriptMode = "input"
}
if len(cronjob.Script) == 0 {
return fmt.Errorf("the script content is empty and is skipped")
}
u.handleShell(*cronjob, taskItem)
case "curl":
if len(cronjob.URL) == 0 {
return fmt.Errorf("the url is empty and is skipped")
}
u.handleCurl(*cronjob, taskItem)
case "ntp":
u.handleNtpSync(*cronjob, taskItem)
case "cutWebsiteLog":
err = u.handleCutWebsiteLog(cronjob, record.StartTime, taskItem)
case "clean":
u.handleSystemClean(*cronjob, taskItem)
case "website":
err = u.handleWebsite(*cronjob, record.StartTime, taskItem)
case "app":
err = u.handleApp(*cronjob, record.StartTime, taskItem)
case "database":
err = u.handleDatabase(*cronjob, record.StartTime, taskItem)
case "directory":
if len(cronjob.SourceDir) == 0 {
return fmt.Errorf("the source dir is empty and is skipped")
}
err = u.handleDirectory(*cronjob, record.StartTime, taskItem)
case "log":
err = u.handleSystemLog(*cronjob, record.StartTime, taskItem)
case "snapshot":
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
err = u.handleSnapshot(*cronjob, *record, taskItem)
}
return err
}
func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task) {
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error {
if len(cronjob.ContainerName) != 0 {
command := "sh"
if len(cronjob.Command) != 0 {
command = cronjob.Command
}
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(cronjob.Script, "\"", "\\\""))
}
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")
_ = os.MkdirAll(pathUtils.Dir(fileItem), os.ModePerm)
shellFile, err := os.OpenFile(fileItem, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, constant.FilePerm)
if err != nil {
return err
}
defer shellFile.Close()
if _, err := shellFile.WriteString(cronjob.Script); err != nil {
return err
}
if len(cronjob.User) == 0 {
return cmdMgr.Run(cronjob.Executor, fileItem)
}
return cmdMgr.Run("sudo", "-u", cronjob.User, cronjob.Executor, fileItem)
}
if len(cronjob.User) == 0 {
return cmdMgr.Run(cronjob.Executor, cronjob.Script)
}
if err := cmdMgr.Run("sudo", "-u", cronjob.User, cronjob.Executor, cronjob.Script); err != nil {
return err
}
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
func (u *CronjobService) handleCurl(cronjob model.Cronjob, taskItem *task.Task) {
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error {
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
return cmdMgr.Run("curl", cronjob.URL)
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
func (u *CronjobService) handleNtpSync(cronjob model.Cronjob, taskItem *task.Task) {
taskItem.AddSubTaskWithOps(i18n.GetMsgByKey("HandleNtpSync"), func(t *task.Task) error {
ntpServer, err := settingRepo.Get(settingRepo.WithByKey("NtpSite"))
if err != nil {
return err
}
taskItem.Logf("ntp server: %s", ntpServer.Value)
ntime, err := ntp.GetRemoteTime(ntpServer.Value)
if err != nil {
return err
}
if err := ntp.UpdateSystemTime(ntime.Format(constant.DateTimeLayout)); 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 {
taskItem.AddSubTaskWithOps(i18n.GetWithName("CutWebsiteLog", cronjob.Name), func(t *task.Task) error {
websites := loadWebsForJob(*cronjob)
fileOp := files.NewFileOp()
baseDir := GetOpenrestyDir(SitesRootDir)
clientMap, err := NewBackupClientMap([]string{fmt.Sprintf("%v", cronjob.DownloadAccountID)})
if err != nil {
return fmt.Errorf("load local backup client failed, err: %v", err)
}
for _, website := range websites {
taskItem.Log(website.Alias)
var record model.BackupRecord
record.From = "cronjob"
record.Type = "cut-website-log"
record.CronjobID = cronjob.ID
record.Name = website.Alias
record.DetailName = website.Alias
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
backupDir := pathUtils.Join(global.Dir.LocalBackupDir, "log", "website", website.Alias)
if !fileOp.Stat(backupDir) {
_ = os.MkdirAll(backupDir, constant.DirPerm)
}
record.FileDir = strings.TrimPrefix(backupDir, global.Dir.LocalBackupDir+"/")
record.FileName = fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format(constant.DateTimeSlimLayout))
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
websiteLogDir := pathUtils.Join(baseDir, website.Alias, "log")
srcAccessLogPath := pathUtils.Join(websiteLogDir, "access.log")
srcErrorLogPath := pathUtils.Join(websiteLogDir, "error.log")
dstFilePath := pathUtils.Join(backupDir, record.FileName)
if err := backupLogFile(dstFilePath, websiteLogDir, fileOp); err != nil {
taskItem.LogFailedWithErr("CutWebsiteLog", err)
continue
} else {
_ = fileOp.WriteFile(srcAccessLogPath, strings.NewReader(""), constant.DirPerm)
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), constant.DirPerm)
}
taskItem.Log(i18n.GetMsgWithMap("CutWebsiteLogSuccess", map[string]interface{}{"name": website.PrimaryDomain, "path": dstFilePath}))
u.removeExpiredBackup(*cronjob, clientMap, record)
}
return nil
}, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
return nil
}
func backupLogFile(dstFilePath, websiteLogDir string, fileOp files.FileOp) error {
cmdMgr := cmd.NewCommandMgr()
if err := cmdMgr.RunBashCf("tar -czf %s -C %s %s", dstFilePath, websiteLogDir, strings.Join([]string{"access.log", "error.log"}, " ")); err != nil {
dstDir := pathUtils.Dir(dstFilePath)
if err = fileOp.Copy(pathUtils.Join(websiteLogDir, "access.log"), dstDir); err != nil {
return err
}
if err = fileOp.Copy(pathUtils.Join(websiteLogDir, "error.log"), dstDir); err != nil {
return err
}
if err = cmdMgr.RunBashCf("tar -czf %s -C %s %s", dstFilePath, dstDir, strings.Join([]string{"access.log", "error.log"}, " ")); err != nil {
return err
}
_ = fileOp.DeleteFile(pathUtils.Join(dstDir, "access.log"))
_ = fileOp.DeleteFile(pathUtils.Join(dstDir, "error.log"))
return nil
}
return nil
}
func (u *CronjobService) handleSystemClean(cronjob model.Cronjob, taskItem *task.Task) {
cleanTask := doSystemClean(taskItem)
taskItem.AddSubTaskWithOps(i18n.GetMsgByKey("HandleSystemClean"), cleanTask, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap map[string]backupClientHelper, file string) (string, error) {
defer func() {
_ = os.Remove(file)
}()
accounts := strings.Split(cronjob.SourceAccountIDs, ",")
cloudSrc := strings.TrimPrefix(file, global.Dir.TmpDir+"/")
for _, account := range accounts {
if len(account) != 0 {
global.LOG.Debugf("start upload file to %s, dir: %s", accountMap[account].name, pathUtils.Join(accountMap[account].backupPath, cloudSrc))
if _, err := accountMap[account].client.Upload(file, pathUtils.Join(accountMap[account].backupPath, cloudSrc)); err != nil {
global.LOG.Errorf("upload file to %s failed, err: %v", accountMap[account].name, err)
continue
}
global.LOG.Debugf("upload successful!")
}
}
return cloudSrc, nil
}
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]backupClientHelper, record model.BackupRecord) {
var opts []repo.DBOption
opts = append(opts, repo.WithByFrom("cronjob"))
opts = append(opts, backupRepo.WithByCronID(cronjob.ID))
opts = append(opts, repo.WithOrderBy("created_at desc"))
if record.ID != 0 {
opts = append(opts, repo.WithByType(record.Type))
opts = append(opts, repo.WithByName(record.Name))
opts = append(opts, repo.WithByDetailName(record.DetailName))
}
records, _ := backupRepo.ListRecord(opts...)
if len(records) <= int(cronjob.RetainCopies) {
return
}
for i := int(cronjob.RetainCopies); i < len(records); i++ {
accounts := strings.Split(cronjob.SourceAccountIDs, ",")
if cronjob.Type == "snapshot" {
for _, account := range accounts {
if len(account) != 0 {
if _, ok := accountMap[account]; !ok {
continue
}
_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName))
}
}
_ = snapshotRepo.Delete(repo.WithByName(strings.TrimSuffix(records[i].FileName, ".tar.gz")))
} else {
for _, account := range accounts {
if len(account) != 0 {
if _, ok := accountMap[account]; !ok {
continue
}
_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName))
}
}
}
_ = backupRepo.DeleteRecord(context.Background(), repo.WithByID(records[i].ID))
}
}
func hasBackup(cronjobType string) bool {
return cronjobType == "app" || cronjobType == "database" || cronjobType == "website" || cronjobType == "directory" || cronjobType == "snapshot" || cronjobType == "log" || cronjobType == "cutWebsiteLog"
}
func handleCronJobAlert(cronjob *model.Cronjob) {
pushAlert := dto.PushAlert{
TaskName: cronjob.Name,
AlertType: cronjob.Type,
EntryID: cronjob.ID,
Param: cronjob.Type,
}
err := xpack.PushAlert(pushAlert)
if err != nil {
global.LOG.Errorf("cronjob alert push failed, err: %v", err)
return
}
}
-561
View File
@@ -1,561 +0,0 @@
package service
import (
"context"
"encoding/json"
"fmt"
network "net"
"os"
"sort"
"strings"
"sync"
"time"
"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/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
"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/copier"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
)
type DashboardService struct{}
type IDashboardService interface {
LoadOsInfo() (*dto.OsInfo, error)
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
LoadCurrentInfoForNode() *dto.NodeCurrent
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error)
ChangeShow(req dto.SettingUpdate) error
ListLauncherOption(filter string) ([]dto.LauncherOption, error)
Restart(operation string) error
}
func NewIDashboardService() IDashboardService {
return &DashboardService{}
}
func (u *DashboardService) Restart(operation string) error {
if operation != "1panel" && operation != "system" && operation != "1panel-agent" {
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) {
var baseInfo dto.OsInfo
hostInfo, err := host.Info()
if err != nil {
return nil, err
}
baseInfo.OS = hostInfo.OS
baseInfo.Platform = hostInfo.Platform
baseInfo.PlatformFamily = hostInfo.PlatformFamily
baseInfo.KernelArch = hostInfo.KernelArch
baseInfo.KernelVersion = hostInfo.KernelVersion
diskInfo, err := disk.Usage(global.Dir.BaseDir)
if err == nil {
baseInfo.DiskSize = int64(diskInfo.Free)
}
if baseInfo.KernelArch == "armv7l" {
baseInfo.KernelArch = "armv7"
}
if baseInfo.KernelArch == "x86_64" {
baseInfo.KernelArch = "amd64"
}
return &baseInfo, nil
}
func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
var currentInfo dto.NodeCurrent
currentInfo.CPUTotal, _ = cpu.Counts(true)
totalPercent, _ := cpu.Percent(100*time.Millisecond, false)
if len(totalPercent) == 1 {
currentInfo.CPUUsedPercent = totalPercent[0]
currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
}
loadInfo, _ := load.Avg()
currentInfo.Load1 = loadInfo.Load1
currentInfo.Load5 = loadInfo.Load5
currentInfo.Load15 = loadInfo.Load15
currentInfo.LoadUsagePercent = loadInfo.Load1 / (float64(currentInfo.CPUTotal*2) * 0.75) * 100
memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryAvailable = memoryInfo.Available
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory()
currentInfo.SwapMemoryTotal = swapInfo.Total
currentInfo.SwapMemoryAvailable = swapInfo.Free
currentInfo.SwapMemoryUsed = swapInfo.Used
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
return &currentInfo
}
func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error) {
var baseInfo dto.DashboardBase
hostInfo, err := host.Info()
if err != nil {
return nil, err
}
baseInfo.Hostname = hostInfo.Hostname
baseInfo.OS = hostInfo.OS
baseInfo.Platform = hostInfo.Platform
baseInfo.PlatformFamily = hostInfo.PlatformFamily
baseInfo.PlatformVersion = hostInfo.PlatformVersion
baseInfo.KernelArch = hostInfo.KernelArch
baseInfo.KernelVersion = hostInfo.KernelVersion
ss, _ := json.Marshal(hostInfo)
baseInfo.VirtualizationSystem = string(ss)
baseInfo.IpV4Addr = GetOutboundIP()
httpProxy := os.Getenv("http_proxy")
if httpProxy == "" {
httpProxy = os.Getenv("HTTP_PROXY")
}
if httpProxy != "" {
baseInfo.SystemProxy = httpProxy
}
baseInfo.SystemProxy = "noProxy"
appInstall, err := appInstallRepo.ListBy(context.Background())
if err != nil {
return nil, err
}
baseInfo.AppInstalledNumber = len(appInstall)
postgresqlDbs, err := postgresqlRepo.List()
if err != nil {
return nil, err
}
mysqlDbs, err := mysqlRepo.List()
if err != nil {
return nil, err
}
baseInfo.DatabaseNumber = len(mysqlDbs) + len(postgresqlDbs)
website, err := websiteRepo.GetBy()
if err != nil {
return nil, err
}
baseInfo.WebsiteNumber = len(website)
cronjobs, err := cronjobRepo.List()
if err != nil {
return nil, err
}
baseInfo.CronjobNumber = len(cronjobs)
cpuInfo, err := cpu.Info()
if err == nil {
baseInfo.CPUModelName = cpuInfo[0].ModelName
}
baseInfo.CPUCores, _ = cpu.Counts(false)
baseInfo.CPULogicalCores, _ = cpu.Counts(true)
baseInfo.CurrentInfo = *u.LoadCurrentInfo(ioOption, netOption)
return &baseInfo, nil
}
func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent {
var currentInfo dto.DashboardCurrent
hostInfo, _ := host.Info()
currentInfo.Uptime = hostInfo.Uptime
currentInfo.TimeSinceUptime = time.Now().Add(-time.Duration(hostInfo.Uptime) * time.Second).Format(constant.DateTimeLayout)
currentInfo.Procs = hostInfo.Procs
currentInfo.CPUTotal, _ = cpu.Counts(true)
totalPercent, _ := cpu.Percent(100*time.Millisecond, false)
if len(totalPercent) == 1 {
currentInfo.CPUUsedPercent = totalPercent[0]
currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
}
currentInfo.CPUPercent, _ = cpu.Percent(100*time.Millisecond, true)
loadInfo, _ := load.Avg()
currentInfo.Load1 = loadInfo.Load1
currentInfo.Load5 = loadInfo.Load5
currentInfo.Load15 = loadInfo.Load15
currentInfo.LoadUsagePercent = loadInfo.Load1 / (float64(currentInfo.CPUTotal*2) * 0.75) * 100
memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryAvailable = memoryInfo.Available
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory()
currentInfo.SwapMemoryTotal = swapInfo.Total
currentInfo.SwapMemoryAvailable = swapInfo.Free
currentInfo.SwapMemoryUsed = swapInfo.Used
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
currentInfo.DiskData = loadDiskInfo()
currentInfo.GPUData = loadGPUInfo()
currentInfo.XPUData = loadXpuInfo()
if ioOption == "all" {
diskInfo, _ := disk.IOCounters()
for _, state := range diskInfo {
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOCount += (state.ReadCount + state.WriteCount)
currentInfo.IOReadTime += state.ReadTime
currentInfo.IOWriteTime += state.WriteTime
}
} else {
diskInfo, _ := disk.IOCounters(ioOption)
for _, state := range diskInfo {
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOCount += (state.ReadCount + state.WriteCount)
currentInfo.IOReadTime += state.ReadTime
currentInfo.IOWriteTime += state.WriteTime
}
}
if netOption == "all" {
netInfo, _ := net.IOCounters(false)
if len(netInfo) != 0 {
currentInfo.NetBytesSent = netInfo[0].BytesSent
currentInfo.NetBytesRecv = netInfo[0].BytesRecv
}
} else {
netInfo, _ := net.IOCounters(true)
for _, state := range netInfo {
if state.Name == netOption {
currentInfo.NetBytesSent = state.BytesSent
currentInfo.NetBytesRecv = state.BytesRecv
}
}
}
currentInfo.ShotTime = time.Now()
return &currentInfo
}
func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error) {
var (
data []dto.AppLauncher
recommendList []dto.AppLauncher
)
appInstalls, err := appInstallRepo.ListBy(context.Background())
if err != nil {
return data, err
}
apps, err := appRepo.GetBy()
if err != nil {
return data, err
}
showList, _ := launcherRepo.ListName()
defaultList := []string{"openresty", "mysql", "halo", "redis", "maxkb", "wordpress"}
allList := common.RemoveRepeatStr(append(defaultList, showList...))
for _, showItem := range allList {
var itemData dto.AppLauncher
for _, app := range apps {
if showItem == app.Key {
itemData.Key = app.Key
itemData.Type = app.Type
itemData.Name = app.Name
itemData.Icon = app.Icon
itemData.Limit = app.Limit
itemData.Recommend = app.Recommend
itemData.Description = app.GetDescription(ctx)
break
}
}
if len(itemData.Icon) == 0 {
continue
}
for _, install := range appInstalls {
if install.App.Key == showItem {
itemData.IsInstall = true
itemData.Detail = append(itemData.Detail, dto.InstallDetail{
InstallID: install.ID,
DetailID: install.AppDetailId,
Name: install.Name,
Version: install.Version,
Status: install.Status,
Path: install.GetPath(),
WebUI: install.WebUI,
HttpPort: install.HttpPort,
HttpsPort: install.HttpsPort,
})
}
}
if ArryContains(defaultList, showItem) && len(itemData.Detail) == 0 {
itemData.IsRecommend = true
recommendList = append(recommendList, itemData)
continue
}
if !ArryContains(showList, showItem) && len(itemData.Detail) != 0 {
continue
}
data = append(data, itemData)
}
sort.Slice(recommendList, func(i, j int) bool {
return recommendList[i].Recommend < recommendList[j].Recommend
})
sort.Slice(data, func(i, j int) bool {
return data[i].Name < data[j].Name
})
data = append(data, recommendList...)
return data, nil
}
func (u *DashboardService) ChangeShow(req dto.SettingUpdate) error {
launcher, _ := launcherRepo.Get(repo.WithByKey(req.Key))
if req.Value == constant.StatusEnable && launcher.ID == 0 {
if err := launcherRepo.Create(&model.AppLauncher{Key: req.Key}); err != nil {
return err
}
}
if req.Value == constant.StatusDisable && launcher.ID != 0 {
if err := launcherRepo.Delete(repo.WithByKey(req.Key)); err != nil {
return err
}
}
return nil
}
func (u *DashboardService) ListLauncherOption(filter string) ([]dto.LauncherOption, error) {
showList, _ := launcherRepo.ListName()
var data []dto.LauncherOption
optionMap := make(map[string]bool)
appInstalls, err := appInstallRepo.ListBy(context.Background())
if err != nil {
return data, err
}
for _, install := range appInstalls {
isShow := false
for _, item := range showList {
if install.App.Key == item {
isShow = true
break
}
}
optionMap[install.App.Key] = isShow
}
for key, val := range optionMap {
if len(filter) != 0 && !strings.Contains(key, filter) {
continue
}
data = append(data, dto.LauncherOption{Key: key, IsShow: val})
}
sort.Slice(data, func(i, j int) bool {
return data[i].Key < data[j].Key
})
return data, nil
}
type diskInfo struct {
Type string
Mount string
Device string
}
func loadDiskInfo() []dto.DiskInfo {
var datas []dto.DiskInfo
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(2 * time.Second))
stdout, err := cmdMgr.RunWithStdoutBashC("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")
if err != nil {
cmdMgr2 := cmd.NewCommandMgr(cmd.WithTimeout(1 * time.Second))
stdout, err = cmdMgr2.RunWithStdoutBashC("df -lhT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")
if err != nil {
return datas
}
}
lines := strings.Split(stdout, "\n")
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)
if len(fields) < 7 {
continue
}
if strings.HasPrefix(fields[6], "/snap") || len(strings.Split(fields[6], "/")) > 10 {
continue
}
if strings.TrimSpace(fields[1]) == "tmpfs" || strings.TrimSpace(fields[1]) == "overlay" {
continue
}
if strings.Contains(fields[2], "K") {
continue
}
if strings.Contains(fields[6], "docker") || strings.Contains(fields[6], "podman") || strings.Contains(fields[6], "containerd") || strings.HasPrefix(fields[6], "/var/lib/containers") {
continue
}
isExclude := false
for _, exclude := range excludes {
if exclude == fields[6] {
isExclude = true
}
}
if isExclude {
continue
}
mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: strings.Join(fields[6:], " ")})
}
var (
wg sync.WaitGroup
mu sync.Mutex
)
wg.Add(len(mounts))
for i := 0; i < len(mounts); i++ {
go func(timeoutCh <-chan time.Time, mount diskInfo) {
defer wg.Done()
var itemData dto.DiskInfo
itemData.Path = mount.Mount
itemData.Type = mount.Type
itemData.Device = mount.Device
select {
case <-timeoutCh:
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: timeout", mount.Mount)
default:
state, err := disk.Usage(mount.Mount)
if err != nil {
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
global.LOG.Errorf("load disk info from %s failed, err: %v", mount.Mount, err)
return
}
itemData.Total = state.Total
itemData.Free = state.Free
itemData.Used = state.Used
itemData.UsedPercent = state.UsedPercent
itemData.InodesTotal = state.InodesTotal
itemData.InodesUsed = state.InodesUsed
itemData.InodesFree = state.InodesFree
itemData.InodesUsedPercent = state.InodesUsedPercent
mu.Lock()
datas = append(datas, itemData)
mu.Unlock()
}
}(time.After(5*time.Second), mounts[i])
}
wg.Wait()
sort.Slice(datas, func(i, j int) bool {
return datas[i].Path < datas[j].Path
})
return datas
}
func loadGPUInfo() []dto.GPUInfo {
ok, client := gpu.New()
var list []interface{}
if ok {
info, err := client.LoadGpuInfo()
if err != nil || len(info.GPUs) == 0 {
return nil
}
for _, item := range info.GPUs {
list = append(list, item)
}
}
if len(list) == 0 {
return nil
}
var data []dto.GPUInfo
for _, gpu := range list {
var dataItem dto.GPUInfo
if err := copier.Copy(&dataItem, &gpu); err != nil {
continue
}
dataItem.PowerUsage = dataItem.PowerDraw + " / " + dataItem.MaxPowerLimit
dataItem.MemoryUsage = dataItem.MemUsed + " / " + dataItem.MemTotal
data = append(data, dataItem)
}
return data
}
type AppLauncher struct {
Key string `json:"key"`
}
func ArryContains(arr []string, element string) bool {
for _, v := range arr {
if v == element {
return true
}
}
return false
}
func loadXpuInfo() []dto.XPUInfo {
var list []interface{}
ok, xpuClient := xpu.New()
if ok {
xpus, err := xpuClient.LoadDashData()
if err != nil || len(xpus) == 0 {
return nil
}
for _, item := range xpus {
list = append(list, item)
}
}
if len(list) == 0 {
return nil
}
var data []dto.XPUInfo
for _, gpu := range list {
var dataItem dto.XPUInfo
if err := copier.Copy(&dataItem, &gpu); err != nil {
continue
}
data = append(data, dataItem)
}
return data
}
func GetOutboundIP() string {
conn, err := network.Dial("udp", "8.8.8.8:80")
if err != nil {
return "IPNotFound"
}
defer conn.Close()
localAddr := conn.LocalAddr().(*network.UDPAddr)
return localAddr.IP.String()
}
-97
View File
@@ -1,97 +0,0 @@
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"
)
type GroupService struct{}
type IGroupService interface {
List(req dto.OperateByType) ([]dto.GroupInfo, error)
Create(req dto.GroupCreate) error
Update(req dto.GroupUpdate) error
Delete(id uint) error
GetDefault() (model.Group, error)
}
func NewIGroupService() IGroupService {
return &GroupService{}
}
func (u *GroupService) List(req dto.OperateByType) ([]dto.GroupInfo, error) {
options := []repo.DBOption{
repo.WithOrderBy("is_default desc"),
repo.WithOrderBy("created_at desc"),
}
if len(req.Type) != 0 {
options = append(options, repo.WithByType(req.Type))
}
var (
groups []model.Group
err error
)
groups, err = groupRepo.GetList(options...)
if err != nil {
return nil, buserr.New("ErrRecordNotFound")
}
var dtoUsers []dto.GroupInfo
for _, group := range groups {
var item dto.GroupInfo
if err := copier.Copy(&item, &group); err != nil {
return nil, buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
dtoUsers = append(dtoUsers, item)
}
return dtoUsers, err
}
func (u *GroupService) Create(req dto.GroupCreate) error {
group, _ := groupRepo.Get(repo.WithByName(req.Name), repo.WithByType(req.Type))
if group.ID != 0 {
return buserr.New("ErrRecordExist")
}
if err := copier.Copy(&group, &req); err != nil {
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
if err := groupRepo.Create(&group); err != nil {
return err
}
return nil
}
func (u *GroupService) Delete(id uint) error {
group, _ := groupRepo.Get(repo.WithByID(id))
if group.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
if group.IsDefault {
return buserr.New("ErrGroupIsDefault")
}
if group.Type == "website" {
websites, _ := websiteRepo.List(websiteRepo.WithGroupID(group.ID))
if len(websites) > 0 {
return buserr.New("ErrGroupIsInWebsiteUse")
}
}
return groupRepo.Delete(repo.WithByID(id))
}
func (u *GroupService) Update(req dto.GroupUpdate) error {
if req.IsDefault {
if err := groupRepo.CancelDefault(req.Type); err != nil {
return err
}
}
upMap := make(map[string]interface{})
upMap["name"] = req.Name
upMap["is_default"] = req.IsDefault
return groupRepo.Update(req.ID, upMap)
}
func (u *GroupService) GetDefault() (model.Group, error) {
return groupRepo.Get(groupRepo.WithByDefault(true))
}
-63
View File
@@ -1,63 +0,0 @@
package service
import (
"os"
"sort"
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/global"
)
type LogService struct{}
type ILogService interface {
ListSystemLogFile() ([]string, error)
}
func NewILogService() ILogService {
return &LogService{}
}
func (u *LogService) ListSystemLogFile() ([]string, error) {
var listFile []string
files, err := os.ReadDir(global.Dir.LogDir)
if err != nil {
return nil, err
}
listMap := make(map[string]struct{})
for _, item := range files {
if item.IsDir() || !strings.HasPrefix(item.Name(), "1Panel") {
continue
}
if item.Name() == "1Panel.log" || item.Name() == "1Panel-Core.log" {
itemName := time.Now().Format("2006-01-02")
if _, ok := listMap[itemName]; ok {
continue
}
listMap[itemName] = struct{}{}
listFile = append(listFile, itemName)
continue
}
itemFileName := strings.TrimPrefix(item.Name(), "1Panel-Core-")
itemFileName = strings.TrimPrefix(itemFileName, "1Panel-")
itemFileName = strings.TrimSuffix(itemFileName, ".gz")
itemFileName = strings.TrimSuffix(itemFileName, ".log")
if len(itemFileName) == 0 {
continue
}
if _, ok := listMap[itemFileName]; ok {
continue
}
listMap[itemFileName] = struct{}{}
listFile = append(listFile, itemFileName)
}
if len(listFile) < 2 {
return listFile, nil
}
sort.Slice(listFile, func(i, j int) bool {
return listFile[i] > listFile[j]
})
return listFile, nil
}

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