Compare commits

...
151 Commits
Author SHA1 Message Date
ssongliu 3f26412585 fix: Modify OneDrive default connection settings (#9403) 2025-07-04 09:03:41 +00:00
ssongliu ac99d77fbb fix: Fix snap docker startup failure due to empty configuration (#9402) 2025-07-04 07:21:40 +00:00
ssongliu 4f6f7632f5 fix: Fix the problem of re-logging during the snapshot recovery process (#9401) 2025-07-04 03:59:40 +00:00
CityFun f74248f0d7 fix: Fixed issue with open app log failed with docker-compose (#9400) 2025-07-04 10:23:02 +08:00
ssongliu bb8a15ca56 fix: Fix system load spike during disk mount failures (#9399)
Refs #8827
2025-07-04 02:19:39 +00:00
ssongliu 52432002c7 fix: Fix port acquisition exception issue (#9398) 2025-07-03 16:31:34 +08:00
ssongliu 3fade48f8f fix: Add error ignore functionality to cronjob backups (#9396) 2025-07-03 16:31:25 +08:00
ssongliu fc81801c4c fix: Fix user settings loading failure after reinstallation (#9395) 2025-07-03 14:52:04 +08:00
ssongliu c23c5b2859 style: Add more pagination options to table (#9392)
Refs #5621
2025-07-03 05:55:38 +00:00
ssongliu ccec57e32d fix: Fix user settings loading failure after reinstallation (#9391) 2025-07-03 02:49:37 +00:00
CityFun 56ae5fbf70 fix: Fixed issue with build core failed (#9387) 2025-07-02 23:32:26 +08:00
CityFun 735b44856e style: Fix the problem where dropdown text is too long to display properly. (#9378) 2025-07-02 10:49:34 +00:00
ssongliu 1f6ea9f494 fix: Support docker configuration modifications for snap installations (#9377) 2025-07-02 09:43:35 +00:00
ssongliu afa643519f fix: Modify default values for recommended apps on dashboard (#9375) 2025-07-02 09:41:36 +00:00
ssongliu 1767b84026 fix: Fix overview page data loading/closing exception (#9373) 2025-07-02 09:39:35 +00:00
ssongliu e855e7692e fix: Fix permission errors when running script library as non-root User (#9370) 2025-07-02 04:25:34 +00:00
CityFun 94d37eb68a fix: Resolve the php runtime start failed (#9369) 2025-07-02 04:23:35 +00:00
CityFun e10b19bcde fix: Fixed issue with the mounted directory configuration cannot be displayed in runtime (#9368)
Refs https://github.com/1Panel-dev/1Panel/issues/9293
2025-07-02 04:21:35 +00:00
ssongliu e9192e75ea fix: Fix file existence check when deleting node upgrade records (#9365) 2025-07-01 14:29:33 +00:00
ssongliu 2fa4f68b1e fix: Update backup account documentation links (#9364) 2025-07-01 10:13:32 +00:00
ssongliu 08f53a4a37 fix: Support configurable docker config file snapshotting (#9363) 2025-07-01 07:49:32 +00:00
CityFun c522295c56 fix: Resolve the issue of database uninstallation failure (#9361) 2025-07-01 15:46:21 +08:00
BugKing 7a45548387 feat: add terminal icon for installed App (#9331)
* feat: add terminal icon for installed App

* refactor: 应用卡片打开容器终端,适配多个容器情况
2025-07-01 15:45:57 +08:00
ssongliu 810ea9c421 style: Handle overflow for node names with ellipsis truncation (#9360) 2025-07-01 07:45:32 +00:00
CityFun ba6a4fe6d7 fix: Resolve the issue of lost GPU configuration after upgrade ollama (#9359) 2025-07-01 07:43:32 +00:00
ssongliu 4d144f7a8e fix: Optimize backup account switching logic (#9358) 2025-07-01 06:31:32 +00:00
ssongliu 45c41184c1 fix: Optimize 1pctl restore command output (#9355) 2025-07-01 03:51:33 +00:00
ssongliu 5c29c5e7dd fix: Implement chunked upload for google drive file transfers (#9354) 2025-07-01 03:49:32 +00:00
蘭 4da9a84363 fix: Disable button when clearing cache and selecting size 0B (#9349) 2025-06-30 14:29:31 +00:00
蘭 22d8fb7867 fix: Fix the display error of the sub node website icon (#9348) 2025-06-30 11:11:31 +00:00
ssongliu c7058cb3e6 fix: Optimize container image repo restart detection logic (#9347)
Refs #8978
2025-06-30 11:09:30 +00:00
ssongliu e6092b2658 fix: Fix the compilation exception problem caused by dependencies (#9345) 2025-06-30 08:49:30 +00:00
ssongliu ae19b72e61 feat: Add root/sudo password-free access check for ssh (#9344) 2025-06-30 08:21:30 +00:00
dependabot[bot] da117582d6 build(deps): bump github.com/go-viper/mapstructure/v2 in /agent (#9343)
Bumps [github.com/go-viper/mapstructure/v2](https://github.com/go-viper/mapstructure) from 2.2.1 to 2.3.0.
- [Release notes](https://github.com/go-viper/mapstructure/releases)
- [Changelog](https://github.com/go-viper/mapstructure/blob/main/CHANGELOG.md)
- [Commits](https://github.com/go-viper/mapstructure/compare/v2.2.1...v2.3.0)

---
updated-dependencies:
- dependency-name: github.com/go-viper/mapstructure/v2
  dependency-version: 2.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 12:00:18 +08:00
CityFun c7ca1ccbd7 core: Remove unused code (#9342) 2025-06-30 03:55:30 +00:00
ssongliu 911b5c406d style: Optimize cronjob report page styling (#9341) 2025-06-30 03:53:30 +00:00
wanghe-fit2cloud 71ed4b946a feat: Adjust the logo’s aspect ratio to prevent it from appearing stretched 2025-06-29 23:19:55 +08:00
蘭 4ef9fcde59 fix: Default display of hidden files (#9327)
#9325 
#9324
2025-06-27 10:23:24 +00:00
ssongliu 7889b5ad69 fix: Optimize script-type cronjob execution output (#9326) 2025-06-27 17:57:40 +08:00
ssongliu be6d3ea337 fix: Update compression backup exclusion rules (#9323) 2025-06-27 09:25:23 +00:00
CityFun 64fd715973 perf: optimization php config (#9321) 2025-06-27 16:56:13 +08:00
ssongliu 315f99a03a feat: Add application exclusion for cronjob snapshots (#9320)
Refs #8914
2025-06-27 08:47:26 +00:00
ssongliu 7e1351c543 fix: resolve code warnings and improve code quality (#9322) 2025-06-27 16:47:01 +08:00
CityFun bd3c829653 fix: translate the requestTimeout msg for frontend (#9316)
Refs https://github.com/1Panel-dev/1Panel/issues/9308
2025-06-27 05:49:24 +00:00
蘭 4f6dde02d2 fix: Fix the issue of missing line breaks in copied log content (#9314)
#9099
2025-06-27 05:47:25 +00:00
蘭 8cc7d1f67f fix: Fix the issue of multiple selection moving folder errors (#9317) 2025-06-27 05:45:23 +00:00
ssongliu 2bbba9d755 style: Adjust node option popup interaction (#9312)
Refs #9290
2025-06-27 02:23:23 +00:00
CityFun e9e4755348 Revert "fix: Fix missing line breaks in copied logs on the log page (… (#9311) 2025-06-27 02:19:26 +00:00
CityFun 9fbb63df95 fix: Resolve command execution failures in the PHP runtime environmen… (#9299)
Refs https://github.com/1Panel-dev/1Panel/issues/9286
2025-06-26 08:31:23 +00:00
ssongliu 5b5a649589 fix: resolve Google Drive token refresh failure (#9298)
Refs #9223
2025-06-26 07:37:22 +00:00
蘭 79556affbd fix: Fix the preview image error of child nodes (#9296)
#9245
2025-06-26 06:43:22 +00:00
蘭 9cba19d4db fix: Fix the process status error issue (#9285) 2025-06-25 22:05:29 +08:00
蘭 4f377c04b8 fix: Fix missing line breaks in copied logs on the log page (#9283) 2025-06-25 22:05:12 +08:00
ssongliu 763df5606b fix: Fix snapshot restoration missing website backup detection (#9282) 2025-06-25 22:04:53 +08:00
ssongliu e1d20e3ec9 fix: Optimize snapshot and snapshot recovery mechanism (#9279) 2025-06-25 16:29:15 +08:00
CityFun 215c082a04 perf: add some config for website_default.conf (#9278)
Refs https://github.com/1Panel-dev/1Panel/issues/8837
2025-06-25 08:23:21 +00:00
ssongliu 984985e650 fix: Fix snapshot restoration for container configuration errors (#9277)
Refs #9216
2025-06-25 08:21:21 +00:00
蘭 249b3c3245 fix: Fixed logging errors for uploaded files (#9273) 2025-06-25 08:19:20 +00:00
Swt242 725786d6aa fix: Fixed logging errors for uploaded files (#9204) 2025-06-25 11:46:27 +08:00
蘭 2edddaf487 feat: Specify the interpretation code based on the file content (#9267) 2025-06-24 14:17:14 +00:00
ssongliu 49cbab08c1 fix: Modify snapshot synchronization and import name restrictions (#9266) 2025-06-24 18:43:56 +08:00
ssongliu a75df66151 fix: Rollback snapshot code (#9263) 2025-06-24 10:41:14 +00:00
CityFun 43e9c13c5e perf: Optimize and Edit PHP Runtime Environment Version Switching (#9265) 2025-06-24 18:40:53 +08:00
CityFun 5ad06f121b fix: fix issue with subsite dir error (#9259) 2025-06-24 10:39:14 +00:00
wanghe-fit2cloud 0eaaf7aacb feat: Adjust the logo’s aspect ratio to prevent it from appearing stretched 2025-06-24 17:29:15 +08:00
CityFun dd44fdd1f2 fix: Fixed issue with tar file failed with exist 1 (#9261) 2025-06-24 16:57:27 +08:00
CityFun 669855de70 fix: Fixed the issue that caused the error (bufio.Scanner: token too long) when viewing container log (#9256)
Refs https://github.com/1Panel-dev/1Panel/issues/9241
2025-06-24 05:53:13 +00:00
CityFun 193ae2b7d6 fix: fix issue with enable website ssl with ipv6 failed (#9255) 2025-06-24 13:44:17 +08:00
Alan af4b23e0e0 fix: fixed dashboard operating system platform Version (#9247) 2025-06-24 09:31:14 +08:00
蘭 c35991315d fix: Fix page loading issue with no data status (#9248) 2025-06-23 22:14:48 +08:00
ssongliu 9aa852370c fix: Split snapshot application backup (#9244) 2025-06-23 10:40:13 +00:00
CityFun 8f18497551 feat: add remark for runtime (#9242) 2025-06-23 18:37:37 +08:00
CityFun 4c59951e8c style: change dnsAccount select option style in create ssl page (#9239) 2025-06-23 06:48:12 +00:00
CityFun eb2a80d216 style: hightlight ipv6 address in task log (#9238)
Refs https://github.com/1Panel-dev/1Panel/issues/9118
2025-06-23 06:46:13 +00:00
CityFun 41d0175a6d style: change show diff button style for upgrade app (#9237)
Refs https://github.com/1Panel-dev/1Panel/issues/9207
2025-06-23 06:44:11 +00:00
igophper 2f6aa0f45b fix: Fix docker status flicker issue (#9218) 2025-06-23 13:46:03 +08:00
igophper 17c685bc4c fix: Fix the apps store docker status error (#9217) 2025-06-23 13:44:09 +08:00
ssongliu cf230ca6e7 fix: Fix the problem of the failure of 1pctl update port (#9199) 2025-06-20 06:45:13 +00:00
蘭 bb99853000 fix: File content support GBK encoding (#9198) 2025-06-20 06:43:13 +00:00
ssongliu 0c64c8ff52 fix: Fix the problem of incomplete printing of the cronjob (#9197) 2025-06-20 14:25:35 +08:00
ssongliu e3e2006c19 fix: Solve the problem of failed binding and synchronization of Ali Pan (#9195) 2025-06-20 11:12:49 +08:00
蘭 eddc1b6a8d fix: Fix hidden file flipping issue (#9189) 2025-06-19 11:51:00 +00:00
ssongliu 83e1db723e fix: Optimize the synchronization log information of the script library (#9188) 2025-06-19 10:42:59 +00:00
ssongliu 76c608c703 style: Adjust the style of the cronjob status button (#9185) 2025-06-19 09:49:01 +00:00
CityFun d61a77bec7 feat: add core debug cmd for cpu/mem analysis (#9186) 2025-06-19 17:48:51 +08:00
ssongliu a6a2d9991c fix: Remove the redis password restriction (#9184) 2025-06-19 08:36:59 +00:00
CityFun 060dd08167 fix: fix issue with http3 config not work in proxy website (#9183) 2025-06-19 15:12:29 +08:00
CityFun dd1d93e096 fix: fix issue with docker cmd not close (#9182) 2025-06-19 07:06:59 +00:00
蘭 e4b24f51fc feat: Add cache to query SMS records (#9180) 2025-06-19 07:01:00 +00:00
CityFun e472269a4e feat: add agent debug cmd for cpu/mem analysis (#9181) 2025-06-19 14:59:51 +08:00
CityFun 4032276556 feat: update demo handle (#9175) 2025-06-18 09:58:56 +00:00
ssongliu 420537f092 fix: Fix the problem of abnormal addition of remote connections to th… (#9174)
Refs #9151
2025-06-18 09:20:57 +00:00
蘭 3867e536b2 fix: Fix menu sorting (#9172) 2025-06-18 09:18:57 +00:00
CityFun 4bc7b96ae4 style: change button style (#9171) 2025-06-18 08:48:58 +00:00
ssongliu 9f0338382f feat: Script within the container support the selection of users (#9170)
Refs #8766
2025-06-18 08:46:58 +00:00
蘭 692c77eb1a fix: Service status acquisition compatible with snap (#9169) 2025-06-18 16:45:22 +08:00
CityFun 0778e3e81d feat: add baidu and rynu dns provider for ssl (#9167)
Refs https://github.com/1Panel-dev/1Panel/issues/9160
2025-06-18 07:20:57 +00:00
CityFun 80e217e839 feat: Adopt temporary container adaptation for app store status verification (#9165)
Refs https://github.com/1Panel-dev/1Panel/issues/8973
2025-06-18 07:18:57 +00:00
蘭 47547ad4e1 fix: Fix image loading issue (#9164) 2025-06-18 07:16:57 +00:00
CityFun 8b22acce27 fix: fix issue with open container log failed (#9163) 2025-06-18 07:14:56 +00:00
ssongliu 754ac64bf6 fix: Increase the timeout period of the swap setting (#9162)
Refs #9142
2025-06-18 03:50:56 +00:00
ssongliu 3b37d5ae72 fix: Upgrade the element version to 2.9.9 (#9158) 2025-06-18 03:48:57 +00:00
ssongliu 4e2f24a8f0 fix: Fix the problem of failed Google drive backup (#9156)
Refs #9129
2025-06-17 10:48:55 +00:00
蘭 23a1fbdd4b fix: Clear cache by specifying default values (#9154)
#8933
2025-06-17 10:42:54 +00:00
蘭 795223e7fa fix: Fix the issue of interface image loss caused by clearing cache (#9149)
Refs: #9050
2025-06-17 09:00:54 +00:00
ssongliu 10039c6173 fix: Optimize the script library to synchronize the task log (#9148) 2025-06-17 16:59:34 +08:00
CityFun 146b628cc1 feat: Restrict the size of log files to read. (#9147) 2025-06-17 16:59:23 +08:00
CityFun 0c31fcc8a3 fix: fix issue with local app can not open port (#9145) 2025-06-17 08:36:53 +00:00
蘭 2c290dbbf7 fix: hide menu and sorting (#9141) 2025-06-17 16:14:28 +08:00
ssongliu 0df8a33b9e fix: Fix the problem of failed start of swap in the toolbox (#9143)
Refs #9142
2025-06-17 08:08:53 +00:00
ssongliu 1278e910f5 fix: Fix the issue of abnormal login log recording (#9139)
Refs #9054
2025-06-17 08:06:55 +00:00
ssongliu 429e2c6cbe feat: Optimize the node to add inspection items (#9138) 2025-06-17 08:04:54 +00:00
CityFun 00a5cc654a fix: fix issue with run script with no return (#9137) 2025-06-17 16:04:02 +08:00
蘭 d68a0b076b feat: transfer some code (#9124)
refs #9067
2025-06-17 03:56:53 +00:00
CityFun 99ef8e9310 fix: fix issue with edit dotnet environment failed (#9121)
Refs https://github.com/1Panel-dev/1Panel/issues/9083
2025-06-16 08:50:51 +00:00
Process Xie 5e549fdb71 fix: terminate Docker logs process when client disconnects (#9091)
* Update container.go

* Update container.go
2025-06-16 16:07:13 +08:00
CityFun 8f7b026f0e feat: Add quick installation for PHP extension pcntl (#9119) 2025-06-16 07:38:51 +00:00
CityFun fabf33a83b feat: Ensure compatibility with docker-compose commands. (#9117) 2025-06-16 07:14:51 +00:00
CityFun 211c956c4b fix: Fix input field validation error when editing installed application (#9115) 2025-06-16 06:44:51 +00:00
CityFun 55d966a52e fix: fix issue with php install swoole failed (#9113)
Refs https://github.com/1Panel-dev/1Panel/issues/9080
2025-06-16 06:42:51 +00:00
ssongliu a0d5319cd9 fix: Modify the license binding node filtering (#9111) 2025-06-16 06:40:52 +00:00
ssongliu d9358b87c4 style: Optimize the closing method of the right drawer (#9108) 2025-06-16 06:38:51 +00:00
CityFun 89526d9075 feat: add docker status for appstore (#9058) 2025-06-13 10:00:42 +00:00
ssongliu b1799d8c06 style: Modify the prompt message for License exists (#9057) 2025-06-13 07:42:42 +00:00
ssongliu 185eaf1211 style: Optimize the internationalization of the cronjob report (#9055) 2025-06-13 07:26:42 +00:00
CityFun 4aa7dbfa9f feat: change runtime timezone helper (#9053) 2025-06-13 03:46:41 +00:00
ssongliu daef59b568 fix: The snapshot increases the number of retries (#9045)
Refs #9026
2025-06-12 14:48:41 +00:00
ssongliu be2c30c9e0 fix: Fix the problem of abnormal reserved copies for cronjob (#9044)
Refs #9042
2025-06-12 14:46:41 +00:00
蘭 3b166801d6 fix: Fix the issue of homepage color after switching themes (#9041) 2025-06-12 10:54:39 +00:00
igophper c289a5f14f fix: Remove proxy settings from Google Drive client (#9040)
Refs #9034
2025-06-12 18:20:21 +08:00
蘭 5dfb4def56 feat: Add AI website building menu (#9039) 2025-06-12 09:04:40 +00:00
蘭 c6d70f4dc8 refactor: update docs url (#9038) 2025-06-12 07:34:40 +00:00
CityFun 254a6e6dbf perf: remove "TLSv1.1", "TLSv1" for create website (#9033)
Refs https://github.com/1Panel-dev/1Panel/issues/9030
2025-06-12 05:52:39 +00:00
蘭 62c010ce13 fix: Fixed the restart panel theme switching issue (#9031) 2025-06-12 13:33:02 +08:00
CityFun 272536a3b5 fix: fix issue with build frontend failed (#9023) 2025-06-11 14:54:37 +00:00
蘭 13dc94aca5 feat: Create a container and use a terminal to execute commands (#9022)
Refs: #8795
2025-06-11 13:38:38 +00:00
蘭 b9a80f15e6 refactor: update docs url (#9021) 2025-06-11 13:36:38 +00:00
david082321 5cab80d842 feat: Update zh-Hant translation (#9020)
* Update zh-Hant.yaml

* Update zh-Hant.yaml
2025-06-11 21:34:12 +08:00
John Niang 8a4ff3c809 Refine English translation for v2 (#9016) 2025-06-11 17:53:13 +08:00
CityFun 5b9e13f35b fxo: fix issue with get php runtime version failed (#9017) 2025-06-11 17:52:43 +08:00
John Niang fd5437139b Revert "Refine English translation (#8991)" (#9015)
This reverts commit f843438baf.
2025-06-11 17:03:13 +08:00
CityFun 26852b6676 feat: add reg.ru DNS provider for website ssl (#9014)
Refs https://github.com/1Panel-dev/1Panel/issues/8889
2025-06-11 08:02:36 +00:00
CityFun b8a02cc094 update go module (#9013) 2025-06-11 06:52:37 +00:00
CityFun f7fa6cf567 fix: fix issue with clean supervisor log failed (#9012) 2025-06-11 06:44:37 +00:00
CityFun 64e74e87c4 feat: Add directory redirection on the PHP runtime environment page (#9010) 2025-06-11 06:42:39 +00:00
John Niang f843438baf Refine English translation (#8991)
* Refine English translation (#7232)

* Refine English translation  (#7247)

* Refine pluralization of some English words (#7318)

* Refine plural translation of English words

* Remove useless i18n modifier

* Refine English translation again and again (#7353)

* Replace whitelist and blacklist with allowlist and blocklist (#7358)

* Refine English translation and UI style (#7399)

* Resolve build error

* Fix build error
2025-06-11 14:41:29 +08:00
CityFun 4528f1feaf feat: update docs url (#8997) 2025-06-10 10:30:35 +00:00
ssongliu 1eae519e1a fix: Fix the problem of failed backup of all databases for cronjob (#8990)
Refs #8982
2025-06-10 06:32:34 +00:00
ssongliu 1416fb0f6e fix: Fix the problem of abnormal acquisition of system update versions (#8988) 2025-06-10 06:30:35 +00:00
CityFun e9257735ba fix: fix issue with change ssl_protocols failed (#8985)
Refs https://github.com/1Panel-dev/1Panel/issues/8981
2025-06-10 06:28:34 +00:00
Halo Dev BotandJohn Niang 81caf126f7 Fix invalid postion of before content in home page in Firefox Linux (#8980)
Co-authored-by: John Niang <johnniang@foxmail.com>
2025-06-10 14:25:55 +08:00
297 changed files with 6082 additions and 3566 deletions
+23
View File
@@ -35,6 +35,29 @@ func (b *BaseApi) SearchContainer(c *gin.Context) {
})
}
// @Tags Container
// @Summary Load container users
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Produce json
// @Success 200 {array} string
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/users [post]
func (b *BaseApi) LoadContainerUsers(c *gin.Context) {
var req dto.OperationWithName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
list, err := containerService.LoadUsers(req)
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Container
// @Summary List containers
// @Accept json
+1 -1
View File
@@ -296,7 +296,7 @@ func (b *BaseApi) SaveContent(c *gin.Context) {
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /files/upload [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传文件 [path]","formatEN":"Upload file [path]"}
// @x-panel-log {"bodyKeys":["path", "file"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传文件 [path]/[file]","formatEN":"Upload file [path]/[file]"}
func (b *BaseApi) UploadFiles(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
+8 -8
View File
@@ -13,7 +13,7 @@ import (
// @Success 200 {object} response.HostToolRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/tool [post]
// @Router /hosts/tool [post]
func (b *BaseApi) GetToolStatus(c *gin.Context) {
var req request.HostToolReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -35,7 +35,7 @@ func (b *BaseApi) GetToolStatus(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/tool/create [post]
// @Router /hosts/tool/create [post]
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 [type] 配置","formatEN":"create [type] config"}
func (b *BaseApi) InitToolConfig(c *gin.Context) {
var req request.HostToolCreate
@@ -57,7 +57,7 @@ func (b *BaseApi) InitToolConfig(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/tool/operate [post]
// @Router /hosts/tool/operate [post]
// @x-panel-log {"bodyKeys":["operate","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] [type] ","formatEN":"[operate] [type]"}
func (b *BaseApi) OperateTool(c *gin.Context) {
var req request.HostToolReq
@@ -79,7 +79,7 @@ func (b *BaseApi) OperateTool(c *gin.Context) {
// @Success 200 {object} response.HostToolConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/tool/config [post]
// @Router /hosts/tool/config [post]
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机工具配置文件 ","formatEN":"[operate] tool config"}
func (b *BaseApi) OperateToolConfig(c *gin.Context) {
var req request.HostToolConfig
@@ -102,7 +102,7 @@ func (b *BaseApi) OperateToolConfig(c *gin.Context) {
// @Success 200 {string} logContent
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/tool/log [post]
// @Router /hosts/tool/log [post]
func (b *BaseApi) GetToolLog(c *gin.Context) {
var req request.HostToolLogReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -124,7 +124,7 @@ func (b *BaseApi) GetToolLog(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/tool/supervisor/process [post]
// @Router /hosts/tool/supervisor/process [post]
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 守护进程 ","formatEN":"[operate] process"}
func (b *BaseApi) OperateProcess(c *gin.Context) {
var req request.SupervisorProcessConfig
@@ -146,7 +146,7 @@ func (b *BaseApi) OperateProcess(c *gin.Context) {
// @Success 200 {object} response.SupervisorProcessConfig
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/tool/supervisor/process [get]
// @Router /hosts/tool/supervisor/process [get]
func (b *BaseApi) GetProcess(c *gin.Context) {
configs, err := hostToolService.GetSupervisorProcessConfig()
if err != nil {
@@ -163,7 +163,7 @@ func (b *BaseApi) GetProcess(c *gin.Context) {
// @Success 200 {string} content
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/tool/supervisor/process/file [post]
// @Router /hosts/tool/supervisor/process/file [post]
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] Supervisor 进程文件 ","formatEN":"[operate] Supervisor Process Config file"}
func (b *BaseApi) GetProcessFile(c *gin.Context) {
var req request.SupervisorProcessFileReq
+4 -4
View File
@@ -98,20 +98,20 @@ func (b *BaseApi) CreateRepo(c *gin.Context) {
// @Tags Container Image-repo
// @Summary Delete image repo
// @Accept json
// @Param request body dto.ImageRepoDelete true "request"
// @Param request body dto.OperateByID true "request"
// @Produce json
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /containers/repo/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"image_repos","output_column":"name","output_value":"names"}],"formatZH":"删除镜像仓库 [names]","formatEN":"delete image repo [names]"}
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"image_repos","output_column":"name","output_value":"name"}],"formatZH":"删除镜像仓库 [name]","formatEN":"delete image repo [name]"}
func (b *BaseApi) DeleteRepo(c *gin.Context) {
var req dto.ImageRepoDelete
var req dto.OperateByID
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := imageRepoService.BatchDelete(req); err != nil {
if err := imageRepoService.Delete(req); err != nil {
helper.InternalServer(c, err)
return
}
+20
View File
@@ -535,3 +535,23 @@ func (b *BaseApi) GetPHPContainerConfig(c *gin.Context) {
}
helper.SuccessWithData(c, data)
}
// @Tags Runtime
// @Summary Update runtime remark
// @Accept json
// @Param request body request.RuntimeRemarkUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /runtimes/remark [post]
func (b *BaseApi) UpdateRuntimeRemark(c *gin.Context) {
var req request.RuntimeRemark
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := runtimeService.UpdateRemark(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.Success(c)
}
+8 -8
View File
@@ -11,7 +11,7 @@ import (
// @Success 200 {object} dto.SSHInfo
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/ssh/search [post]
// @Router /hosts/ssh/search [post]
func (b *BaseApi) GetSSHInfo(c *gin.Context) {
info, err := sshService.GetSSHInfo()
if err != nil {
@@ -28,7 +28,7 @@ func (b *BaseApi) GetSSHInfo(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/ssh/operate [post]
// @Router /hosts/ssh/operate [post]
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] SSH ","formatEN":"[operation] SSH"}
func (b *BaseApi) OperateSSH(c *gin.Context) {
var req dto.Operate
@@ -50,7 +50,7 @@ func (b *BaseApi) OperateSSH(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/ssh/update [post]
// @Router /hosts/ssh/update [post]
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置 [key] => [value]","formatEN":"update SSH setting [key] => [value]"}
func (b *BaseApi) UpdateSSH(c *gin.Context) {
var req dto.SSHUpdate
@@ -72,7 +72,7 @@ func (b *BaseApi) UpdateSSH(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/conffile/update [post]
// @Router /hosts/conffile/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置文件","formatEN":"update SSH conf"}
func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
var req dto.SSHConf
@@ -94,7 +94,7 @@ func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/ssh/generate [post]
// @Router /hosts/ssh/generate [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
func (b *BaseApi) GenerateSSH(c *gin.Context) {
var req dto.GenerateSSH
@@ -116,7 +116,7 @@ func (b *BaseApi) GenerateSSH(c *gin.Context) {
// @Success 200 {string} secret
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/ssh/secret [post]
// @Router /hosts/ssh/secret [post]
func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
var req dto.GenerateLoad
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -138,7 +138,7 @@ func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
// @Success 200 {object} dto.SSHLog
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/ssh/log [post]
// @Router /hosts/ssh/log [post]
func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
var req dto.SearchSSHLog
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -158,7 +158,7 @@ func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
// @Success 200 {string} conf
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /host/ssh/conf [get]
// @Router /hosts/ssh/conf [get]
func (b *BaseApi) LoadSSHConf(c *gin.Context) {
data, err := sshService.LoadSSHConf()
if err != nil {
+30 -21
View File
@@ -29,27 +29,34 @@ type CronjobOperate struct {
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"`
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"`
SnapshotRule SnapshotRule `json:"snapshotRule"`
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"`
IgnoreErr bool `json:"ignoreErr"`
Secret string `json:"secret"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
}
type SnapshotRule struct {
WithImage bool `json:"withImage"`
IgnoreAppIDs []uint `json:"ignoreAppIDs"`
}
type CronjobUpdateStatus struct {
ID uint `json:"id" validate:"required"`
Status string `json:"status" validate:"required"`
@@ -87,18 +94,20 @@ type CronjobInfo struct {
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"`
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"`
IgnoreErr bool `json:"ignoreErr"`
SnapshotRule SnapshotRule `json:"snapshotRule"`
SourceAccounts []string `json:"sourceAccounts"`
DownloadAccount string `json:"downloadAccount"`
+4 -4
View File
@@ -6,8 +6,8 @@ type ImageRepoCreate struct {
Name string `json:"name" validate:"required"`
DownloadUrl string `json:"downloadUrl"`
Protocol string `json:"protocol"`
Username string `json:"username" validate:"max=256"`
Password string `json:"password" validate:"max=256"`
Username string `json:"username"`
Password string `json:"password"`
Auth bool `json:"auth"`
}
@@ -15,8 +15,8 @@ type ImageRepoUpdate struct {
ID uint `json:"id"`
DownloadUrl string `json:"downloadUrl"`
Protocol string `json:"protocol"`
Username string `json:"username" validate:"max=256"`
Password string `json:"password" validate:"max=256"`
Username string `json:"username"`
Password string `json:"password"`
Auth bool `json:"auth"`
}
+6 -5
View File
@@ -92,11 +92,12 @@ type FileWget struct {
}
type FileMove struct {
Type string `json:"type" validate:"required"`
OldPaths []string `json:"oldPaths" validate:"required"`
NewPath string `json:"newPath" validate:"required"`
Name string `json:"name"`
Cover bool `json:"cover"`
Type string `json:"type" validate:"required"`
OldPaths []string `json:"oldPaths" validate:"required"`
NewPath string `json:"newPath" validate:"required"`
Name string `json:"name"`
Cover bool `json:"cover"`
CoverPaths []string `json:"coverPaths"`
}
type FileDownload struct {
+26 -17
View File
@@ -12,15 +12,17 @@ type RuntimeSearch struct {
}
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"`
AppDetailID uint `json:"appDetailId"`
Name string `json:"name"`
Resource string `json:"resource"`
Image string `json:"image"`
Type string `json:"type"`
Version string `json:"version"`
Source string `json:"source"`
CodeDir string `json:"codeDir"`
Remark string `json:"remark"`
Params map[string]interface{} `json:"params"`
NodeConfig
}
@@ -53,14 +55,16 @@ type RuntimeDelete struct {
}
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"`
Name string `json:"name"`
ID uint `json:"id"`
Image string `json:"image"`
Version string `json:"version"`
Rebuild bool `json:"rebuild"`
Source string `json:"source"`
CodeDir string `json:"codeDir"`
Remark string `json:"remark"`
Params map[string]interface{} `json:"params"`
NodeConfig
}
@@ -131,3 +135,8 @@ type PHPContainerConfig struct {
Environments []Environment `json:"environments"`
Volumes []Volume `json:"volumes"`
}
type RuntimeRemark struct {
ID uint `json:"id" validate:"required"`
Remark string `json:"remark"`
}
+2
View File
@@ -32,6 +32,8 @@ type WebsiteCreate struct {
TaskID string `json:"taskID"`
ParentWebsiteID uint `json:"parentWebsiteID"`
SiteDir string `json:"siteDir"`
RuntimeConfig
FtpConfig
DataBaseConfig
+1
View File
@@ -53,6 +53,7 @@ type ExistFileInfo struct {
Path string `json:"path"`
Size int64 `json:"size"`
ModTime time.Time `json:"modTime"`
IsDir bool `json:"isDir"`
}
type UserInfo struct {
+2
View File
@@ -30,6 +30,7 @@ type RuntimeDTO struct {
Volumes []request.Volume `json:"volumes"`
ContainerStatus string `json:"containerStatus"`
Container string `json:"container"`
Remark string `json:"remark"`
}
type PackageScripts struct {
@@ -53,6 +54,7 @@ func NewRuntimeDTO(runtime model.Runtime) RuntimeDTO {
Port: runtime.Port,
Path: runtime.GetPath(),
Container: runtime.ContainerName,
Remark: runtime.Remark,
}
}
-2
View File
@@ -23,8 +23,6 @@ type SettingInfo struct {
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
FileRecycleBin string `json:"fileRecycleBin"`
SnapshotIgnore string `json:"snapshotIgnore"`
}
type SettingUpdate struct {
+10 -5
View File
@@ -23,11 +23,14 @@ type SnapshotCreate struct {
BackupData []DataTree `json:"backupData"`
PanelData []DataTree `json:"panelData"`
WithDockerConf bool `json:"withDockerConf"`
WithMonitorData bool `json:"withMonitorData"`
WithLoginLog bool `json:"withLoginLog"`
WithOperationLog bool `json:"withOperationLog"`
WithSystemLog bool `json:"withSystemLog"`
WithTaskLog bool `json:"withTaskLog"`
IgnoreFiles []string `json:"ignoreFiles"`
}
type SnapshotData struct {
@@ -35,11 +38,13 @@ type SnapshotData struct {
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"`
WithDockerConf bool `json:"withDockerConf"`
WithMonitorData bool `json:"withMonitorData"`
WithLoginLog bool `json:"withLoginLog"`
WithOperationLog bool `json:"withOperationLog"`
WithSystemLog bool `json:"withSystemLog"`
WithTaskLog bool `json:"withTaskLog"`
IgnoreFiles []string `json:"ignoreFiles"`
}
type DataTree struct {
ID string `json:"id"`
+2
View File
@@ -29,12 +29,14 @@ type Cronjob struct {
URL string `json:"url"`
IsDir bool `json:"isDir"`
SourceDir string `json:"sourceDir"`
SnapshotRule string `json:"snapshotRule"`
ExclusionRules string `json:"exclusionRules"`
SourceAccountIDs string `json:"sourceAccountIDs"`
DownloadAccountID uint `json:"downloadAccountID"`
RetryTimes uint `json:"retryTimes"`
Timeout uint `json:"timeout"`
IgnoreErr bool `json:"ignoreErr"`
RetainCopies uint64 `json:"retainCopies"`
Status string `json:"status"`
+1
View File
@@ -23,6 +23,7 @@ type Runtime struct {
Message string `json:"message"`
CodeDir string `json:"codeDir"`
ContainerName string `json:"containerName"`
Remark string `json:"remark"`
}
func (r *Runtime) GetComposePath() string {
+6 -3
View File
@@ -15,14 +15,17 @@ type Snapshot struct {
TaskRecoverID string `json:"taskRecoverID"`
TaskRollbackID string `json:"taskRollbackID"`
AppData string `json:"appData"`
PanelData string `json:"panelData"`
BackupData string `json:"backupData"`
AppData string `json:"appData"`
PanelData string `json:"panelData"`
BackupData string `json:"backupData"`
WithDockerConf bool `json:"withDockerConf"`
WithMonitorData bool `json:"withMonitorData"`
WithLoginLog bool `json:"withLoginLog"`
WithOperationLog bool `json:"withOperationLog"`
WithSystemLog bool `json:"withSystemLog"`
WithTaskLog bool `json:"withTaskLog"`
IgnoreFiles string `json:"ignoreFiles"`
InterruptStep string `json:"interruptStep"`
RecoverStatus string `json:"recoverStatus"`
+1 -1
View File
@@ -108,7 +108,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (interface{
lang := strings.ToLower(common.GetLang(ctx))
for _, ap := range apps {
if req.Type == "php" {
if ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(fmt.Sprintf("%f", ap.RequiredPanelVersion), common.GetSystemVersion(info.SystemVersion)) {
if ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion)) {
continue
}
}
+24 -22
View File
@@ -14,29 +14,25 @@ import (
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
"github.com/docker/docker/api/types"
"gopkg.in/yaml.v3"
"github.com/1Panel-dev/1Panel/agent/utils/env"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/joho/godotenv"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/compose"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/env"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
"github.com/docker/docker/api/types/container"
"github.com/joho/godotenv"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
)
type AppInstallService struct {
@@ -124,7 +120,7 @@ func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []respo
}
}
installDTOs, err := handleInstalled(installs, req.Update, req.Sync)
installDTOs, _ := handleInstalled(installs, req.Update, req.Sync)
if req.Update {
total = int64(len(installDTOs))
}
@@ -635,10 +631,14 @@ func (a *AppInstallService) DeleteCheck(installID uint) ([]dto.AppResource, erro
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(appInstall.ID), repo.WithByFrom(constant.AppResourceLocal))
for _, resource := range resources {
linkInstall, _ := appInstallRepo.GetFirst(repo.WithByID(resource.AppInstallId))
res = append(res, dto.AppResource{
Type: "app",
Name: linkInstall.Name,
})
if linkInstall.ID > 0 {
res = append(res, dto.AppResource{
Type: "app",
Name: linkInstall.Name,
})
} else {
_ = appInstallResourceRepo.DeleteBy(context.Background(), appInstallResourceRepo.WithAppInstallId(resource.AppInstallId))
}
}
return res, nil
}
@@ -703,6 +703,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
Rule: form.Rule,
Type: form.Type,
Multiple: form.Multiple,
Required: form.Required,
}
if form.Edit {
appParam.Edit = true
@@ -761,6 +762,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
Value: form.Default,
Values: form.Values,
Multiple: form.Multiple,
Required: form.Required,
})
}
}
@@ -789,15 +791,15 @@ func syncAppInstallStatus(appInstall *model.AppInstall, force bool) error {
defer cli.Close()
var (
containers []types.Container
containersMap map[string]types.Container
containers []container.Summary
containersMap map[string]container.Summary
containerNames = strings.Split(appInstall.ContainerName, ",")
)
containers, err = cli.ListContainersByName(containerNames)
if err != nil {
return err
}
containersMap = make(map[string]types.Container)
containersMap = make(map[string]container.Summary)
for _, con := range containers {
containersMap[con.Names[0]] = con
}
+79 -109
View File
@@ -18,41 +18,33 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/docker/docker/api/types"
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
"github.com/docker/docker/api/types/container"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/subosito/gotenv"
"gopkg.in/yaml.v3"
"github.com/1Panel-dev/1Panel/agent/utils/env"
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"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/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/docker"
composeV2 "github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/1Panel-dev/1Panel/agent/utils/env"
"github.com/1Panel-dev/1Panel/agent/utils/files"
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/docker/api/types/container"
"github.com/pkg/errors"
"github.com/subosito/gotenv"
"gopkg.in/yaml.v3"
)
type DatabaseOp string
@@ -387,21 +379,25 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
}
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
if deleteReq.DeleteDB && len(resources) > 0 {
del := dto.DelAppLink{
Ctx: ctx,
Install: &install,
ForceDelete: deleteReq.ForceDelete,
Task: uninstallTask,
}
t.LogWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
if err = deleteLink(del); err != nil {
t.LogFailedWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"), err)
if !deleteReq.ForceDelete {
return err
if len(resources) > 0 {
if deleteReq.DeleteDB {
del := dto.DelAppLink{
Ctx: ctx,
Install: &install,
ForceDelete: deleteReq.ForceDelete,
Task: uninstallTask,
}
t.LogWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
if err = deleteLink(del); err != nil {
t.LogFailedWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"), err)
if !deleteReq.ForceDelete {
return err
}
}
t.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
} else {
_ = appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
}
t.LogSuccessWithOps(task.TaskDelete, i18n.GetMsgByKey("Database"))
}
if DatabaseKeys[install.App.Key] > 0 {
@@ -490,17 +486,14 @@ func deleteLink(del dto.DelAppLink) error {
return appInstallResourceRepo.DeleteBy(del.Ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
}
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
if detail.DockerCompose == "" {
return "", nil
}
func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map[string]interface{}, error) {
composeMap := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); err != nil {
return "", err
return nil, err
}
value, ok := composeMap["services"]
if !ok || value == nil {
return "", buserr.New("ErrFileParse")
return nil, buserr.New("ErrFileParse")
}
servicesMap := value.(map[string]interface{})
if len(servicesMap) == 1 {
@@ -518,6 +511,34 @@ func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string
delete(servicesMap, oldServiceName)
}
}
serviceValue := servicesMap[install.ServiceName].(map[string]interface{})
oldComposeMap := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(install.DockerCompose), &oldComposeMap); err != nil {
return nil, err
}
oldValue, ok := oldComposeMap["services"]
if !ok || oldValue == nil {
return nil, buserr.New("ErrFileParse")
}
oldValueMap := oldValue.(map[string]interface{})
oldServiceValue := oldValueMap[install.ServiceName].(map[string]interface{})
if oldServiceValue["deploy"] != nil {
serviceValue["deploy"] = oldServiceValue["deploy"]
}
servicesMap[install.ServiceName] = serviceValue
composeMap["services"] = servicesMap
return composeMap, nil
}
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
if detail.DockerCompose == "" {
return "", nil
}
composeMap, err := handleUpgradeCompose(install, detail)
if err != nil {
return "", err
}
envs := make(map[string]interface{})
if err := json.Unmarshal([]byte(install.Env), &envs); err != nil {
return "", err
@@ -618,7 +639,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
if err != nil {
return err
}
dockerCLi, err := docker.NewClient()
dockerCLi, _ := docker.NewClient()
if req.PullImage {
images, err := composeV2.GetDockerComposeImagesV2(content, []byte(detail.DockerCompose))
if err != nil {
@@ -675,63 +696,6 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
}
envParams := make(map[string]string, len(envs))
handleMap(envs, envParams)
if install.App.Key == "openresty" && install.App.Resource == "remote" && !common.CompareVersion(install.Version, "1.25.3.2-0-1") {
t.Log(i18n.GetMsgByKey("MoveSiteDir"))
siteDir := path.Join(global.Dir.DataDir, "www")
envParams["WEBSITE_DIR"] = siteDir
oldSiteDir := path.Join(install.GetPath(), "www")
t.Log(i18n.GetWithName("MoveSiteToDir", siteDir))
if err := fileOp.CopyDir(oldSiteDir, global.Dir.DataDir); err != nil {
t.Log(i18n.GetMsgByKey("ErrMoveSiteDir"))
return err
}
newConfDir := path.Join(global.Dir.DataDir, "www", "conf.d")
_ = fileOp.CreateDir(newConfDir, constant.DirPerm)
oldConfDir := path.Join(install.GetPath(), "conf/conf.d")
items, err := os.ReadDir(oldConfDir)
if err != nil {
return err
}
for _, item := range items {
itemPath := path.Join(oldConfDir, item.Name())
if item.IsDir() {
_ = fileOp.Mv(itemPath, newConfDir)
}
if item.Name() != "default.conf" && item.Name() != "00.default.conf" {
_ = fileOp.Mv(itemPath, newConfDir)
}
}
nginxConfPath := path.Join(install.GetPath(), "conf", "nginx.conf")
parse, err := parser.NewParser(nginxConfPath)
if err != nil {
return err
}
config, err := parse.Parse()
if err != nil {
return err
}
config.FilePath = nginxConfPath
httpDirective := config.FindHttp()
httpDirective.UpdateDirective("include", []string{"/usr/local/openresty/nginx/conf/default/*.conf"})
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr("ErrUpdateBuWebsite", err)
}
t.Log(i18n.GetMsgByKey("DeleteRuntimePHP"))
_ = fileOp.DeleteDir(path.Join(global.Dir.RuntimeDir, "php"))
websites, _ := websiteRepo.List(repo.WithByType("runtime"))
for _, website := range websites {
runtime, _ := runtimeRepo.GetFirst(context.Background(), repo.WithByID(website.RuntimeID))
if runtime != nil && runtime.Type == "php" {
website.Type = constant.Static
website.RuntimeID = 0
_ = websiteRepo.SaveWithoutCtx(&website)
}
}
_ = runtimeRepo.DeleteBy(repo.WithByType("php"))
t.Log(i18n.GetMsgByKey("MoveSiteDirSuccess"))
}
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
return err
}
@@ -786,6 +750,15 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
return appInstallRepo.Save(context.Background(), &install)
}
func skipCheckStatus(service types.ServiceConfig) bool {
for key := range service.Labels {
if key == "skipStatusCheck" {
return true
}
}
return false
}
func getContainerNames(install model.AppInstall) ([]string, error) {
envStr, err := coverEnvJsonToStr(install.Env)
if err != nil {
@@ -797,7 +770,7 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
}
containerMap := make(map[string]struct{})
for _, service := range project.AllServices() {
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" {
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" || skipCheckStatus(service) {
continue
}
containerMap[service.ContainerName] = struct{}{}
@@ -1363,7 +1336,7 @@ func doNotNeedSync(installed model.AppInstall) bool {
installed.Status == constant.StatusSyncing || installed.Status == constant.StatusUninstalling || installed.Status == constant.StatusInstallErr
}
func synAppInstall(containers map[string]types.Container, appInstall *model.AppInstall, force bool) {
func synAppInstall(containers map[string]container.Summary, appInstall *model.AppInstall, force bool) {
oldStatus := appInstall.Status
containerNames := strings.Split(appInstall.ContainerName, ",")
if len(containers) == 0 {
@@ -1437,7 +1410,7 @@ func synAppInstall(containers map[string]types.Container, appInstall *model.AppI
func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool) ([]response.AppInstallDTO, error) {
var (
res []response.AppInstallDTO
containersMap map[string]types.Container
containersMap map[string]container.Summary
)
if sync {
cli, err := docker.NewClient()
@@ -1445,7 +1418,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
defer cli.Close()
containers, err := cli.ListAllContainers()
if err == nil {
containersMap = make(map[string]types.Container, len(containers))
containersMap = make(map[string]container.Summary, len(containers))
for _, contain := range containers {
containersMap[contain.Names[0]] = contain
}
@@ -1794,10 +1767,7 @@ func ignoreUpdate(installed model.AppInstall) bool {
return true
}
ignores, _ := appIgnoreUpgradeRepo.List(appDetailRepo.WithAppId(installed.AppId), appIgnoreUpgradeRepo.WithScope("all"))
if len(ignores) > 0 {
return true
}
return false
return len(ignores) > 0
}
func RequestDownloadCallBack(downloadCallBackUrl string) {
-2
View File
@@ -290,8 +290,6 @@ func (u *BackupService) RefreshToken(req dto.OperateByID) 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)
}
+1 -2
View File
@@ -5,7 +5,6 @@ import (
"fmt"
"os"
"path"
"strings"
"sync"
"github.com/1Panel-dev/1Panel/agent/app/dto"
@@ -194,7 +193,7 @@ func (u *BackupRecordService) ListFiles(req dto.OperateByID) []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.")) {
if len(file) != 0 && checkSnapshotIsOk(fileName) {
datas = append(datas, path.Base(file))
}
}
+1 -4
View File
@@ -639,10 +639,7 @@ func handleAlert(stdout, clamName string, clamId uint) {
EntryID: clamId,
Param: strconv.Itoa(infectedFiles),
}
err := xpack.PushAlert(pushAlert)
if err != nil {
global.LOG.Errorf("clamdscan push failed, err: %v", err)
}
_ = xpack.PushAlert(pushAlert)
break
}
}
+110 -46
View File
@@ -33,7 +33,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
@@ -83,6 +83,8 @@ type IContainerService interface {
ComposeUpdate(req dto.ComposeUpdate) error
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
LoadUsers(req dto.OperationWithName) ([]string, error)
StreamLogs(ctx *gin.Context, params dto.StreamLog)
}
@@ -92,8 +94,8 @@ func NewIContainerService() IContainerService {
func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, error) {
var (
records []types.Container
list []types.Container
records []container.Summary
list []container.Summary
)
client, err := docker.NewDockerClient()
if err != nil {
@@ -163,7 +165,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
records = make([]types.Container, 0)
records = make([]container.Summary, 0)
} else {
if end >= total {
end = total
@@ -305,7 +307,7 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
var wg sync.WaitGroup
wg.Add(len(list))
for i := 0; i < len(list); i++ {
go func(item types.Container) {
go func(item container.Summary) {
datas = append(datas, loadCpuAndMem(client, item.ID))
wg.Done()
}(list[i])
@@ -464,7 +466,7 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneRepo
report.DeletedNumber = len(rep.VolumesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
case "buildcache":
opts := types.BuildCachePruneOptions{}
opts := build.CachePruneOptions{}
opts.All = true
rep, err := client.BuildCachePrune(context.Background(), opts)
if err != nil {
@@ -882,16 +884,20 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
func (u *ContainerService) StreamLogs(ctx *gin.Context, params dto.StreamLog) {
messageChan := make(chan string, 1024)
errorChan := make(chan error, 1)
doneChan := make(chan struct{})
go collectLogs(params, messageChan, errorChan)
go func() {
<-ctx.Request.Context().Done()
close(doneChan)
}()
go collectLogs(doneChan, params, messageChan, errorChan)
ctx.Stream(func(w io.Writer) bool {
select {
case msg, ok := <-messageChan:
if !ok {
return msg == ""
return false
}
_, err := fmt.Fprintf(w, "data: %v\n\n", msg)
_, err := fmt.Fprintf(w, "data: %s\n\n", msg)
if err != nil {
return false
}
@@ -900,21 +906,15 @@ func (u *ContainerService) StreamLogs(ctx *gin.Context, params dto.StreamLog) {
if err != nil {
_, _ = fmt.Fprintf(w, "event: error\ndata: %v\n\n", err.Error())
}
return true
case <-ctx.Request.Context().Done():
return false
}
})
}
func collectLogs(params dto.StreamLog, messageChan chan<- string, errorChan chan<- error) {
func collectLogs(done <-chan struct{}, params dto.StreamLog, messageChan chan<- string, errorChan chan<- error) {
defer close(messageChan)
defer close(errorChan)
var cmdArgs []string
if params.Type == "compose" {
cmdArgs = []string{"compose", "-f", params.Compose}
}
cmdArgs = append(cmdArgs, "logs")
if params.Follow {
cmdArgs = append(cmdArgs, "-f")
@@ -928,36 +928,85 @@ func collectLogs(params dto.StreamLog, messageChan chan<- string, errorChan chan
if params.Container != "" {
cmdArgs = append(cmdArgs, params.Container)
}
dockerCmd := exec.Command("docker", cmdArgs...)
var dockerCmd *exec.Cmd
if params.Type == "compose" {
dockerComposCmd := common.GetDockerComposeCommand()
if dockerComposCmd == "docker-compose" {
newCmdArgs := append([]string{"-f", params.Compose}, cmdArgs...)
dockerCmd = exec.Command(dockerComposCmd, newCmdArgs...)
} else {
newCmdArgs := append([]string{"compose", "-f", params.Compose}, cmdArgs...)
dockerCmd = exec.Command("docker", newCmdArgs...)
}
} else {
dockerCmd = exec.Command("docker", cmdArgs...)
}
dockerCmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
stdout, err := dockerCmd.StdoutPipe()
if err != nil {
errorChan <- fmt.Errorf("failed to get stdout pipe: %v", err)
return
}
dockerCmd.Stderr = dockerCmd.Stdout
if err = dockerCmd.Start(); err != nil {
errorChan <- fmt.Errorf("failed to start command: %v", err)
errorChan <- fmt.Errorf("failed to start docker logs command: %v", err)
return
}
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
message := scanner.Text()
defer func() {
if dockerCmd.Process != nil {
if pgid, err := syscall.Getpgid(dockerCmd.Process.Pid); err == nil {
_ = syscall.Kill(-pgid, syscall.SIGKILL)
}
_ = dockerCmd.Process.Kill()
_ = dockerCmd.Wait()
}
}()
reader := bufio.NewReader(stdout)
processKilled := false
go func() {
<-done
if !processKilled && dockerCmd.Process != nil {
processKilled = true
if pgid, err := syscall.Getpgid(dockerCmd.Process.Pid); err == nil {
_ = syscall.Kill(-pgid, syscall.SIGKILL)
}
_ = dockerCmd.Process.Kill()
}
}()
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
if len(line) > 0 {
line = strings.TrimSuffix(line, "\n")
select {
case messageChan <- line:
case <-done:
return
}
}
break
}
errorChan <- fmt.Errorf("reader error: %v", err)
return
}
line = strings.TrimSuffix(line, "\n")
select {
case messageChan <- message:
case <-time.After(5 * time.Second):
errorChan <- fmt.Errorf("message channel blocked")
case messageChan <- line:
case <-done:
return
}
}
if err = scanner.Err(); err != nil {
errorChan <- fmt.Errorf("scanner error: %v", err)
return
}
if err = dockerCmd.Wait(); err != nil {
errorChan <- fmt.Errorf("%v", err)
return
}
_ = dockerCmd.Wait()
}
func (u *ContainerService) DownloadContainerLogs(containerType, container, since, tail string, c *gin.Context) error {
@@ -1065,6 +1114,21 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
return &data, nil
}
func (u *ContainerService) LoadUsers(req dto.OperationWithName) ([]string, error) {
std, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s cat /etc/passwd", req.Name)
if err != nil {
return nil, err
}
var users []string
lines := strings.Split(string(std), "\n")
for _, line := range lines {
if strings.Contains(line, ":") {
users = append(users, strings.Split(line, ":")[0])
}
}
return users, nil
}
func stringsToMap(list []string) map[string]string {
var labelMap = make(map[string]string)
for _, label := range list {
@@ -1257,7 +1321,7 @@ func checkPortStats(ports []dto.PortHelper) (nat.PortMap, error) {
return portMap, nil
}
func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types.ContainerJSON) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *container.InspectResponse) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
var config container.Config
var hostConf container.HostConfig
if !isCreate {
@@ -1340,7 +1404,7 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
return &config, &hostConf, &networkConf, nil
}
func reCreateAfterUpdate(name string, client *client.Client, config *container.Config, hostConf *container.HostConfig, networkConf *types.NetworkSettings) {
func reCreateAfterUpdate(name string, client *client.Client, config *container.Config, hostConf *container.HostConfig, networkConf *container.NetworkSettings) {
ctx := context.Background()
var oldNetworkConf network.NetworkingConfig
@@ -1362,7 +1426,7 @@ func reCreateAfterUpdate(name string, client *client.Client, config *container.C
global.LOG.Info("recreate after container update successful")
}
func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
func loadVolumeBinds(binds []container.MountPoint) []dto.VolumeHelper {
var datas []dto.VolumeHelper
for _, bind := range binds {
var volumeItem dto.VolumeHelper
@@ -1382,13 +1446,13 @@ func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
return datas
}
func loadPortByInspect(id string, client *client.Client) ([]types.Port, error) {
container, err := client.ContainerInspect(context.Background(), id)
func loadPortByInspect(id string, client *client.Client) ([]container.Port, error) {
containerItem, err := client.ContainerInspect(context.Background(), id)
if err != nil {
return nil, err
}
var itemPorts []types.Port
for key, val := range container.ContainerJSONBase.HostConfig.PortBindings {
var itemPorts []container.Port
for key, val := range containerItem.ContainerJSONBase.HostConfig.PortBindings {
if !strings.Contains(string(key), "/") {
continue
}
@@ -1397,15 +1461,15 @@ func loadPortByInspect(id string, client *client.Client) ([]types.Port, error) {
for _, itemVal := range val {
publicPort, _ := strconv.ParseUint(itemVal.HostPort, 10, 16)
itemPorts = append(itemPorts, types.Port{PrivatePort: uint16(itemPort), Type: item[1], PublicPort: uint16(publicPort), IP: itemVal.HostIP})
itemPorts = append(itemPorts, container.Port{PrivatePort: uint16(itemPort), Type: item[1], PublicPort: uint16(publicPort), IP: itemVal.HostIP})
}
}
return itemPorts, nil
}
func transPortToStr(ports []types.Port) []string {
func transPortToStr(ports []container.Port) []string {
var (
ipv4Ports []types.Port
ipv6Ports []types.Port
ipv4Ports []container.Port
ipv6Ports []container.Port
)
for _, port := range ports {
if strings.Contains(port.IP, ":") {
@@ -1418,7 +1482,7 @@ func transPortToStr(ports []types.Port) []string {
list2 := simplifyPort(ipv6Ports)
return append(list1, list2...)
}
func simplifyPort(ports []types.Port) []string {
func simplifyPort(ports []container.Port) []string {
var datas []string
if len(ports) == 0 {
return datas
@@ -1514,7 +1578,7 @@ func loadComposeCount(client *client.Client) int {
return len(composeMap)
}
func loadContainerPortForInfo(itemPorts []types.Port) []dto.PortHelper {
func loadContainerPortForInfo(itemPorts []container.Port) []dto.PortHelper {
var exposedPorts []dto.PortHelper
samePortMap := make(map[string]dto.PortHelper)
ports := transPortToStr(itemPorts)
+22
View File
@@ -73,6 +73,9 @@ func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interfac
} else {
item.AlertCount = 0
}
if cronjob.Type == "snapshot" && len(cronjob.SnapshotRule) != 0 {
_ = json.Unmarshal([]byte(cronjob.SnapshotRule), &item.SnapshotRule)
}
dtoCronjobs = append(dtoCronjobs, item)
}
return total, dtoCronjobs, err
@@ -94,6 +97,9 @@ func (u *CronjobService) LoadInfo(req dto.OperateByID) (*dto.CronjobOperate, err
} else {
item.AlertCount = 0
}
if cronjob.Type == "snapshot" && len(cronjob.SnapshotRule) != 0 {
_ = json.Unmarshal([]byte(cronjob.SnapshotRule), &item.SnapshotRule)
}
return &item, err
}
@@ -279,6 +285,13 @@ func (u *CronjobService) Create(req dto.CronjobOperate) error {
if err := copier.Copy(&cronjob, &req); err != nil {
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
if cronjob.Type == "snapshot" {
rule, err := json.Marshal(req.SnapshotRule)
if err != nil {
return err
}
cronjob.SnapshotRule = string(rule)
}
if cronjob.Type == "cutWebsiteLog" {
backupAccount, err := backupRepo.Get(repo.WithByType(constant.Local))
if backupAccount.ID == 0 {
@@ -372,6 +385,13 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
if err := copier.Copy(&cronjob, &req); err != nil {
return buserr.WithDetail("ErrStructTransform", err.Error(), nil)
}
if req.Type == "snapshot" {
itemRule, err := json.Marshal(req.SnapshotRule)
if err != nil {
return err
}
cronjob.SnapshotRule = string(itemRule)
}
cronModel, err := cronjobRepo.Get(repo.WithByID(id))
if err != nil {
return buserr.New("ErrRecordNotFound")
@@ -412,12 +432,14 @@ func (u *CronjobService) Update(id uint, req dto.CronjobOperate) error {
upMap["db_name"] = req.DBName
upMap["url"] = req.URL
upMap["source_dir"] = req.SourceDir
upMap["snapshot_rule"] = cronjob.SnapshotRule
upMap["source_account_ids"] = req.SourceAccountIDs
upMap["download_account_id"] = req.DownloadAccountID
upMap["retain_copies"] = req.RetainCopies
upMap["retry_times"] = req.RetryTimes
upMap["timeout"] = req.Timeout
upMap["ignore_err"] = req.IgnoreErr
upMap["secret"] = req.Secret
err = cronjobRepo.Update(id, upMap)
if err != nil {
+112 -17
View File
@@ -2,6 +2,7 @@ package service
import (
"context"
"encoding/json"
"fmt"
"os"
"path"
@@ -47,6 +48,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
return err
}
for _, app := range apps {
retry := 0
taskItem.AddSubTaskWithOps(task.GetTaskName(app.Name, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
var record model.BackupRecord
record.From = "cronjob"
@@ -58,11 +60,22 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
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
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
} else {
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
return nil
}
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
return err
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
}
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
return nil
}
record.FileDir = path.Dir(downloadPath)
if err := backupRepo.CreateRecord(&record); err != nil {
@@ -86,6 +99,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
return err
}
for _, web := range webs {
retry := 0
taskItem.AddSubTaskWithOps(task.GetTaskName(web.Alias, task.TaskBackup, task.TaskScopeCronjob), func(task *task.Task) error {
var record model.BackupRecord
record.From = "cronjob"
@@ -98,12 +112,23 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
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
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
} else {
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
return nil
}
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
return err
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
}
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
return nil
}
record.FileDir = path.Dir(downloadPath)
if err := backupRepo.CreateRecord(&record); err != nil {
@@ -127,6 +152,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
return err
}
for _, dbInfo := range dbs {
retry := 0
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
@@ -141,17 +167,34 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
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
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
} else {
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
return nil
}
}
} else {
if err := doPostgresqlgBackup(dbInfo, backupDir, record.FileName); err != nil {
return err
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
} else {
task.Log(i18n.GetMsgWithDetail("IgnoreBackupErr", err.Error()))
return nil
}
}
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
if err != nil {
return err
if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr {
retry++
return err
}
task.Log(i18n.GetMsgWithDetail("IgnoreUploadErr", err.Error()))
return nil
}
record.FileDir = path.Dir(downloadPath)
if err := backupRepo.CreateRecord(&record); err != nil {
@@ -193,7 +236,7 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, fileName))
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, fileName))
if err != nil {
taskItem.LogFailedWithErr("Upload backup file", err)
return err
@@ -228,7 +271,7 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
record.CronjobID = cronjob.ID
record.Name = cronjob.Name
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(path.Dir(backupDir), fileName))
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(path.Dir(backupDir), fileName))
if err != nil {
taskItem.LogFailedWithErr("Upload backup file", err)
return err
@@ -250,10 +293,6 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
if err != nil {
return err
}
itemData, err := NewISnapshotService().LoadSnapshotData()
if err != nil {
return err
}
var record model.BackupRecord
record.From = "cronjob"
@@ -268,6 +307,11 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
if !global.IsMaster {
scope = "agent"
}
itemData, err := loadSnapWithRule(cronjob)
if err != nil {
return err
}
req := dto.SnapshotCreate{
Name: fmt.Sprintf("snapshot-1panel-%s-%s-linux-%s-%s", scope, versionItem.Value, loadOs(), jobRecord.StartTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)),
Secret: cronjob.Secret,
@@ -278,11 +322,13 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, jobRecord model.J
AppData: itemData.AppData,
PanelData: itemData.PanelData,
BackupData: itemData.BackupData,
WithDockerConf: true,
WithMonitorData: true,
WithLoginLog: true,
WithOperationLog: true,
WithSystemLog: true,
WithTaskLog: true,
IgnoreFiles: strings.Split(cronjob.ExclusionRules, ","),
}
if err := NewISnapshotService().SnapshotCreate(taskItem, req, jobRecord.ID, cronjob.RetryTimes, cronjob.Timeout); err != nil {
@@ -315,7 +361,7 @@ func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper {
dbs = append(dbs, DatabaseHelper{
ID: mysql.ID,
DBType: cronjob.DBType,
Database: mysql.Name,
Database: mysql.Database,
Name: mysql.Name,
})
}
@@ -451,3 +497,52 @@ func handleBackupLogs(taskItem *task.Task, targetDir, fileName string, secret st
}()
return nil
}
func loadSnapWithRule(cronjob model.Cronjob) (dto.SnapshotData, error) {
itemData, err := NewISnapshotService().LoadSnapshotData()
if err != nil {
return itemData, err
}
if len(cronjob.SnapshotRule) == 0 {
return itemData, nil
}
var snapRule dto.SnapshotRule
if err := json.Unmarshal([]byte(cronjob.SnapshotRule), &snapRule); err != nil {
return itemData, err
}
if len(snapRule.IgnoreAppIDs) == 0 && !snapRule.WithImage {
return itemData, nil
}
var ignoreApps []model.AppInstall
if len(snapRule.IgnoreAppIDs) != 0 {
ignoreApps, _ = appInstallRepo.ListBy(context.Background(), repo.WithByIDs(snapRule.IgnoreAppIDs))
}
if len(ignoreApps) == 0 && !snapRule.WithImage {
return itemData, nil
}
for i := 0; i < len(itemData.AppData); i++ {
isIgnore := false
for _, ignore := range ignoreApps {
if ignore.App.Key == itemData.AppData[i].Key && ignore.Name == itemData.AppData[i].Name {
isIgnore = true
itemData.AppData[i].IsCheck = false
for j := 0; j < len(itemData.AppData[i].Children); j++ {
if itemData.AppData[i].Children[j].Label == "appData" {
itemData.AppData[i].Children[j].IsCheck = false
}
}
break
}
}
if snapRule.WithImage && !isIgnore {
for j := 0; j < len(itemData.AppData[i].Children); j++ {
if itemData.AppData[i].Children[j].Label == "appImage" {
itemData.AppData[i].Children[j].IsCheck = true
}
}
}
}
return itemData, nil
}
+50 -12
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"path"
pathUtils "path"
"strings"
"time"
@@ -78,17 +79,21 @@ func (u *CronjobService) loadTask(cronjob *model.Cronjob, record *model.JobRecor
return fmt.Errorf("the script content is empty and is skipped")
}
u.handleShell(*cronjob, taskItem)
u.removeExpiredLog(*cronjob)
case "curl":
if len(cronjob.URL) == 0 {
return fmt.Errorf("the url is empty and is skipped")
}
u.handleCurl(*cronjob, taskItem)
u.removeExpiredLog(*cronjob)
case "ntp":
u.handleNtpSync(*cronjob, taskItem)
u.removeExpiredLog(*cronjob)
case "cutWebsiteLog":
err = u.handleCutWebsiteLog(cronjob, record.StartTime, taskItem)
case "clean":
u.handleSystemClean(*cronjob, taskItem)
u.removeExpiredLog(*cronjob)
case "website":
err = u.handleWebsite(*cronjob, record.StartTime, taskItem)
case "app":
@@ -113,11 +118,21 @@ 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 {
scriptItem := cronjob.Script
if cronjob.ScriptMode == "select" {
scriptItem = path.Join("/tmp", path.Base(cronjob.Script))
if err := cmdMgr.Run("docker", "cp", cronjob.Script, cronjob.ContainerName+":"+scriptItem); err != nil {
return err
}
}
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.User) != 0 {
return cmdMgr.Run("docker", "exec", "-u", cronjob.User, cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
}
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
}
if len(cronjob.Executor) == 0 {
cronjob.Executor = "bash"
@@ -247,23 +262,32 @@ func (u *CronjobService) handleSystemClean(cronjob model.Cronjob, taskItem *task
taskItem.AddSubTaskWithOps(i18n.GetMsgByKey("HandleSystemClean"), cleanTask, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
}
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap map[string]backupClientHelper, file string) (string, error) {
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, task *task.Task, accountMap map[string]backupClientHelper, file string) (string, error) {
defer func() {
_ = os.Remove(file)
}()
var errItem error
accounts := strings.Split(cronjob.SourceAccountIDs, ",")
cloudSrc := strings.TrimPrefix(file, global.Dir.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)
task.LogStart(i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
"file": pathUtils.Join(accountMap[account].backupPath, cloudSrc),
"backup": accountMap[account].name,
}))
_, err := accountMap[account].client.Upload(file, pathUtils.Join(accountMap[account].backupPath, cloudSrc))
task.LogWithStatus(
i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
"file": pathUtils.Join(accountMap[account].backupPath, cloudSrc),
"backup": accountMap[account].name,
}), err)
if err != nil {
errItem = err
continue
}
global.LOG.Debugf("upload successful!")
}
}
return cloudSrc, nil
return cloudSrc, errItem
}
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]backupClientHelper, record model.BackupRecord) {
@@ -306,6 +330,24 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
}
}
func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), repo.WithOrderBy("created_at desc"))
if len(records) <= int(cronjob.RetainCopies) {
return
}
for i := int(cronjob.RetainCopies); i < len(records); i++ {
if len(records[i].File) != 0 {
files := strings.Split(records[i].File, ",")
for _, file := range files {
_ = os.Remove(file)
}
}
_ = cronjobRepo.DeleteRecord(repo.WithByID(uint(records[i].ID)))
_ = taskRepo.Delete(taskRepo.WithByID(records[i].TaskID))
_ = os.Remove(path.Join(global.CONF.Base.InstallDir, "1panel/log/task/Cronjob", records[i].TaskID+".log"))
}
}
func hasBackup(cronjobType string) bool {
return cronjobType == "app" || cronjobType == "database" || cronjobType == "website" || cronjobType == "directory" || cronjobType == "snapshot" || cronjobType == "log" || cronjobType == "cutWebsiteLog"
}
@@ -317,9 +359,5 @@ func handleCronJobAlert(cronjob *model.Cronjob) {
EntryID: cronjob.ID,
Param: cronjob.Type,
}
err := xpack.PushAlert(pushAlert)
if err != nil {
global.LOG.Errorf("cronjob alert push failed, err: %v", err)
return
}
_ = xpack.PushAlert(pushAlert)
}
+2 -2
View File
@@ -390,10 +390,10 @@ type diskInfo struct {
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")
stdout, err := cmdMgr.RunWithStdoutBashC("timeout 2 df -hT -P | awk 'NR>1 && !/tmpfs|snap\\/core|udev/ {print}'")
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")
stdout, err = cmdMgr2.RunWithStdoutBashC("timeout 1 df -lhT -P | awk 'NR>1 && !/tmpfs|snap\\/core|udev/ {print}'")
if err != nil {
return datas
}
+11 -6
View File
@@ -246,18 +246,23 @@ func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
}
return operateSwapWithFile(true, req)
}
std1, err := cmd.RunDefaultWithStdoutBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
stdDD, err := cmd.RunDefaultWithStdoutBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std1)
return fmt.Errorf("handle dd %s failed, std: %s, err: %s", req.Path, stdDD, err)
}
std2, err := cmd.RunDefaultWithStdoutBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
stdChmod, err := cmd.RunDefaultWithStdoutBashCf("%s chmod 0600 %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std2)
return fmt.Errorf("handle chmod 0600 %s failed,std: %s, err: %s", req.Path, stdChmod, err)
}
std3, err := cmd.RunDefaultWithStdoutBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
stdMkswap, err := cmd.RunDefaultWithStdoutBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
return fmt.Errorf("handle mkswap -f %s failed, std: %s, err: %s", req.Path, stdMkswap, err)
}
stdSwapon, err := cmd.RunDefaultWithStdoutBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
if err != nil {
_, _ = cmd.RunDefaultWithStdoutBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std3)
return fmt.Errorf("handle swapoff %s failed,std: %s, err: %s", req.Path, stdSwapon, err)
}
return operateSwapWithFile(false, req)
}
+32 -6
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"sort"
"strings"
"time"
@@ -12,6 +13,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/filters"
"github.com/1Panel-dev/1Panel/agent/app/dto"
@@ -50,7 +52,7 @@ func (u *DeviceService) Scan() dto.CleanData {
ID: uuid.NewString(),
Label: "1panel_original",
Size: uint64(originalSize),
IsCheck: true,
IsCheck: originalSize > 0,
IsRecommend: true,
Type: "1panel_original",
Children: loadTreeWithDir(true, "1panel_original", originalPath, fileOp),
@@ -85,7 +87,7 @@ func (u *DeviceService) Scan() dto.CleanData {
ID: uuid.NewString(),
Label: "snapshot",
Size: snapSize,
IsCheck: true,
IsCheck: snapSize > 0,
IsRecommend: true,
Type: "snapshot",
Children: snapTree,
@@ -100,7 +102,7 @@ func (u *DeviceService) Scan() dto.CleanData {
ID: uuid.NewString(),
Label: "rollback",
Size: rollbackSize,
IsCheck: true,
IsCheck: rollbackSize > 0,
IsRecommend: true,
Type: "rollback",
Children: rollBackTree,
@@ -126,7 +128,7 @@ func (u *DeviceService) Scan() dto.CleanData {
ID: uuid.NewString(),
Label: "unused",
Size: unusedSize,
IsCheck: true,
IsCheck: unusedSize > 0,
IsRecommend: true,
Type: "unused",
Children: unusedTree,
@@ -338,7 +340,7 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
}
dropWithTask(path.Join(global.Dir.BaseDir, tmpUploadPath), taskItem, &size, &fileCount)
dropWithTask(path.Join(global.Dir.BaseDir, uploadPath), taskItem, &size, &fileCount)
dropWithExclude(path.Join(global.Dir.BaseDir, uploadPath), []string{"theme"}, taskItem, &size, &fileCount)
dropWithTask(path.Join(global.Dir.BaseDir, downloadPath), taskItem, &size, &fileCount)
logFiles, _ := os.ReadDir(global.Dir.LogDir)
@@ -662,7 +664,7 @@ func dropBuildCache() {
if err != nil {
global.LOG.Errorf("do not get docker client")
}
opts := types.BuildCachePruneOptions{}
opts := build.CachePruneOptions{}
opts.All = true
_, err = client.BuildCachePrune(context.Background(), opts)
if err != nil {
@@ -714,6 +716,30 @@ func dropVolumes() {
}
}
func dropWithExclude(pathToDelete string, excludeSubDirs []string, taskItem *task.Task, size *int64, count *int) {
entries, err := os.ReadDir(pathToDelete)
if err != nil {
taskItem.LogFailed(fmt.Sprintf("read dir %s failed: %v", pathToDelete, err))
return
}
for _, entry := range entries {
name := entry.Name()
fullPath := filepath.Join(pathToDelete, name)
excluded := false
for _, ex := range excludeSubDirs {
if name == ex {
excluded = true
break
}
}
if excluded {
continue
}
dropWithTask(fullPath, taskItem, size, count)
}
}
func dropWithTask(itemPath string, taskItem *task.Task, size *int64, count *int) {
itemSize := int64(0)
itemCount := 0
-27
View File
@@ -220,16 +220,6 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate, withRestart bool) erro
daemonMap["proxies"] = proxies
}
}
if len(daemonMap) == 0 {
if len(file) == 0 {
return nil
}
_ = os.Remove(constant.DaemonJsonPath)
if withRestart {
return restartDocker()
}
return nil
}
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
if err != nil {
return err
@@ -277,11 +267,6 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
_ = json.Unmarshal(file, &daemonMap)
changeLogOption(daemonMap, req.LogMaxFile, req.LogMaxSize)
if len(daemonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
_ = restartDocker()
return nil
}
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
if err != nil {
return err
@@ -321,11 +306,6 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
if req.Experimental {
daemonMap["experimental"] = req.Experimental
}
if len(daemonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
_ = restartDocker()
return nil
}
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
if err != nil {
return err
@@ -345,13 +325,6 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
}
func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
if len(req.File) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
if err := restartDocker(); err != nil {
return err
}
return nil
}
err := createIfNotExistDaemonJsonFile()
if err != nil {
return err
+27 -14
View File
@@ -4,7 +4,10 @@ import (
"bufio"
"context"
"fmt"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/jinzhu/copier"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/simplifiedchinese"
"io"
"io/fs"
"os"
@@ -15,9 +18,6 @@ import (
"strconv"
"strings"
"time"
"unicode/utf8"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/repo"
@@ -27,7 +27,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"golang.org/x/net/html/charset"
"golang.org/x/sys/unix"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
"github.com/1Panel-dev/1Panel/agent/global"
@@ -335,17 +334,22 @@ func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo,
if len(content) > 1024 {
content = content[:1024]
}
if !utf8.Valid(content) {
_, decodeName, _ := charset.DetermineEncoding(content, "")
_, decodeName, _ := charset.DetermineEncoding(content, "")
decoder := files.GetDecoderByName(decodeName)
if decoder != nil {
reader := strings.NewReader(info.Content)
var dec *encoding.Decoder
if decodeName == "windows-1252" {
reader := strings.NewReader(info.Content)
item := transform.NewReader(reader, simplifiedchinese.GBK.NewDecoder())
contents, err := io.ReadAll(item)
if err != nil {
return response.FileInfo{}, err
}
info.Content = string(contents)
dec = simplifiedchinese.GBK.NewDecoder()
} else {
dec = decoder.NewDecoder()
}
decodedReader := transform.NewReader(reader, dec)
contents, err := io.ReadAll(decodedReader)
if err != nil {
return response.FileInfo{}, err
}
info.Content = string(contents)
}
return response.FileInfo{FileInfo: *info}, nil
}
@@ -390,10 +394,18 @@ func (f *FileService) MvFile(m request.FileMove) error {
return buserr.New("ErrMovePathFailed")
}
}
var errs []error
if m.Type == "cut" {
if len(m.CoverPaths) > 0 {
for _, src := range m.CoverPaths {
if err := fo.CopyAndReName(src, m.NewPath, "", true); err != nil {
errs = append(errs, err)
global.LOG.Errorf("cut copy file [%s] to [%s] failed, err: %s", src, m.NewPath, err.Error())
}
}
}
return fo.Cut(m.OldPaths, m.NewPath, m.Name, m.Cover)
}
var errs []error
if m.Type == "copy" {
for _, src := range m.OldPaths {
if err := fo.CopyAndReName(src, m.NewPath, m.Name, m.Cover); err != nil {
@@ -565,6 +577,7 @@ func (f *FileService) BatchCheckFiles(req request.FilePathsCheck) []response.Exi
Name: info.Name(),
Path: filePath,
ModTime: info.ModTime(),
IsDir: info.IsDir(),
})
}
}
+12 -7
View File
@@ -182,6 +182,7 @@ func (u *FirewallService) OperateFirewall(operation string) error {
if err != nil {
return err
}
needRestartDocker := false
switch operation {
case "start":
if err := client.Start(); err != nil {
@@ -191,26 +192,30 @@ func (u *FirewallService) OperateFirewall(operation string) error {
_ = client.Stop()
return err
}
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
return nil
needRestartDocker = true
case "stop":
if err := client.Stop(); err != nil {
return err
}
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
return nil
needRestartDocker = true
case "restart":
if err := client.Restart(); err != nil {
return err
}
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
return nil
needRestartDocker = true
case "disablePing":
return u.updatePingStatus("0")
case "enablePing":
return u.updatePingStatus("1")
default:
return fmt.Errorf("not supported operation: %s", operation)
}
return fmt.Errorf("not support such operation: %s", operation)
if needRestartDocker {
if err := restartDocker(); err != nil {
return err
}
}
return nil
}
func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool) error {
+13 -11
View File
@@ -105,21 +105,23 @@ func (f *FtpService) Sync() error {
if err != nil {
return err
}
currentData := make(map[string]model.Ftp)
for _, item := range listsInDB {
currentData[item.User] = item
}
sameData := make(map[string]struct{})
for _, item := range lists {
for _, itemInDB := range listsInDB {
if item.User == itemInDB.User {
sameData[item.User] = struct{}{}
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
_ = ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status})
if itemInDB, ok := currentData[item.User]; ok {
sameData[item.User] = struct{}{}
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
if err := ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status}); err != nil {
return err
}
break
}
}
}
for _, item := range lists {
if _, ok := sameData[item.User]; !ok {
_ = ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status})
} else {
if err := ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status}); err != nil {
return err
}
}
}
for _, item := range listsInDB {
+1 -1
View File
@@ -602,7 +602,7 @@ func getProcessStatus(config *response.SupervisorProcessConfig, containerName st
out, err = exec.Command("supervisorctl", processNames...).Output()
output = string(out)
}
if containerName == "" && err != nil {
if output == "" && err != nil {
return err
}
lines := strings.Split(output, "\n")
+4 -4
View File
@@ -22,7 +22,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/registry"
@@ -189,7 +189,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) error {
return err
}
opts := types.ImageBuildOptions{
opts := build.ImageBuildOptions{
Dockerfile: fileName,
Tags: []string{req.Name},
Remove: true,
@@ -280,7 +280,7 @@ func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
return err
}
defer client.Close()
res, err := client.ImageLoad(context.TODO(), file, true)
res, err := client.ImageLoad(context.TODO(), file)
if err != nil {
return err
}
@@ -432,7 +432,7 @@ func formatFileSize(fileSize int64) (size string) {
}
}
func checkUsed(imageID string, containers []types.Container) bool {
func checkUsed(imageID string, containers []container.Summary) bool {
for _, container := range containers {
if container.ImageID == imageID {
return true
+69 -44
View File
@@ -28,7 +28,7 @@ type IImageRepoService interface {
Login(req dto.OperateByID) error
Create(req dto.ImageRepoCreate) error
Update(req dto.ImageRepoUpdate) error
BatchDelete(req dto.ImageRepoDelete) error
Delete(req dto.OperateByID) error
}
func NewIImageRepoService() IImageRepoService {
@@ -91,29 +91,7 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
}
stdout, err := cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
}
ticker := time.NewTicker(3 * time.Second)
defer ticker.Stop()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
if err := func() error {
for range ticker.C {
select {
case <-ctx.Done():
cancel()
return errors.New("the docker service cannot be restarted")
default:
stdout, err := cmd.RunDefaultWithStdoutBashC("systemctl is-active docker")
if string(stdout) == "active\n" && err == nil {
global.LOG.Info("docker restart with new conf successful!")
return nil
}
}
}
return nil
}(); err != nil {
if err := stopBeforeUpdateRepo(); err != nil {
return err
}
}
@@ -132,15 +110,32 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
return imageRepoRepo.Create(&imageRepo)
}
func (u *ImageRepoService) BatchDelete(req dto.ImageRepoDelete) error {
for _, id := range req.Ids {
if id == 1 {
return errors.New("The default value cannot be edit !")
}
func (u *ImageRepoService) Delete(req dto.OperateByID) error {
if req.ID == 1 {
return errors.New("The default value cannot be edit !")
}
if err := imageRepoRepo.Delete(repo.WithByIDs(req.Ids)); err != nil {
itemRepo, _ := imageRepoRepo.Get(repo.WithByID(req.ID))
if itemRepo.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
if itemRepo.Auth {
_, _ = cmd.NewCommandMgr().RunWithStdout("docker", "logout", "-i", itemRepo.DownloadUrl)
}
if itemRepo.Protocol == "https" {
return imageRepoRepo.Delete(repo.WithByID(req.ID))
}
if err := u.handleRegistries("", itemRepo.DownloadUrl, "delete"); err != nil {
return fmt.Errorf("delete registry %s failed, err: %v", itemRepo.DownloadUrl, err)
}
if err := validateDockerConfig(); err != nil {
return err
}
if err := imageRepoRepo.Delete(repo.WithByID(req.ID)); err != nil {
return err
}
go func() {
_ = restartDocker()
}()
return nil
}
@@ -155,38 +150,37 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
if err != nil {
return err
}
needRestart := false
if repo.Protocol == "http" && req.Protocol == "https" {
if err := u.handleRegistries("", repo.DownloadUrl, "delete"); err != nil {
return fmt.Errorf("delete registry %s failed, err: %v", repo.DownloadUrl, err)
}
needRestart = true
}
if repo.Protocol == "http" && req.Protocol == "http" {
if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil {
return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err)
}
needRestart = repo.DownloadUrl == req.DownloadUrl
}
if repo.Protocol == "https" && req.Protocol == "http" {
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
}
needRestart = true
}
if repo.Auth != req.Auth || repo.DownloadUrl != req.DownloadUrl {
if repo.Auth {
cmdMgr := cmd.NewCommandMgr()
_, _ = cmdMgr.RunWithStdout("docker", "logout", "-i", repo.DownloadUrl)
}
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
return err
}
if needRestart {
if err := stopBeforeUpdateRepo(); err != nil {
return err
}
}
if err := validateDockerConfig(); err != nil {
return err
if repo.Auth {
_, _ = cmd.NewCommandMgr().RunWithStdout("docker", "logout", "-i", repo.DownloadUrl)
}
if err := restartDocker(); err != nil {
return err
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
return err
}
}
upMap := make(map[string]interface{})
@@ -261,3 +255,34 @@ func (u *ImageRepoService) handleRegistries(newHost, delHost, handle string) err
}
return nil
}
func stopBeforeUpdateRepo() error {
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
}
ticker := time.NewTicker(3 * time.Second)
defer ticker.Stop()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
if err := func() error {
for range ticker.C {
select {
case <-ctx.Done():
cancel()
return errors.New("the docker service cannot be restarted")
default:
stdout, err := cmd.RunDefaultWithStdoutBashC("systemctl is-active docker")
if string(stdout) == "active\n" && err == nil {
global.LOG.Info("docker restart with new conf successful!")
return nil
}
}
}
return nil
}(); err != nil {
return err
}
return nil
}
+33 -18
View File
@@ -52,6 +52,7 @@ type IRuntimeService interface {
SyncForRestart() error
SyncRuntimeStatus() error
DeleteCheck(installID uint) ([]dto.AppResource, error)
UpdateRemark(req request.RuntimeRemark) error
GetPHPExtensions(runtimeID uint) (response.PHPExtensionRes, error)
InstallPHPExtension(req request.PHPExtensionInstallReq) error
@@ -108,6 +109,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
Type: create.Type,
Version: create.Version,
Status: constant.StatusNormal,
Remark: create.Remark,
}
return nil, runtimeRepo.Create(context.Background(), runtime)
}
@@ -168,6 +170,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
Version: create.Version,
ContainerName: containerName.(string),
Port: strings.Join(hostPorts, ","),
Remark: create.Remark,
}
switch create.Type {
@@ -454,6 +457,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
Params: req.Params,
CodeDir: req.CodeDir,
Version: req.Version,
Remark: req.Remark,
NodeConfig: request.NodeConfig{
Install: true,
ExposedPorts: req.ExposedPorts,
@@ -465,6 +469,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
if err != nil {
return err
}
runtime.Remark = req.Remark
runtime.Env = string(envContent)
runtime.DockerCompose = string(composeContent)
@@ -682,7 +687,7 @@ func (r *RuntimeService) GetPHPExtensions(runtimeID uint) (response.PHPExtension
}
}
var phpExtensions []response.SupportExtension
if err = json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil {
if err = json.Unmarshal(nginx_conf.GetWebsiteFile("php_extensions.json"), &phpExtensions); err != nil {
return res, err
}
for _, ext := range phpExtensions {
@@ -719,25 +724,27 @@ func (r *RuntimeService) InstallPHPExtension(req request.PHPExtensionInstallReq)
return err
}
client, err := docker.NewClient()
if err != nil {
return err
}
defer client.Close()
if err == nil {
oldImageID, err := client.GetImageIDByName(runtime.Image)
if err != nil {
return err
}
err = cmdMgr.RunBashCf("docker commit %s %s", runtime.ContainerName, runtime.Image)
if err != nil {
return err
}
newImageID, err := client.GetImageIDByName(runtime.Image)
if err == nil && newImageID != oldImageID {
if err := client.DeleteImage(oldImageID); err != nil {
t.Log(fmt.Sprintf("delete old image error %v", err))
} else {
t.Log("delete old image success")
}
oldImageID, err := client.GetImageIDByName(runtime.Image)
if err != nil {
return err
}
err = cmdMgr.RunBashCf("docker commit %s %s", runtime.ContainerName, runtime.Image)
if err != nil {
return err
}
newImageID, err := client.GetImageIDByName(runtime.Image)
if err == nil && newImageID != oldImageID {
if err := client.DeleteImage(oldImageID); err != nil {
t.Log(fmt.Sprintf("delete old image error %v", err))
} else {
t.Log("delete old image success")
}
}
handlePHPDir(*runtime)
if err = restartRuntime(runtime); err != nil {
return err
}
@@ -887,7 +894,6 @@ func (r *RuntimeService) UpdatePHPConfig(req request.PHPConfigUpdate) (err error
pattern := "^" + regexp.QuoteMeta("disable_functions") + "\\s*=\\s*.*$"
if matched, _ := regexp.MatchString(pattern, line); matched {
lines[i] = "disable_functions" + " = " + strings.Join(req.DisableFunctions, ",")
break
}
case "upload_max_filesize":
pattern := "^" + regexp.QuoteMeta("post_max_size") + "\\s*=\\s*.*$"
@@ -1143,3 +1149,12 @@ func (r *RuntimeService) OperateSupervisorProcessFile(req request.PHPSupervisorP
logFile := path.Join(supervisorDir, "log", fmt.Sprintf("%s.out.log", req.SupervisorProcessFileReq.Name))
return handleSupervisorFile(req.SupervisorProcessFileReq, configDir, runtime.ContainerName, logFile)
}
func (r *RuntimeService) UpdateRemark(req request.RuntimeRemark) error {
runtime, err := runtimeRepo.GetFirst(context.Background(), repo.WithByID(req.ID))
if err != nil {
return err
}
runtime.Remark = req.Remark
return runtimeRepo.Save(runtime)
}
+81 -11
View File
@@ -6,7 +6,6 @@ import (
"context"
"encoding/json"
"fmt"
cmd2 "github.com/1Panel-dev/1Panel/agent/utils/cmd"
"io"
"os"
"os/exec"
@@ -17,6 +16,8 @@ import (
"strings"
"time"
cmd2 "github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/common"
@@ -89,7 +90,7 @@ func handlePHP(create request.RuntimeCreate, runtime *model.Runtime, fileOp file
version, ok := create.Params["PHP_VERSION"]
if ok {
extensionsDir := path.Join(projectDir, "extensions", getExtensionDir(version.(string)))
_ = fileOp.CreateDir(extensionsDir, 0644)
_ = fileOp.CreateDir(extensionsDir, 0755)
}
composeContent, envContent, forms, err := handleParams(create, projectDir)
@@ -141,11 +142,27 @@ func reCreateRuntime(runtime *model.Runtime) {
}
}
func runComposeCmdWithLog(operate string, composePath string, logPath string) error {
cmd := exec.Command("docker", "compose", "-f", composePath, operate)
if operate == "up" {
cmd = exec.Command("docker", "compose", "-f", composePath, operate, "-d")
func getComposeCmd(composePath, operate string) *exec.Cmd {
dockerCommand := global.CONF.DockerConfig.Command
var cmd *exec.Cmd
if dockerCommand == "docker-compose" {
if operate == "up" {
cmd = exec.Command("docker-compose", "-f", composePath, operate, "-d")
} else {
cmd = exec.Command("docker-compose", "-f", composePath, operate)
}
} else {
if operate == "up" {
cmd = exec.Command("docker", "compose", "-f", composePath, operate, "-d")
} else {
cmd = exec.Command("docker", "compose", "-f", composePath, operate)
}
}
return cmd
}
func runComposeCmdWithLog(operate string, composePath string, logPath string) error {
cmd := getComposeCmd(composePath, operate)
logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, constant.FilePerm)
if err != nil {
global.LOG.Errorf("Failed to open log file: %v", err)
@@ -287,10 +304,22 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebu
_ = logFile.Close()
}()
newPHPVersion := getRuntimeEnv(runtime.Env, "PHP_VERSION")
oldPHPVersion := getRuntimeEnv(oldEnv, "PHP_VERSION")
if newPHPVersion != oldPHPVersion {
_ = os.Rename(path.Join(runtimePath, "extensions", getExtensionDir(oldPHPVersion)), path.Join(runtimePath, "extensions", getExtensionDir(newPHPVersion)))
}
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Hour)
defer cancel()
cmd := exec.CommandContext(ctx, "docker", "compose", "-f", composePath, "build")
dockerCommand := global.CONF.DockerConfig.Command
var cmd *exec.Cmd
if dockerCommand == "docker-compose" {
cmd = exec.CommandContext(ctx, "docker-compose", "-f", composePath, "build")
} else {
cmd = exec.CommandContext(ctx, "docker", "compose", "-f", composePath, "build")
}
cmd.Stdout = logFile
var stderrBuf bytes.Buffer
multiWriterStderr := io.MultiWriter(&stderrBuf, logFile)
@@ -373,6 +402,7 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, oldEnv string, rebu
if deleteImageID != "" {
deleteImageByID(deleteImageID, runtime.Image, client)
}
handlePHPDir(*runtime)
if out, err := compose.DownAndUp(composePath); err != nil {
runtime.Status = constant.StatusStartErr
runtime.Message = out
@@ -553,7 +583,7 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
serviceName = name
serviceValue = service.(map[string]interface{})
delete(serviceValue, "ports")
if create.ExposedPorts != nil && len(create.ExposedPorts) > 0 {
if len(create.ExposedPorts) > 0 {
var ports []interface{}
for i, port := range create.ExposedPorts {
containerPortStr := fmt.Sprintf("CONTAINER_PORT_%d", i)
@@ -567,12 +597,12 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
create.Params[hostIPStr] = port.HostIP
}
if create.Type == constant.RuntimePHP {
ports = append(ports, fmt.Sprintf("127.0.0.1:${PANEL_APP_PORT_HTTP}:9000"))
ports = append(ports, "127.0.0.1:${PANEL_APP_PORT_HTTP}:9000")
}
serviceValue["ports"] = ports
} else {
if create.Type == constant.RuntimePHP {
serviceValue["ports"] = []interface{}{fmt.Sprintf("127.0.0.1:${PANEL_APP_PORT_HTTP}:9000")}
serviceValue["ports"] = []interface{}{"127.0.0.1:${PANEL_APP_PORT_HTTP}:9000"}
}
}
var environments []interface{}
@@ -657,7 +687,7 @@ func unInstallPHPExtension(runtime *model.Runtime, delExtensions []string) error
dir := runtime.GetPath()
fileOP := files.NewFileOp()
var phpExtensions []response.SupportExtension
if err := json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil {
if err := json.Unmarshal(nginx_conf.GetWebsiteFile("php_extensions.json"), &phpExtensions); err != nil {
return err
}
phpVersion := getRuntimeEnv(runtime.Env, "PHP_VERSION")
@@ -959,3 +989,43 @@ func handleRuntimeDTO(res *response.RuntimeDTO, runtime model.Runtime) error {
}
return nil
}
func handlePHPDir(runtime model.Runtime) {
fileOp := files.NewFileOp()
dirs := []string{
path.Join(runtime.GetPath(), "conf"),
path.Join(runtime.GetPath(), "extensions"),
path.Join(runtime.GetPath(), "composer"),
path.Join(runtime.GetPath(), "log"),
}
for _, dir := range dirs {
_ = fileOp.ChmodR(dir, 0755, true)
_ = fileOp.ChownR(dir, strconv.Itoa(1000), strconv.Itoa(1000), true)
}
}
func HandleOldPHPRuntime() {
runtimes, _ := runtimeRepo.List(repo.WithByType(constant.RuntimePHP))
if len(runtimes) == 0 {
return
}
fileOp := files.NewFileOp()
for _, runtime := range runtimes {
composePtah := runtime.GetComposePath()
composeBytes, _ := fileOp.GetContent(composePtah)
composeContent := strings.ReplaceAll(string(composeBytes), "./conf:/usr/local/etc/php", "./conf/php.ini:/usr/local/etc/php/php.ini")
composeContent = strings.ReplaceAll(composeContent, "./conf/php-fpm.conf:/usr/local/etc/php-fpm.d/www.conf", "./conf/php-fpm.conf:/usr/local/etc/php-fpm.conf")
composeContent = strings.ReplaceAll(composeContent, "./extensions:${EXTENSION_DIR}", "./extensions:/usr/local/lib/php/extensions")
_ = fileOp.WriteFile(composePtah, strings.NewReader(composeContent), constant.DirPerm)
_ = fileOp.WriteFile(runtime.GetFPMPath(), bytes.NewReader(nginx_conf.GetWebsiteFile("php-fpm.conf")), constant.DirPerm)
supervisorConfigPath := path.Join(runtime.GetPath(), "supervisor", "supervisor.d", "php-fpm.ini")
supervisorConfigBytes, _ := fileOp.GetContent(supervisorConfigPath)
if !strings.Contains(string(supervisorConfigBytes), "nodaemonize") {
newConfigContent := strings.ReplaceAll(string(supervisorConfigBytes), "command=php-fpm", "command=php-fpm --nodaemonize")
_ = fileOp.WriteFile(supervisorConfigPath, bytes.NewReader([]byte(newConfigContent)), constant.DirPerm)
}
go func() {
_ = restartRuntime(&runtime)
}()
}
}
+19 -8
View File
@@ -68,7 +68,7 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
}
for _, snapName := range req.Names {
if !strings.HasPrefix(snapName, "1panel-v2.") && !strings.HasPrefix(snapName, "snapshot-1panel-v2.") {
if !checkSnapshotIsOk(snapName) {
return fmt.Errorf("incorrect snapshot name format of %s", snapName)
}
snap, _ := snapshotRepo.Get(repo.WithByName(strings.ReplaceAll(snapName, ".tar.gz", "")))
@@ -77,11 +77,11 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
}
}
for _, snap := range req.Names {
shortName := strings.TrimPrefix(snap, "snapshot_")
nameItems := strings.Split(shortName, "-")
if !strings.HasPrefix(shortName, "1panel-v") || !strings.HasSuffix(shortName, ".tar.gz") || len(nameItems) < 3 {
return fmt.Errorf("incorrect snapshot name format of %s", shortName)
}
shortName := strings.ReplaceAll(snap, "snapshot-", "")
shortName = strings.ReplaceAll(shortName, "1panel-", "")
shortName = strings.ReplaceAll(shortName, "core-", "")
shortName = strings.ReplaceAll(shortName, "agent-", "")
nameItems := strings.Split(shortName, "-linux")
if strings.HasSuffix(snap, ".tar.gz") {
snap = strings.ReplaceAll(snap, ".tar.gz", "")
}
@@ -89,7 +89,7 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
Name: snap,
SourceAccountIDs: fmt.Sprintf("%v", req.BackupAccountID),
DownloadAccountID: req.BackupAccountID,
Version: nameItems[1],
Version: nameItems[0],
Description: req.Description,
Status: constant.StatusSuccess,
}
@@ -110,6 +110,7 @@ func (u *SnapshotService) LoadSnapshotData() (dto.SnapshotData, error) {
if err != nil {
return data, err
}
data.WithDockerConf = true
data.PanelData, err = loadPanelFile(fileOp)
if err != nil {
return data, err
@@ -326,7 +327,7 @@ func loadPanelFile(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {
switch itemData.Label {
case "agent", "runtime", "docker", "task", "geo", "secret", "uploads":
itemData.IsDisable = true
case "clamav", "download", "resource":
case "clamav", "download":
panelPath := path.Join(global.Dir.DataDir, itemData.Label)
itemData.Children, _ = loadFile(panelPath, 3, fileOp)
case "apps", "backup", "log", "db", "tmp":
@@ -403,3 +404,13 @@ func loadFile(pathItem string, index int, fileOp fileUtils.FileOp) ([]dto.DataTr
}
return data, nil
}
func checkSnapshotIsOk(name string) bool {
names := []string{"1panel-core-v2.", "1panel-agent-v2.", "1panel-v2.", "snapshot-1panel-core-v2.", "snapshot-1panel-agent-v2."}
for _, item := range names {
if strings.HasPrefix(name, item) {
return true
}
}
return false
}
+34 -26
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"sync"
"time"
@@ -34,7 +35,7 @@ func (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.Snapshot
scope = "agent"
}
if jobID == 0 {
req.Name = fmt.Sprintf("1panel-%s-%s-linux-%s-%s", versionItem.Value, scope, loadOs(), time.Now().Format(constant.DateTimeSlimLayout))
req.Name = fmt.Sprintf("1panel-%s-%s-linux-%s-%s", scope, versionItem.Value, loadOs(), time.Now().Format(constant.DateTimeSlimLayout))
}
appItem, _ := json.Marshal(req.AppData)
panelItem, _ := json.Marshal(req.PanelData)
@@ -50,11 +51,13 @@ func (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.Snapshot
AppData: string(appItem),
PanelData: string(panelItem),
BackupData: string(backupItem),
WithDockerConf: req.WithDockerConf,
WithMonitorData: req.WithMonitorData,
WithLoginLog: req.WithLoginLog,
WithOperationLog: req.WithOperationLog,
WithTaskLog: req.WithTaskLog,
WithSystemLog: req.WithSystemLog,
IgnoreFiles: strings.Join(req.IgnoreFiles, ","),
Version: versionItem.Value,
Status: constant.StatusWaiting,
@@ -76,7 +79,7 @@ func (u *SnapshotService) SnapshotCreate(parentTask *task.Task, req dto.Snapshot
}
if jobID == 0 {
go func() {
_ = handleSnapshot(req, taskItem, jobID, 0, 0)
_ = handleSnapshot(req, taskItem, jobID, 3, 0)
}()
return nil
}
@@ -106,13 +109,14 @@ func (u *SnapshotService) SnapshotReCreate(id uint) error {
return err
}
req.TaskID = taskModel.ID
taskItem, err := task.NewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeSnapshot, req.TaskID, req.ID)
taskItem, err := task.ReNewTaskWithOps(req.Name, task.TaskCreate, task.TaskScopeSnapshot, req.TaskID, req.ID)
if err != nil {
global.LOG.Errorf("new task for create snapshot failed, err: %v", err)
return err
}
_ = snapshotRepo.Update(req.ID, map[string]interface{}{"status": constant.StatusWaiting, "message": ""})
go func() {
_ = handleSnapshot(req, taskItem, 0, 0, 0)
_ = handleSnapshot(req, taskItem, 0, 3, 0)
}()
return nil
@@ -145,7 +149,7 @@ func handleSnapshot(req dto.SnapshotCreate, taskItem *task.Task, jobID, retry, t
if len(req.InterruptStep) == 0 || req.InterruptStep == "SnapBaseInfo" {
taskItem.AddSubTaskWithAliasAndOps(
"SnapBaseInfo",
func(t *task.Task) error { return snapBaseData(itemHelper, baseDir) },
func(t *task.Task) error { return snapBaseData(itemHelper, baseDir, req.WithDockerConf) },
nil, int(retry), time.Duration(timeout)*time.Second,
)
req.InterruptStep = ""
@@ -233,6 +237,7 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro
if err != nil {
return err
}
_ = os.Remove(path.Join(targetDir, "db/session.db"))
agentDb, err := common.LoadDBConnByPathWithErr(path.Join(targetDir, "db/agent.db"), "agent.db")
snap.Task.LogWithStatus(i18n.GetWithName("SnapNewDB", "agent"), err)
@@ -284,7 +289,7 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro
return nil
}
func snapBaseData(snap snapHelper, targetDir string) error {
func snapBaseData(snap snapHelper, targetDir string, withDockerConf bool) error {
snap.Task.Log("---------------------- 2 / 8 ----------------------")
snap.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo"))
@@ -321,11 +326,13 @@ func snapBaseData(snap snapHelper, targetDir string) error {
return err
}
if snap.FileOp.Stat("/etc/docker/daemon.json") {
err = snap.FileOp.CopyFile("/etc/docker/daemon.json", targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/docker/daemon.json"), err)
if err != nil {
return err
if withDockerConf {
if snap.FileOp.Stat(constant.DaemonJsonPath) {
err = snap.FileOp.CopyFile(constant.DaemonJsonPath, targetDir)
snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", constant.DaemonJsonPath), err)
if err != nil {
return err
}
}
}
@@ -377,8 +384,8 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err
}
}
snap.Task.Log(strings.Join(imageList, " "))
if len(imageList) != 0 {
snap.Task.Log(strings.Join(imageList, " "))
snap.Task.Logf("docker save %s | gzip -c > %s", strings.Join(imageList, " "), path.Join(targetDir, "images.tar.gz"))
std, err := cmd.NewCommandMgr(cmd.WithTimeout(10*time.Minute)).RunWithStdoutBashCf("docker save %s | gzip -c > %s", strings.Join(imageList, " "), path.Join(targetDir, "images.tar.gz"))
if err != nil {
@@ -386,6 +393,8 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err
return errors.New(std)
}
snap.Task.LogSuccess(i18n.GetMsgByKey("SnapDockerSave"))
} else {
snap.Task.Log(i18n.GetMsgByKey("SnapInstallAppImageEmpty"))
}
return nil
}
@@ -395,6 +404,7 @@ func snapBackupData(snap snapHelper, req dto.SnapshotCreate, targetDir string) e
snap.Task.LogStart(i18n.GetMsgByKey("SnapLocalBackup"))
excludes := loadBackupExcludes(snap, req.BackupData)
excludes = append(excludes, req.IgnoreFiles...)
excludes = append(excludes, "./system_snapshot")
for _, item := range req.AppData {
for _, itemApp := range item.Children {
@@ -418,7 +428,8 @@ func loadBackupExcludes(snap snapHelper, req []dto.DataTree) []string {
if err := snap.snapAgentDB.Where("file_dir = ? AND file_name = ?", strings.TrimPrefix(path.Dir(item.Path), global.Dir.LocalBackupDir+"/"), path.Base(item.Path)).Delete(&model.BackupRecord{}).Error; err != nil {
snap.Task.LogWithStatus("delete backup file from database", err)
}
excludes = append(excludes, "."+strings.TrimPrefix(item.Path, global.Dir.LocalBackupDir))
itemDir, _ := filepath.Rel(item.Path, global.Dir.LocalBackupDir)
excludes = append(excludes, itemDir)
} else {
excludes = append(excludes, loadBackupExcludes(snap, item.Children)...)
}
@@ -430,7 +441,8 @@ func loadAppBackupExcludes(req []dto.DataTree) []string {
for _, item := range req {
if len(item.Children) == 0 {
if !item.IsCheck {
excludes = append(excludes, "."+strings.TrimPrefix(item.Path, path.Join(global.Dir.LocalBackupDir)))
itemDir, _ := filepath.Rel(item.Path, global.Dir.LocalBackupDir)
excludes = append(excludes, itemDir)
}
} else {
excludes = append(excludes, loadAppBackupExcludes(item.Children)...)
@@ -463,26 +475,21 @@ func snapPanelData(snap snapHelper, req dto.SnapshotCreate, targetDir string) er
rootDir := global.Dir.DataDir
if strings.Contains(global.Dir.LocalBackupDir, rootDir) {
excludes = append(excludes, "."+strings.ReplaceAll(global.Dir.LocalBackupDir, rootDir, ""))
itemDir, _ := filepath.Rel(rootDir, global.Dir.LocalBackupDir)
excludes = append(excludes, itemDir)
}
if len(snap.OpenrestyDir) != 0 && strings.Contains(snap.OpenrestyDir, rootDir) {
excludes = append(excludes, "."+strings.ReplaceAll(snap.OpenrestyDir, rootDir, ""))
}
ignoreVal, _ := settingRepo.Get(settingRepo.WithByKey("SnapshotIgnore"))
rules := strings.Split(ignoreVal.Value, ",")
for _, ignore := range rules {
if len(ignore) == 0 || cmd.CheckIllegal(ignore) {
continue
}
excludes = append(excludes, "."+strings.ReplaceAll(ignore, rootDir, ""))
itemDir, _ := filepath.Rel(rootDir, snap.OpenrestyDir)
excludes = append(excludes, itemDir)
}
excludes = append(excludes, req.IgnoreFiles...)
err := snap.FileOp.TarGzCompressPro(false, rootDir, path.Join(targetDir, "1panel_data.tar.gz"), "", strings.Join(excludes, ","))
snap.Task.LogWithStatus(i18n.GetMsgByKey("SnapCompressPanel"), err)
if err != nil {
return err
}
if len(snap.OpenrestyDir) != 0 {
err := snap.FileOp.TarGzCompressPro(false, snap.OpenrestyDir, path.Join(targetDir, "website.tar.gz"), "", "")
err := snap.FileOp.TarGzCompressPro(false, snap.OpenrestyDir, path.Join(targetDir, "website.tar.gz"), "", strings.Join(req.IgnoreFiles, ","))
snap.Task.LogWithStatus(i18n.GetMsgByKey("SnapWebsite"), err)
if err != nil {
return err
@@ -496,7 +503,8 @@ func loadPanelExcludes(req []dto.DataTree) []string {
for _, item := range req {
if len(item.Children) == 0 {
if !item.IsCheck {
excludes = append(excludes, "."+strings.TrimPrefix(item.Path, path.Join(global.Dir.BaseDir, "1panel")))
itemDir, _ := filepath.Rel(item.Path, path.Join(global.Dir.BaseDir, "1panel"))
excludes = append(excludes, itemDir)
}
} else {
excludes = append(excludes, loadPanelExcludes(item.Children)...)
+56 -22
View File
@@ -38,6 +38,18 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"recover_status": constant.StatusFailed, "recover_message": errInfo})
return errors.New(errInfo)
}
if !strings.Contains(snap.Name, "-v2.") {
return errors.New("snapshots are currently not supported for recovery across major versions")
}
if !strings.Contains(snap.Name, "-core") && !strings.Contains(snap.Name, "-agent") {
return errors.New("the name of the snapshot file does not conform to the format")
}
if strings.Contains(snap.Name, "-core") && !global.IsMaster {
return errors.New("the snapshot of the master node cannot be restored on the agent nodes")
}
if strings.Contains(snap.Name, "-agent") && global.IsMaster {
return errors.New("the snapshot of the agent node cannot be restored on the master node")
}
if len(snap.RollbackStatus) != 0 && snap.RollbackStatus != constant.StatusSuccess {
req.IsNew = true
}
@@ -55,7 +67,13 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
} else {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"task_recover_id": req.TaskID})
}
taskItem, err := task.NewTaskWithOps(snap.Name, task.TaskRecover, task.TaskScopeSnapshot, req.TaskID, snap.ID)
var taskItem *task.Task
if req.IsNew {
taskItem, err = task.NewTaskWithOps(snap.Name, task.TaskRecover, task.TaskScopeSnapshot, req.TaskID, snap.ID)
} else {
taskItem, err = task.ReNewTaskWithOps(snap.Name, task.TaskRecover, task.TaskScopeSnapshot, req.TaskID, snap.ID)
}
if err != nil {
global.LOG.Errorf("new task for create snapshot failed, err: %v", err)
return err
@@ -84,7 +102,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
taskItem.AddSubTaskWithAlias(
"RecoverDecompress",
func(t *task.Task) error {
itemHelper.Task.Log("---------------------- 2 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 2 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetWithName("RecoverDecompress", snap.Name))
err := itemHelper.FileOp.TarGzExtractPro(fmt.Sprintf("%s/%s.tar.gz", rootDir, snap.Name), rootDir, req.Secret)
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
@@ -140,7 +158,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
taskItem.AddSubTaskWithAlias(
"RecoverBackups",
func(t *task.Task) error {
itemHelper.Task.Log("---------------------- 8 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 8 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetWithName("RecoverBackups", snap.Name))
err := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, "/1panel_backup.tar.gz"), snapJson.BackupDataDir, "")
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
@@ -150,11 +168,29 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "RecoverWebsite" {
taskItem.AddSubTaskWithAlias(
"RecoverWebsite",
func(t *task.Task) error {
itemHelper.Task.Log("---------------------- 9 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetWithName("RecoverWebsite", snap.Name))
webFile := path.Join(rootDir, snap.Name, "/website.tar.gz")
var err error
if itemHelper.FileOp.Stat(webFile) {
err = itemHelper.FileOp.TarGzExtractPro(webFile, snapJson.OperestyDir, "")
}
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
return err
},
nil,
)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "RecoverPanelData" {
taskItem.AddSubTaskWithAlias(
"RecoverPanelData",
func(t *task.Task) error {
itemHelper.Task.Log("---------------------- 9 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 10 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetWithName("RecoverPanelData", snap.Name))
err := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, "/1panel_data.tar.gz"), path.Join(snapJson.BaseDir, "1panel"), "")
itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err)
@@ -195,7 +231,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
}
func handleDownloadSnapshot(itemHelper *snapRecoverHelper, snap model.Snapshot, targetDir string) error {
itemHelper.Task.Log("---------------------- 1 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 1 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDownload"))
account, client, err := NewBackupClientWithID(snap.DownloadAccountID)
@@ -214,7 +250,7 @@ func handleDownloadSnapshot(itemHelper *snapRecoverHelper, snap model.Snapshot,
}
func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("---------------------- 3 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 3 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("BackupBeforeRecover"))
rootDir := fmt.Sprintf("%s/1panel_original/original_%s", global.Dir.BaseDir, name)
@@ -273,9 +309,9 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
if err != nil {
return err
}
if itemHelper.FileOp.Stat("/etc/docker/daemon.json") {
err = itemHelper.FileOp.CopyFile("/etc/docker/daemon.json", baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/docker/daemon.json"), err)
if itemHelper.FileOp.Stat(constant.DaemonJsonPath) {
err = itemHelper.FileOp.CopyFile(constant.DaemonJsonPath, baseDir)
itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", constant.DaemonJsonPath), err)
if err != nil {
return err
}
@@ -284,7 +320,7 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error {
}
func readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson, error) {
itemHelper.Task.Log("---------------------- 4 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 4 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("Readjson"))
snapJsonPath := path.Join(rootDir, "base/snapshot.json")
@@ -308,7 +344,7 @@ func readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson,
}
func recoverAppData(src string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("---------------------- 5 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 5 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverApp"))
if _, err := os.Stat(path.Join(src, "images.tar.gz")); err != nil {
@@ -325,7 +361,7 @@ func recoverAppData(src string, itemHelper *snapRecoverHelper) error {
}
func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("---------------------- 6 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 6 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo"))
if global.IsMaster {
@@ -356,27 +392,25 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
return err
}
daemonJsonPath := "/etc/docker/daemon.json"
_, errSrc := os.Stat(path.Join(src, "docker/daemon.json"))
_, errPath := os.Stat(daemonJsonPath)
if os.IsNotExist(errSrc) && os.IsNotExist(errPath) {
if !itemHelper.FileOp.Stat(path.Join(src, "daemon.json")) {
itemHelper.Task.Log(i18n.GetMsgByKey("RecoverDaemonJsonEmpty"))
return nil
}
if errSrc == nil {
err = itemHelper.FileOp.CopyFile(path.Join(src, "docker/daemon.json"), "/etc/docker")
} else {
err = itemHelper.FileOp.CopyFile(path.Join(src, "daemon.json"), path.Dir(constant.DaemonJsonPath))
itemHelper.Task.Log(i18n.GetMsgByKey("RecoverDaemonJson"))
if err != nil {
return fmt.Errorf("recover docker daemon.json failed, err: %v", err)
}
}
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
if err := restartDocker(); err != nil {
return err
}
return nil
}
func recoverDBData(src string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("---------------------- 7 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 7 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDBData"))
err := itemHelper.FileOp.CopyDirWithExclude(src, global.Dir.DataDir, nil)
@@ -385,7 +419,7 @@ func recoverDBData(src string, itemHelper *snapRecoverHelper) error {
}
func restartCompose(composePath string, itemHelper *snapRecoverHelper) error {
itemHelper.Task.Log("---------------------- 10 / 10 ----------------------")
itemHelper.Task.Log("---------------------- 11 / 11 ----------------------")
itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverCompose"))
composes, err := composeRepo.ListRecord()
+5 -2
View File
@@ -76,9 +76,12 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error {
nil,
)
taskItem.AddSubTask(
i18n.GetWithName("SnapCopy", "/etc/docker/daemon.json"),
i18n.GetWithName("SnapCopy", constant.DaemonJsonPath),
func(t *task.Task) error {
return FileOp.CopyFile(path.Join(baseDir, "daemon.json"), "/etc/docker")
if FileOp.Stat(path.Join(baseDir, "daemon.json")) {
return FileOp.CopyFile(path.Join(baseDir, "daemon.json"), path.Dir(constant.DaemonJsonPath))
}
return nil
},
nil,
)
+22 -28
View File
@@ -415,6 +415,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
return err
}
website.ParentWebsiteID = parentWebsite.ID
website.SiteDir = create.SiteDir
}
configNginx := func(t *task.Task) error {
@@ -469,7 +470,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
WebsiteSSLID: websiteModel.ID,
Type: "existed",
HttpConfig: "HTTPToHTTPS",
SSLProtocol: []string{"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"},
SSLProtocol: []string{"TLSv1.3", "TLSv1.2"},
Algorithm: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED",
Hsts: true,
}
@@ -922,18 +923,12 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS,
res response.WebsiteHTTPS
httpsPorts []string
)
websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteId))
for _, domain := range websiteDomains {
if domain.SSL {
httpsPorts = append(httpsPorts, strconv.Itoa(domain.Port))
}
}
if len(httpsPorts) == 0 {
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
res.HttpsPort = strconv.Itoa(nginxInstall.HttpsPort)
} else {
res.HttpsPort = strings.Join(httpsPorts, ",")
httpsPortsMap := getHttpsPort(websiteId)
for port := range httpsPortsMap {
httpsPorts = append(httpsPorts, strconv.Itoa(port))
}
res.HttpsPort = strings.Join(httpsPorts, ",")
if website.WebsiteSSLID == 0 {
res.Enable = false
return res, nil
@@ -981,11 +976,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
res response.WebsiteHTTPS
websiteSSL model.WebsiteSSL
)
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return nil, err
}
if err = ChangeHSTSConfig(req.Hsts, website); err != nil {
if err = ChangeHSTSConfig(req.Hsts, req.Http3, website); err != nil {
return nil, err
}
res.Enable = req.Enable
@@ -995,24 +986,27 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
website.Protocol = constant.ProtocolHTTP
website.WebsiteSSLID = 0
httpsPorts, err := getHttpsPort(&website)
websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID))
ports := make(map[int]struct{})
for _, domain := range websiteDomains {
ports[domain.Port] = struct{}{}
}
for port := range ports {
if err = removeSSLListen(website, []string{strconv.Itoa(port)}); err != nil {
return nil, err
}
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return nil, err
}
if len(httpsPorts) == 1 && httpsPorts[0] == nginxInstall.HttpsPort {
httpsPortStr := strconv.Itoa(httpsPorts[0])
if _, ok := ports[nginxInstall.HttpsPort]; !ok {
httpsPortStr := strconv.Itoa(nginxInstall.HttpsPort)
if err = deleteListenAndServerName(website, []string{httpsPortStr, "[::]:" + httpsPortStr}, []string{}); err != nil {
return nil, err
}
} else {
for _, port := range httpsPorts {
httpsPortStr := strconv.Itoa(port)
if err = removeSSLListen(website, []string{httpsPortStr}); err != nil {
return nil, err
}
}
}
nginxParams := getNginxParamsFromStaticFile(dto.SSL, nil)
nginxParams = append(nginxParams,
dto.NginxParam{
+3 -3
View File
@@ -436,14 +436,14 @@ func (w WebsiteCAService) DownloadFile(id uint) (*os.File, error) {
if err = fileOp.CreateDir(dir, constant.DirPerm); err != nil {
return nil, err
}
if err = fileOp.WriteFile(path.Join(dir, "ca.csr"), strings.NewReader(ca.CSR), constant.DirPerm); err != nil {
if err = fileOp.WriteFile(path.Join(dir, "ca.crt"), strings.NewReader(ca.CSR), constant.DirPerm); err != nil {
return nil, err
}
if err = fileOp.WriteFile(path.Join(dir, "private.key"), strings.NewReader(ca.PrivateKey), constant.DirPerm); err != nil {
if err = fileOp.WriteFile(path.Join(dir, "ca.key"), strings.NewReader(ca.PrivateKey), constant.DirPerm); err != nil {
return nil, err
}
fileName := ca.Name + ".zip"
if err = fileOp.Compress([]string{path.Join(dir, "ca.csr"), path.Join(dir, "private.key")}, dir, fileName, files.SdkZip, ""); err != nil {
if err = fileOp.Compress([]string{path.Join(dir, "ca.crt"), path.Join(dir, "ca.key")}, dir, fileName, files.SdkZip, ""); err != nil {
return nil, err
}
return os.Open(path.Join(dir, fileName))
+68 -45
View File
@@ -182,8 +182,8 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
return err
}
configPath := GetSitePath(*website, SiteConf)
nginxContent := string(nginx_conf.WebsiteDefault)
config, err := parser.NewStringParser(nginxContent).Parse()
nginxContent := nginx_conf.GetWebsiteFile("website_default.conf")
config, err := parser.NewStringParser(string(nginxContent)).Parse()
if err != nil {
return err
}
@@ -651,22 +651,33 @@ func createPemFile(website model.Website, websiteSSL model.WebsiteSSL) error {
return nil
}
func getHttpsPort(website *model.Website) ([]int, error) {
websiteDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID))
var httpsPorts []int
for _, domain := range websiteDomains {
if domain.SSL {
httpsPorts = append(httpsPorts, domain.Port)
func getHttpsPort(websiteID uint) map[int]struct{} {
domains, err := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(websiteID))
if err != nil {
return nil
}
httpsPorts := make(map[int]struct{})
nginxInstall, _ := getAppInstallByKey(constant.AppOpenresty)
hasDefaultPort := false
for _, domain := range domains {
if domain.Port == nginxInstall.HttpPort {
hasDefaultPort = true
}
if domain.SSL {
httpsPorts[domain.Port] = struct{}{}
}
}
if hasDefaultPort {
httpsPorts[nginxInstall.HttpsPort] = struct{}{}
}
if len(httpsPorts) == 0 {
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return nil, err
for _, domain := range domains {
if !domain.SSL {
httpsPorts[domain.Port] = struct{}{}
}
}
httpsPorts = append(httpsPorts, nginxInstall.HttpsPort)
}
return httpsPorts, nil
return httpsPorts
}
func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.WebsiteHTTPSOp) error {
@@ -678,63 +689,71 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
if err != nil {
return nil
}
noDefaultPort := true
httpPorts := make(map[int]struct{})
httpsPorts := make(map[int]struct{})
hasDefaultPort := false
for _, domain := range domains {
if domain.Port == 80 {
noDefaultPort = false
if domain.Port == nginxFull.Install.HttpPort {
hasDefaultPort = true
}
if domain.Port != 80 && !domain.SSL {
if domain.SSL {
httpsPorts[domain.Port] = struct{}{}
} else {
httpPorts[domain.Port] = struct{}{}
}
}
if hasDefaultPort {
httpsPorts[nginxFull.Install.HttpsPort] = struct{}{}
}
if len(httpsPorts) == 0 {
for port := range httpPorts {
httpsPorts[port] = struct{}{}
}
}
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
httpPort := strconv.Itoa(nginxFull.Install.HttpPort)
httpsPort, err := getHttpsPort(website)
if err != nil {
return err
}
httpPortIPV6 := "[::]:" + httpPort
defaultHttpPort := strconv.Itoa(nginxFull.Install.HttpPort)
defaultHttpPortIPV6 := "[::]:" + defaultHttpPort
for _, port := range httpsPort {
if _, ok := httpPorts[port]; !ok {
server.DeleteListen(strconv.Itoa(port))
}
setListen(server, strconv.Itoa(port), website.IPV6, req.Http3, website.DefaultServer, true)
for port := range httpsPorts {
portStr := strconv.Itoa(port)
server.RemoveListenByBind(portStr)
server.RemoveListenByBind("[::]:" + portStr)
setListen(server, portStr, website.IPV6, req.Http3, website.DefaultServer, true)
}
server.UpdateDirective("http2", []string{"on"})
switch req.HttpConfig {
case constant.HTTPSOnly:
server.RemoveListenByBind(httpPort)
server.RemoveListenByBind(httpPortIPV6)
server.RemoveListenByBind(defaultHttpPort)
server.RemoveListenByBind(defaultHttpPortIPV6)
server.RemoveDirective("if", []string{"($scheme"})
case constant.HTTPToHTTPS:
if !noDefaultPort {
server.UpdateListen(httpPort, website.DefaultServer)
}
if website.IPV6 {
server.UpdateListen(httpPortIPV6, website.DefaultServer)
if hasDefaultPort {
server.UpdateListen(defaultHttpPort, website.DefaultServer)
if website.IPV6 {
server.UpdateListen(defaultHttpPortIPV6, website.DefaultServer)
}
}
server.AddHTTP2HTTPS()
case constant.HTTPAlso:
if !noDefaultPort {
server.UpdateListen(httpPort, website.DefaultServer)
if hasDefaultPort {
server.UpdateListen(defaultHttpPort, website.DefaultServer)
if website.IPV6 {
server.UpdateListen(defaultHttpPortIPV6, website.DefaultServer)
}
}
server.RemoveDirective("if", []string{"($scheme"})
if website.IPV6 {
server.UpdateListen(httpPortIPV6, website.DefaultServer)
}
}
if !req.Hsts {
server.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
}
if !req.Http3 {
for _, port := range httpsPort {
for port := range httpsPorts {
server.RemoveListen(strconv.Itoa(port), "quic")
if website.IPV6 {
httpsPortIPV6 := "[::]:" + strconv.Itoa(port)
@@ -761,7 +780,7 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
if param.Name == "ssl_protocols" {
nginxParams[i].Params = req.SSLProtocol
if len(req.SSLProtocol) == 0 {
nginxParams[i].Params = []string{"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"}
nginxParams[i].Params = []string{"TLSv1.3", "TLSv1.2"}
}
}
if param.Name == "ssl_ciphers" {
@@ -1078,7 +1097,7 @@ func getWebsiteDomains(domains []request.WebsiteDomain, defaultPort int, website
err = buserr.WithMap("ErrPortExist", errMap, nil)
return
}
if common.ScanPort(port) {
if port != 443 && common.ScanPort(port) {
err = buserr.WithDetail("ErrPortInUsed", port, nil)
return
}
@@ -1117,7 +1136,6 @@ func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
func GetSystemSSL() (bool, uint) {
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
if err != nil {
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
return false, 0
}
if sslSetting.Value == "enable" {
@@ -1162,7 +1180,7 @@ func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
return nil
}
func ChangeHSTSConfig(enable bool, website model.Website) error {
func ChangeHSTSConfig(enable bool, http3Enable bool, website model.Website) error {
includeDir := GetSitePath(website, SiteProxyDir)
fileOp := files.NewFileOp()
if !fileOp.Stat(includeDir) {
@@ -1193,6 +1211,11 @@ func ChangeHSTSConfig(enable bool, website model.Website) error {
} else {
location.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
}
if http3Enable {
location.UpdateDirective("add_header", []string{"Alt-Svc", "'h3=\":443\"; ma=2592000'"})
} else {
location.RemoveDirective("add_header", []string{"Alt-Svc", "'h3=\":443\"; ma=2592000'"})
}
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr("ErrUpdateBuWebsite", err)
}
+35 -1
View File
@@ -142,6 +142,40 @@ func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, e
return task, nil
}
func ReNewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) {
return ReNewTask(GetTaskName(resourceName, operate, scope), operate, scope, taskID, resourceID)
}
func ReNewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) {
if taskID == "" {
return NewTask(name, operate, taskScope, taskID, resourceID)
}
logDir := path.Join(global.Dir.TaskDir, taskScope)
if _, err := os.Stat(logDir); err != nil {
if err = os.MkdirAll(logDir, constant.DirPerm); err != nil {
return nil, fmt.Errorf("failed to create log directory: %w", err)
}
}
logPath := path.Join(global.Dir.TaskDir, taskScope, taskID+".log")
file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, constant.FilePerm)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %w", err)
}
writer := bufio.NewWriter(file)
logger := log.New(file, "", log.LstdFlags)
taskModel := &model.Task{
ID: taskID,
Name: name,
Type: taskScope,
LogFile: logPath,
Status: constant.StatusExecuting,
ResourceID: resourceID,
Operate: operate,
}
taskRepo := repo.NewITaskRepo()
task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel, Writer: writer}
return task, nil
}
func (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) {
subTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 30 * time.Minute, Action: action, Rollback: rollback}
t.SubTasks = append(t.SubTasks, subTask)
@@ -158,7 +192,7 @@ func (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback Rollba
}
func (t *Task) AddSubTaskWithAliasAndOps(key string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) {
subTask := &SubTask{RootTask: t, Name: i18n.GetMsgByKey(key), Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}
subTask := &SubTask{RootTask: t, Name: i18n.GetMsgByKey(key), StepAlias: key, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback}
t.SubTasks = append(t.SubTasks, subTask)
}
+169
View File
@@ -0,0 +1,169 @@
package cmd
import (
"fmt"
"os"
"runtime"
"runtime/pprof"
"time"
"github.com/spf13/cobra"
)
var (
cpuProfile string
memProfile string
duration time.Duration
showMemStats bool
showGoroutine bool
)
func init() {
RootCmd.AddCommand(debugCmd)
debugCmd.Flags().StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to file")
debugCmd.Flags().StringVar(&memProfile, "memprofile", "", "write memory profile to file")
debugCmd.Flags().DurationVar(&duration, "duration", 30*time.Second, "duration for cpu profiling")
debugCmd.Flags().BoolVar(&showMemStats, "memstats", false, "show memory statistics")
debugCmd.Flags().BoolVar(&showGoroutine, "goroutine", false, "show goroutine profile")
}
var debugCmd = &cobra.Command{
Use: "debug",
Short: "Performance debugging with pprof",
Long: `Debug command provides CPU and memory profiling capabilities using pprof.
Examples:
# CPU profiling for 30 seconds
1panel-agent debug --cpuprofile=cpu.prof --duration=30s
# Memory profiling
1panel-agent debug --memprofile=mem.prof
# Show memory statistics
1panel-agent debug --memstats
# Show goroutine information
1panel-agent debug --goroutine
# Combined profiling
1panel-agent debug --cpuprofile=cpu.prof --memprofile=mem.prof --memstats`,
RunE: func(cmd *cobra.Command, args []string) error {
if cpuProfile != "" {
if err := startCPUProfile(cpuProfile); err != nil {
return fmt.Errorf("failed to start CPU profile: %v", err)
}
defer stopCPUProfile()
time.Sleep(duration)
fmt.Printf("CPU profile saved to: %s\n", cpuProfile)
}
if memProfile != "" {
if err := writeMemProfile(memProfile); err != nil {
return fmt.Errorf("failed to write memory profile: %v", err)
}
fmt.Printf("Memory profile saved to: %s\n", memProfile)
}
if showMemStats {
printMemStats()
}
if showGoroutine {
printGoroutineInfo()
}
if cpuProfile == "" && memProfile == "" && !showMemStats && !showGoroutine {
printBasicInfo()
}
return nil
},
}
func startCPUProfile(filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
if err := pprof.StartCPUProfile(f); err != nil {
f.Close()
return err
}
return nil
}
func stopCPUProfile() {
pprof.StopCPUProfile()
}
func writeMemProfile(filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
runtime.GC()
if err := pprof.WriteHeapProfile(f); err != nil {
return err
}
return nil
}
func printMemStats() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Println("\n=== Memory Statistics ===")
fmt.Printf("Allocated memory: %d KB (%d MB)\n", m.Alloc/1024, m.Alloc/1024/1024)
fmt.Printf("Total allocated: %d KB (%d MB)\n", m.TotalAlloc/1024, m.TotalAlloc/1024/1024)
fmt.Printf("System memory: %d KB (%d MB)\n", m.Sys/1024, m.Sys/1024/1024)
fmt.Printf("Heap allocated: %d KB (%d MB)\n", m.HeapAlloc/1024, m.HeapAlloc/1024/1024)
fmt.Printf("Heap system: %d KB (%d MB)\n", m.HeapSys/1024, m.HeapSys/1024/1024)
fmt.Printf("Heap idle: %d KB (%d MB)\n", m.HeapIdle/1024, m.HeapIdle/1024/1024)
fmt.Printf("Heap in use: %d KB (%d MB)\n", m.HeapInuse/1024, m.HeapInuse/1024/1024)
fmt.Printf("Heap released: %d KB (%d MB)\n", m.HeapReleased/1024, m.HeapReleased/1024/1024)
fmt.Printf("Heap objects: %d\n", m.HeapObjects)
fmt.Printf("GC runs: %d\n", m.NumGC)
fmt.Printf("GC pause total: %d ns\n", m.PauseTotalNs)
fmt.Printf("Next GC: %d KB (%d MB)\n", m.NextGC/1024, m.NextGC/1024/1024)
fmt.Printf("Last GC: %s\n", time.Unix(0, int64(m.LastGC)).Format("2006-01-02 15:04:05"))
}
func printGoroutineInfo() {
fmt.Println("\n=== Goroutine Information ===")
fmt.Printf("Number of goroutines: %d\n", runtime.NumGoroutine())
fmt.Printf("Number of CPUs: %d\n", runtime.NumCPU())
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
buf := make([]byte, 1024*1024)
stackSize := runtime.Stack(buf, true)
fmt.Printf("\nGoroutine stack trace (first 2048 bytes):\n")
if stackSize > 2048 {
fmt.Printf("%s...\n", buf[:2048])
fmt.Printf("(truncated, total size: %d bytes)\n", stackSize)
} else {
fmt.Printf("%s\n", buf[:stackSize])
}
}
func printBasicInfo() {
fmt.Println("=== Performance Debug Info ===")
fmt.Printf("Go version: %s\n", runtime.Version())
fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
fmt.Printf("CPUs: %d\n", runtime.NumCPU())
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine())
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Memory allocated: %d KB\n", m.Alloc/1024)
fmt.Printf("Total allocations: %d KB\n", m.TotalAlloc/1024)
fmt.Printf("GC runs: %d\n", m.NumGC)
fmt.Println("\nUse --help to see available profiling options.")
}
+14
View File
@@ -0,0 +1,14 @@
package cmd
import (
"github.com/1Panel-dev/1Panel/agent/server"
"github.com/spf13/cobra"
)
var RootCmd = &cobra.Command{
Use: "1panel-agent",
RunE: func(cmd *cobra.Command, args []string) error {
server.Start()
return nil
},
}
+7 -4
View File
@@ -1,9 +1,9 @@
package main
import (
_ "net/http/pprof"
"github.com/1Panel-dev/1Panel/agent/server"
"fmt"
"github.com/1Panel-dev/1Panel/agent/cmd/server/cmd"
"os"
)
// @title 1Panel
@@ -15,5 +15,8 @@ import (
// @host localhost
// @BasePath /api/v2
func main() {
server.Start()
if err := cmd.RootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
+14 -6
View File
@@ -3,14 +3,12 @@ package nginx_conf
import (
"embed"
_ "embed"
"io"
)
//go:embed ssl.conf
var SSL []byte
//go:embed website_default.conf
var WebsiteDefault []byte
//go:embed index.html
var Index []byte
@@ -44,8 +42,18 @@ var PathAuth []byte
//go:embed upstream.conf
var Upstream []byte
//go:embed php_extensions.json
var PHPExtensionsJson []byte
//go:embed sse.conf
var SSE []byte
//go:embed *.json *.conf
var websitesFiles embed.FS
func GetWebsiteFile(filename string) []byte {
file, err := websitesFiles.Open(filename)
if err != nil {
return nil
}
defer file.Close()
res, _ := io.ReadAll(file)
return res
}
+16
View File
@@ -0,0 +1,16 @@
[global]
error_log = /var/log/php/fpm.error.log
log_level = notice
[www]
user = www-data
group = www-data
listen = 0.0.0.0:9000
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.status_path = /status
slowlog = /var/log/php/fpm.slow.log
request_slowlog_timeout = 3
catch_workers_output = yes
@@ -91,7 +91,7 @@
"installed": false
},
{
"name": "Swoole",
"name": "swoole",
"check": "swoole",
"file": "swoole.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
@@ -362,5 +362,19 @@
"file": "gd.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"installed": false
},
{
"name": "bcmath",
"check": "bcmath",
"file": "bcmath.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"installed": false
},
{
"name": "pcntl",
"check": "pcntl",
"file": "pcntl.so",
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
"installed": false
}
]
@@ -15,9 +15,16 @@ server {
access_log /www/sites/domain/log/access.log main;
error_log /www/sites/domain/log/error.log;
location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md){
return 404;
}
location ^~ /.well-known{
allow all;
root /usr/share/nginx/html;
}
if ( $uri ~ "^/\.well-known/.*\.(php|jsp|py|js|css|lua|ts|go|zip|tar\.gz|rar|7z|sql|bak)$" ) {
return 403;
}
}
+2 -2
View File
@@ -13,6 +13,6 @@ const (
ComposeOpStop = "stop"
ComposeOpRestart = "restart"
ComposeOpRemove = "remove"
DaemonJsonPath = "/etc/docker/daemon.json"
)
var DaemonJsonPath = "/etc/docker/daemon.json"
+3 -3
View File
@@ -41,11 +41,11 @@ var RuntimeDefaultVolumes = map[string]string{
var PHPDefaultVolumes = map[string]string{
"${PANEL_WEBSITE_DIR}": "/www/",
"./conf": "/usr/local/etc/php",
"./conf/php.ini": "/usr/local/etc/php/php.ini",
"./conf/conf.d": "/usr/local/etc/php/conf.d",
"./conf/php-fpm.conf": "/usr/local/etc/php-fpm.d/www.conf",
"./conf/php-fpm.conf": "/usr/local/etc/php-fpm.conf",
"./log": "/var/log/php",
"./extensions": "${EXTENSION_DIR}",
"./extensions": "/usr/local/lib/php/extensions",
"./supervisor/supervisord.conf": "/etc/supervisord.conf",
"./supervisor/supervisor.d/php-fpm.ini": "/etc/supervisor.d/php-fpm.ini",
"./supervisor/supervisor.d": "/etc/supervisor.d",
+1 -3
View File
@@ -18,7 +18,7 @@ func NewBackupJob() *backup {
func (b *backup) Run() {
var backups []model.BackupAccount
_ = global.DB.Where("`type` in (?) AND is_public = 0", []string{constant.OneDrive, constant.ALIYUN, constant.GoogleDrive}).Find(&backups)
_ = global.DB.Where("`type` in (?) AND is_public = 0", []string{constant.OneDrive, constant.ALIYUN}).Find(&backups)
if len(backups) == 0 {
return
}
@@ -39,8 +39,6 @@ func (b *backup) Run() {
switch backupItem.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)
}
+8 -3
View File
@@ -1,9 +1,10 @@
package global
type ServerConfig struct {
Base Base `mapstructure:"base"`
RemoteURL RemoteURL `mapstructure:"remote_url"`
Log LogConfig `mapstructure:"log"`
Base Base `mapstructure:"base"`
RemoteURL RemoteURL `mapstructure:"remote_url"`
Log LogConfig `mapstructure:"log"`
DockerConfig DockerConfig
}
type Base struct {
@@ -51,3 +52,7 @@ type LogConfig struct {
LogSuffix string `mapstructure:"log_suffix"`
MaxBackup int `mapstructure:"max_backup"`
}
type DockerConfig struct {
Command string
}
+77 -98
View File
@@ -1,23 +1,21 @@
module github.com/1Panel-dev/1Panel/agent
go 1.23.0
toolchain go1.23.3
go 1.23.9
require (
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
github.com/aws/aws-sdk-go v1.55.0
github.com/compose-spec/compose-go/v2 v2.1.4
github.com/creack/pty v1.1.21
github.com/docker/compose/v2 v2.29.0
github.com/docker/docker v27.1.0+incompatible
github.com/compose-spec/compose-go/v2 v2.6.4
github.com/creack/pty v1.1.24
github.com/docker/compose/v2 v2.37.0
github.com/docker/docker v28.2.2+incompatible
github.com/docker/go-connections v0.5.0
github.com/fsnotify/fsnotify v1.8.0
github.com/fsnotify/fsnotify v1.9.0
github.com/gin-gonic/gin v1.10.0
github.com/glebarez/sqlite v1.11.0
github.com/go-acme/lego/v4 v4.23.1
github.com/go-gormigrate/gormigrate/v2 v2.1.2
github.com/go-playground/validator/v10 v10.22.0
github.com/go-playground/validator/v10 v10.26.0
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-resty/resty/v2 v2.16.5
github.com/go-sql-driver/mysql v1.8.1
@@ -28,11 +26,11 @@ require (
github.com/jinzhu/copier v0.4.0
github.com/jinzhu/gorm v1.9.16
github.com/joho/godotenv v1.5.1
github.com/klauspost/compress v1.17.9
github.com/klauspost/compress v1.18.0
github.com/mholt/archiver/v4 v4.0.0-alpha.8
github.com/minio/minio-go/v7 v7.0.74
github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/opencontainers/image-spec v1.1.0
github.com/opencontainers/image-spec v1.1.1
github.com/oschwald/maxminddb-golang v1.13.1
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
@@ -42,69 +40,66 @@ require (
github.com/shirou/gopsutil/v3 v3.24.5
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.11.0
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.19.0
github.com/studio-b12/gowebdav v0.9.0
github.com/subosito/gotenv v1.6.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.54
github.com/upyun/go-sdk v2.1.0+incompatible
golang.org/x/crypto v0.36.0
golang.org/x/net v0.37.0
golang.org/x/oauth2 v0.28.0
golang.org/x/sys v0.31.0
golang.org/x/text v0.23.0
golang.org/x/crypto v0.39.0
golang.org/x/net v0.41.0
golang.org/x/oauth2 v0.29.0
golang.org/x/sys v0.33.0
golang.org/x/text v0.26.0
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.25.11
gorm.io/gorm v1.30.0
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/baidubce/bce-sdk-go v0.9.223 // indirect
github.com/bodgit/plumbing v1.2.0 // indirect
github.com/bodgit/sevenzip v1.3.0 // indirect
github.com/bodgit/windows v1.0.0 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
github.com/cloudflare/cloudflare-go v0.115.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/connesc/cipherio v0.2.1 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/containerd v1.7.19 // indirect
github.com/containerd/containerd/api v1.7.19 // indirect
github.com/containerd/continuity v0.4.3 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
github.com/containerd/containerd/api v1.9.0 // indirect
github.com/containerd/containerd/v2 v2.1.1 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/containerd/ttrpc v1.2.5 // indirect
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/containerd/platforms v1.0.0-rc.1 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/buildx v0.16.0 // indirect
github.com/docker/cli v27.0.3+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/buildx v0.24.0 // indirect
github.com/docker/cli v28.2.2+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fvbommel/sortorder v1.0.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/fvbommel/sortorder v1.1.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-logr/logr v1.4.2 // indirect
@@ -112,26 +107,22 @@ require (
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
github.com/h2non/filetype v1.1.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@@ -146,29 +137,32 @@ require (
github.com/kr/fs v0.1.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/matishsiao/goInfo v0.0.0-20210923090445-da2e3fa8d45f // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/dns v1.1.64 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/buildkit v0.15.0 // indirect
github.com/moby/buildkit v0.22.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.1.0 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nrdcg/dnspod-go v0.4.0 // indirect
github.com/nrdcg/freemyip v0.3.0 // indirect
github.com/nrdcg/mailinabox v0.2.0 // indirect
@@ -177,15 +171,12 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_golang v1.17.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
@@ -194,17 +185,15 @@ require (
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/theupdateframework/notary v0.7.0 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c // indirect
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 // indirect
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
@@ -215,47 +204,37 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver v1.13.1 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
golang.org/x/mod v0.23.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.30.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
google.golang.org/grpc v1.71.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apimachinery v0.29.2 // indirect
k8s.io/client-go v0.29.2 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/sqlite v1.23.1 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
google.golang.org/grpc v1.72.2 // indirect
google.golang.org/protobuf v1.36.6 // indirect
modernc.org/libc v1.66.1 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
modernc.org/sqlite v1.38.0 // indirect
)
+207 -294
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -92,6 +92,7 @@ ErrCustomApps: 'There is an installed application, please uninstall it first'
ErrCustomRuntimes: 'There is an installed runtime environment, please delete it first'
ErrAppVersionDeprecated: "The {{ .name }} application is not compatible with the current 1Panel version, skipped"
ErrDockerFailed: "The state of Docker is abnormal, please check the service status"
ErrDockerComposeCmdNotFound: "The Docker Compose command does not exist, please install this command on the host machine first"
#file
ErrFileCanNotRead: 'This file does not support preview'
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'Source directory does not exist'
ErrFavoriteExist: 'This path has already been favorited'
ErrInvalidChar: 'Illegal characters are not allowed'
ErrPathNotDelete: 'The selected directory cannot be deleted'
ErrLogFileToLarge: "The log file exceeds 500MB and cannot be opened"
#website
ErrAliasIsExist: 'Alias already exists'
@@ -202,6 +204,9 @@ HandleSystemClean: 'System cache cleanup'
SystemLog: 'System Log'
CutWebsiteLog: 'Rotate Website Log'
FileOrDir: 'Directory / File'
UploadFile: 'Uploading backup file {{ .file }} to {{ .backup }}'
IgnoreBackupErr: 'Backup failed, error: {{ .detail }}, ignoring this error...'
IgnoreUploadErr: 'Upload failed, error: {{ .detail }}, ignoring this error...'
#toolbox
ErrNotExistUser: 'The current user does not exist, please modify and try again!'
@@ -330,6 +335,7 @@ SnapDeleteLoginLog: 'Delete access log'
SnapDeleteMonitor: 'Delete monitoring data'
SnapRemoveSystemIP: 'Remove system IP'
SnapBaseInfo: 'Write 1Panel basic information'
SnapInstallAppImageEmpty: 'No application images selected, skipping...'
SnapInstallApp: 'Backup 1Panel installed applications'
SnapDockerSave: 'Compress installed applications'
SnapLocalBackup: 'Backup 1Panel local backup directory'
+7 -1
View File
@@ -92,6 +92,7 @@ ErrCustomApps: 'インストールされているアプリケーションがあ
ErrCustomRuntimes: 'ランタイム環境がインストールされています。まずそれを削除してください'
ErrAppVersionDeprecated: "{{ .name }} アプリケーションは現在の 1Panel バージョンと互換性がありません、スキップしました"
ErrDockerFailed: "Docker の状態が異常です。サービス状態を確認してください"
ErrDockerComposeCmdNotFound: "Docker Compose コマンドは存在しません。ホストマシンにこのコマンドを先にインストールしてください"
#file
ErrFileCanNotRead: 'このファイルはプレビューをサポートしていません'
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'ソース ディレクトリが存在しません'
ErrFavoriteExist: 'このパスはすでにお気に入りに登録されています'
ErrInvalidChar: '不正な文字は許可されません'
ErrPathNotDelete: '選択されたディレクトリは削除できません'
ErrLogFileToLarge: "ログファイルが500MBを超えており、開くことができません"
#website
ErrAliasIsExist: 'エイリアスがすでに存在します'
@@ -202,6 +204,9 @@ HandleSystemClean: 'システム キャッシュのクリーンアップ'
SystemLog: 'システムログ'
CutWebsiteLog: 'ウェブサイトログのローテーション'
FileOrDir: 'ディレクトリ / ファイル'
UploadFile: 'バックアップファイル {{ .file }} を {{ .backup }} にアップロード中'
IgnoreBackupErr: 'バックアップ失敗、エラー:{{ .detail }}、このエラーを無視します...'
IgnoreUploadErr: 'アップロード失敗、エラー:{{ .detail }}、このエラーを無視します...'
#toolbox
ErrNotExistUser: '現在のユーザーは存在しません。変更してもう一度お試しください。'
@@ -330,7 +335,8 @@ SnapDeleteLoginLog: 'アクセスログを削除する'
SnapDeleteMonitor: '監視データを削除する'
SnapRemoveSystemIP: 'システム IP を削除します'
SnapBaseInfo: '1Panel の基本情報を書き込む'
SnapInstallApp: '1Panel にインストールされたアプリケーションをバックアップする'
SnapInstallAppImageEmpty: 'アプリケーションイメージが選択されていないため、スキップします...'
SnapInstallApp: '1Panelインストール済みアプリケーションのバックアップ'
SnapDockerSave: 'インストールされたアプリケーションを圧縮する'
SnapLocalBackup: '1Panel ローカル バックアップ ディレクトリをバックアップします'
SnapCompressBackup: 'ローカル バックアップ ディレクトリを圧縮する'
+6
View File
@@ -92,6 +92,7 @@ ErrCustomApps: '설치된 애플리케이션이 있습니다. 먼저 제거해
ErrCustomRuntimes: '설치된 런타임 환경이 있습니다. 먼저 삭제해 주세요'
ErrAppVersionDeprecated: "{{ .name }} 응용 프로그램은 현재 1Panel 버전과 호환되지 않아 건너뛰었습니다"
ErrDockerFailed: "Docker의 상태가 비정상입니다. 서비스 상태를 확인하세요"
ErrDockerComposeCmdNotFound: "Docker Compose 명령이 없습니다. 호스트 머신에 먼저 이 명령을 설치하세요"
#파일
ErrFileCanNotRead: '이 파일은 미리보기를 지원하지 않습니다'
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: '소스 디렉토리가 존재하지 않습니다'
ErrFavoriteExist: '이 경로는 이미 즐겨찾기되었습니다'
ErrInvalidChar: '불법 문자는 허용되지 않습니다'
ErrPathNotDelete: '선택한 디렉토리를 삭제할 수 없습니다'
ErrLogFileToLarge: "로그 파일이 500MB를 초과하여 열 수 없습니다"
#웹사이트
ErrAliasIsExist: '별칭이 이미 존재합니다'
@@ -202,6 +204,9 @@ HandleSystemClean: '시스템 캐시 정리'
SystemLog: '시스템 로그'
CutWebsiteLog: '웹사이트 로그 회전'
FileOrDir: '디렉터리 / 파일'
UploadFile: '백업 파일 {{ .file }} 을(를) {{ .backup }}(으)로 업로드 중'
IgnoreBackupErr: 'Sandaran gagal, ralat: {{ .detail }}, abaikan ralat ini...'
IgnoreUploadErr: 'Muat naik gagal, ralat: {{ .detail }}, abaikan ralat ini...'
#도구상자
ErrNotExistUser: '현재 사용자가 존재하지 않습니다. 수정한 후 다시 시도하세요!'
@@ -330,6 +335,7 @@ SnapDeleteLoginLog: '액세스 로그 삭제'
SnapDeleteMonitor: '모니터링 데이터 삭제'
SnapRemoveSystemIP: '시스템 IP 제거'
SnapBaseInfo: '1패널 기본 정보 쓰기'
SnapInstallAppImageEmpty: '애플리케이션 이미지가 선택되지 않아 건너뜁니다...'
SnapInstallApp: '1Panel 설치된 애플리케이션 백업'
SnapDockerSave: '설치된 애플리케이션 압축'
SnapLocalBackup: '1Panel 로컬 백업 디렉토리 백업'
+6 -1
View File
@@ -92,6 +92,7 @@ ErrCustomApps: 'Ada aplikasi yang dipasang, sila nyahpasangnya dahulu'
ErrCustomRuntimes: 'Terdapat persekitaran masa jalan yang dipasang, sila padamkannya dahulu'
ErrAppVersionDeprecated: "Aplikasi {{ .name }} tidak sesuai dengan versi 1Panel saat ini, dilewati"
ErrDockerFailed: "Keadaan Docker tidak normal, sila periksa status perkhidmatan"
ErrDockerComposeCmdNotFound: "Perintah Docker Compose tidak wujud, sila pasang perintah ini di mesin tuan terlebih dahulu"
#fail
ErrFileCanNotRead: 'Fail ini tidak menyokong pratonton'
@@ -202,6 +203,9 @@ HandleSystemClean: 'Pembersihan cache sistem'
SystemLog: 'Log Sistem'
CutWebsiteLog: 'Putar Log Laman Web'
FileOrDir: 'Direktori / Fail'
UploadFile: 'Muat naik fail sandaran {{ .file }} ke {{ .backup }}'
IgnoreBackupErr: 'Sandaran gagal, ralat: {{ .detail }}, abaikan ralat ini...'
IgnoreUploadErr: 'Muat naik gagal, ralat: {{ .detail }}, abaikan ralat ini...'
#kotak alat
ErrNotExistUser: 'Pengguna semasa tidak wujud, sila ubah suai dan cuba lagi!'
@@ -330,7 +334,8 @@ SnapDeleteLoginLog: 'Padam log akses'
SnapDeleteMonitor: 'Padamkan data pemantauan'
SnapRemoveSystemIP: 'Alih keluar IP sistem'
SnapBaseInfo: 'Tulis maklumat asas 1Panel'
SnapInstallApp: 'Aplikasi yang dipasang sandaran 1Panel'
SnapInstallAppImageEmpty: 'Tiada imej aplikasi dipilih, dilangkau...'
SnapInstallApp: 'Sandaran aplikasi terpasang 1Panel'
SnapDockerSave: 'Mampatkan aplikasi yang dipasang'
SnapLocalBackup: 'Sandaran 1Panel direktori sandaran tempatan'
SnapCompressBackup: 'Mampatkan direktori sandaran tempatan'
+7 -1
View File
@@ -92,6 +92,7 @@ ErrCustomApps: 'Há um aplicativo instalado, desinstale-o primeiro'
ErrCustomRuntimes: 'Há um ambiente de execução instalado, exclua-o primeiro'
ErrAppVersionDeprecated: "O aplicativo {{ .name }} não é compatível com a versão atual do 1Panel, ignorado"
ErrDockerFailed: "O estado do Docker está anormal, verifique o status do serviço"
ErrDockerComposeCmdNotFound: "O comando Docker Compose não existe, por favor, instale este comando na máquina host primeiro"
#arquivo
ErrFileCanNotRead: 'Este arquivo não suporta visualização'
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'O diretório de origem não existe'
ErrFavoriteExist: 'Este caminho já foi favorito'
ErrInvalidChar: 'Caracteres ilegais não são permitidos'
ErrPathNotDelete: 'O diretório selecionado não pode ser excluído'
ErrLogFileToLarge: "Fail log melebihi 500MB, tidak boleh dibuka"
#site
ErrAliasIsExist: 'Alias já existe'
@@ -202,6 +204,9 @@ HandleSystemClean: 'Limpeza de cache do sistema'
SystemLog: 'Log do Sistema'
CutWebsiteLog: 'Rotacionar Log do Website'
FileOrDir: 'Diretório / Arquivo'
UploadFile: 'Enviando arquivo de backup {{ .file }} para {{ .backup }}'
IgnoreBackupErr: 'Backup falhou, erro: {{ .detail }}, ignorando este erro...'
IgnoreUploadErr: 'Upload falhou, erro: {{ .detail }}, ignorando este erro...'
#caixa de ferramentas
ErrNotExistUser: 'O usuário atual não existe, modifique e tente novamente!'
@@ -330,7 +335,8 @@ SnapDeleteLoginLog: 'Excluir log de acesso'
SnapDeleteMonitor: 'Excluir dados de monitoramento'
SnapRemoveSystemIP: 'Remover IP do sistema'
SnapBaseInfo: 'Escreva informações básicas do 1Panel'
SnapInstallApp: 'Fazer backup dos aplicativos instalados do 1Panel'
SnapInstallAppImageEmpty: 'Nenhuma imagem de aplicativo selecionada, pulando...'
SnapInstallApp: 'Backup dos aplicativos instalados do 1Panel'
SnapDockerSave: 'Compactar aplicativos instalados'
SnapLocalBackup: 'Backup do diretório de backup local do 1Panel'
SnapCompressBackup: 'Compactar diretório de backup local'
+7 -1
View File
@@ -92,6 +92,7 @@ ErrCustomApps: 'Установлено приложение. Сначала уд
ErrCustomRuntimes: 'Установлена среда выполнения. Сначала удалите ее'
ErrAppVersionDeprecated: "Приложение {{ .name }} несовместимо с текущей версией 1Panel, пропущено"
ErrDockerFailed: "Состояние Docker аномально, проверьте состояние сервиса"
ErrDockerComposeCmdNotFound: "Команда Docker Compose отсутствует, пожалуйста, установите эту команду на хост-машине сначала"
#файл
ErrFileCanNotRead: 'Этот файл не поддерживает предварительный просмотр'
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'Исходный каталог не существуе
ErrFavoriteExist: 'Этот путь уже добавлен в избранное'
ErrInvalidChar: 'Недопустимые символы не допускаются'
ErrPathNotDelete: 'Выбранный каталог не может быть удален'
ErrLogFileToLarge: "Файл журнала превышает 500MB и не может быть открыт"
#веб-сайт
ErrAliasIsExist: 'Псевдоним уже существует'
@@ -202,6 +204,9 @@ HandleSystemClean: 'Очистка системного кэша'
SystemLog: 'Системный лог'
CutWebsiteLog: 'Ротация логов сайта'
FileOrDir: 'Каталог / Файл'
UploadFile: 'Загрузка файла резервной копии {{ .file }} в {{ .backup }}'
IgnoreBackupErr: 'Ошибка резервного копирования: {{ .detail }}, игнорируем эту ошибку...'
IgnoreUploadErr: 'Ошибка загрузки: {{ .detail }}, игнорируем эту ошибку...'
#ящик для инструментов
ErrNotExistUser: 'Текущий пользователь не существует, измените его и повторите попытку!'
@@ -330,7 +335,8 @@ SnapDeleteLoginLog: 'Удалить журнал доступа'
SnapDeleteMonitor: 'Удалить данные мониторинга'
SnapRemoveSystemIP: 'Удалить системный IP'
SnapBaseInfo: 'Записать основную информацию 1Panel'
SnapInstallApp: 'Резервное копирование установленных приложений 1Panel'
SnapInstallAppImageEmpty: 'Образы приложений не выбраны, пропускаем...'
SnapInstallApp: 'Резервное копирование приложений, установленных в 1Panel'
SnapDockerSave: 'Сжать установленные приложения'
SnapLocalBackup: 'Резервное копирование локального каталога резервных копий 1Panel'
SnapCompressBackup: 'Сжать локальный каталог резервных копий'
+54 -49
View File
@@ -11,8 +11,8 @@ ErrNotSupportType: '系統暫不支援目前類型: {{ .name }}'
ErrProxy: '請求錯誤,請檢查該節點狀態: {{ .detail }}'
ErrApiConfigStatusInvalid: 'API 介面禁止存取: {{ .detail }}'
ErrApiConfigKeyInvalid: 'API 介面金鑰錯誤: {{ .detail }}'
ErrApiConfigIPInvalid: '呼叫API 介面IP 不在白名單: {{ .detail }}'
ErrApiConfigDisable: '此介面禁止使用API 介面呼叫: {{ .detail }}'
ErrApiConfigIPInvalid: '呼叫 API 介面 IP 不在白名單: {{ .detail }}'
ErrApiConfigDisable: '此介面禁止使用 API 介面呼叫: {{ .detail }}'
ErrApiConfigKeyTimeInvalid: 'API 介面時間戳記錯誤: {{ .detail }}'
#common
@@ -20,13 +20,13 @@ ErrUsernameIsExist: '使用者名稱已存在'
ErrNameIsExist: '名稱已存在'
ErrDemoEnvironment: '示範伺服器,禁止此操作!'
ErrCmdTimeout: '指令執行逾時!'
ErrCmdIllegal: '執行指令中存在不合法字符,請修改後重試!'
ErrCmdIllegal: '執行指令中存在不合法字元,請修改後重試!'
ErrPortExist: '{{ .port }} 埠已被{{ .type }} [{{ .name }}] 佔用'
TYPE_APP: '應用'
TYPE_RUNTIME: '運作環境'
TYPE_DOMAIN: '網域名稱'
ErrTypePort: '埠{{ .name }} 格式錯誤'
ErrTypePortRange: '連接埠範圍需要在1-65535 之間'
ErrTypePortRange: '連接埠範圍需要在 1-65535 之間'
Success: '成功'
Failed: '失敗'
SystemRestart: '系統重新啟動導致任務中斷'
@@ -48,7 +48,7 @@ ErrDbUserNotValid: '存量資料庫,使用者名稱密碼不符!'
ErrUpdateBuWebsite: '應用程式更新成功,但網站設定檔修改失敗,請檢查設定! '
Err1PanelNetworkFailed: '預設容器網路建立失敗! {{ .detail }}'
ErrFileParse: '應用docker-compose 檔案解析失敗!'
ErrInstallDirNotFound: '安裝目錄不存在,如需卸載,請選擇強制卸載'
ErrInstallDirNotFound: '安裝目錄不存在,如需移除,請選擇強制移除'
AppStoreIsUpToDate: '應用程式商店已經是最新版本'
LocalAppVersionNull: '{{ .name }} 應用程式未同步到版本!無法加入到應用程式列表'
LocalAppVersionErr: '{{ .name }} 同步版本{{ .version }} 失敗! {{ .err }}'
@@ -76,22 +76,22 @@ ErrAppUpgrade: '應用程式{{ .name }} 升級失敗{{ .err }}'
AppRecover: '應用程式{{ .name }} 回滾'
PullImageStart: '開始拉取鏡像{{ .name }}'
PullImageSuccess: '鏡像拉取成功'
AppStoreIsLastVersion: '應用程式商店已經是最新版本'
AppStoreSyncSuccess: '應用程式商店同步成功'
SyncAppDetail: '同步應用程式設定'
AppVersionNotMatch: '{{ .name }} 應用程式需要更高的1Panel 版本,跳過同步'
MoveSiteDir: '目前升級需要遷移OpenResty 網站目錄'
AppVersionNotMatch: '{{ .name }} 應用程式需要更高的 1Panel 版本,跳過同步'
MoveSiteDir: '目前升級需要遷移 OpenResty 網站目錄'
MoveSiteToDir: '遷移網站目錄到{{ .name }}'
ErrMoveSiteDir: '遷移網站目錄失敗'
MoveSiteDirSuccess: '遷移網站目錄成功'
DeleteRuntimePHP: '刪除PHP 運行環境'
CustomAppStoreFileValid: '應用程式商店包需要.tar.gz 格式'
DeleteRuntimePHP: '刪除PHP 執行環境'
CustomAppStoreFileValid: '應用程式商店包需要 .tar.gz 格式'
PullImageTimeout: '拉取鏡像逾時,請嘗試增加鏡像加速或更換其他鏡像加速'
ErrAppIsDown: '{{ .name }} 應用程式狀態異常,請檢查'
ErrCustomApps: '存在已安裝的應用程式,請先解除安裝'
ErrCustomRuntimes: '存在已安裝的運作環境,請先刪除'
ErrAppVersionDeprecated: "{{ .name }} 應用不適配當前 1Panel 版本,跳過"
ErrAppVersionDeprecated: "{{ .name }} 應用不適配目前 1Panel 版本,跳過"
ErrDockerFailed: "Docker 狀態異常,請檢查服務狀態"
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,請先在宿主機安裝此命令"
#file
ErrFileCanNotRead: '此檔案不支援預覽'
@@ -107,13 +107,14 @@ ErrSourcePathNotFound: '來源目錄不存在'
ErrFavoriteExist: '已收藏此路徑'
ErrInvalidChar: '禁止使用非法字元'
ErrPathNotDelete: '所選目錄不可刪除'
ErrLogFileToLarge: "日志文件超过 500M,無法打開"
#website
ErrAliasIsExist: '代號已存在'
ErrBackupMatch: '該備份檔案與目前網站部分資料不符{{ .detail }}'
ErrBackupExist: '該備份檔案對應部分來源資料不存在{{ .detail }}'
ErrPHPResource: '本地運行環境不支援切換! '
ErrPathPermission: 'index 目錄下偵測到非1000:1000 權限資料夾,可能導致網站存取Access denied 錯誤,請點擊上方儲存按鈕'
ErrPHPResource: '本機執行環境不支援切換! '
ErrPathPermission: 'index 目錄下偵測到非 1000:1000 權限資料夾,可能導致網站存取 Access denied 錯誤,請點擊上方儲存按鈕'
ErrDomainIsUsed: '網域名稱已被網站【{{ .name }}】使用'
ErrDomainFormat: '{{ .name }} 網域格式不正確'
ErrDefaultAlias: 'default 為保留代號,請使用其他代號'
@@ -126,13 +127,13 @@ ErrSSLValid: '證書文件異常,請檢查證書狀態!'
#ssl
ErrSSLCannotDelete: '{{ .name }} 憑證正在被網站使用,無法刪除'
ErrAccountCannotDelete: '帳號關聯證書,無法刪除'
ErrSSLApply: '憑證續簽成功,openresty reload失敗,請檢查設定!'
ErrEmailIsExist: '郵箱已存在'
ErrSSLApply: '憑證續簽成功,openresty reload 失敗,請檢查設定!'
ErrEmailIsExist: '信箱已存在'
ErrSSLKeyNotFound: '私鑰檔案不存在'
ErrSSLCertificateNotFound: '憑證檔案不存在'
ErrSSLKeyFormat: '私鑰檔案校驗失敗'
ErrSSLCertificateFormat: '憑證檔案格式錯誤,請使用pem 格式'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或EabHmacKey 不能為空'
ErrSSLCertificateFormat: '憑證檔案格式錯誤,請使用 pem 格式'
ErrEabKidOrEabHmacKeyCannotBlank: 'EabKid 或 EabHmacKey 不能為空'
ErrOpenrestyNotFound: 'Http 模式需要先安裝Openresty'
ApplySSLStart: '開始申請證書,網域名稱[{{ .domain }}] 申請方式[{{ .type }}] '
dnsAccount: 'DNS 自動'
@@ -163,7 +164,7 @@ ErrRemoteExist: '遠端資料庫已存在該名稱,請修改後重試'
ErrLocalExist: '本機資料庫已存在該名稱,請修改後重試'
#redis
ErrTypeOfRedis: '恢復檔案類型與目前持久化方式不符,請修改後重試'
ErrTypeOfRedis: '復原檔案類型與目前持久化方式不符,請修改後重試'
#container
ErrInUsed: '{{ .detail }} 正被使用,無法刪除'
@@ -176,21 +177,21 @@ ErrPgImagePull: '鏡像拉取逾時,請配置鏡像加速或手動拉取{{ .na
ErrFileNotExist: '{{ .detail }} 檔案不存在!請檢查來源檔案完整性!'
ErrImageBuildErr: '鏡像build 失敗'
ErrImageExist: '鏡像已存在!'
ErrDelWithWebsite: '運行環境已經關聯網站,無法刪除'
ErrDelWithWebsite: '執行環境已經關聯網站,無法刪除'
ErrRuntimeStart: '啟動失敗'
ErrPackageJsonNotFound: 'package.json 檔案不存在'
ErrScriptsNotFound: '沒有在package.json 中找到scripts 設定項'
ErrContainerNameNotFound: '無法取得容器名稱,請檢查.env 檔案'
ErrNodeModulesNotFound: 'node_modules 資料夾不存在!請編輯運行環境或等待運行環境啟動成功'
ErrScriptsNotFound: '沒有在 package.json 中找到 scripts 設定項'
ErrContainerNameNotFound: '無法取得容器名稱,請檢查 .env 檔案'
ErrNodeModulesNotFound: 'node_modules 資料夾不存在!請編輯執行環境或等待執行環境啟動成功'
ErrContainerNameIsNull: '容器名稱不存在'
ErrPHPPortIsDefault: "9000 端口為默認端口,請修改後重試"
ErrPHPRuntimePortFailed: "{{ .name }} 端口已被當前運行環境使用,請修改後重試"
ErrPHPPortIsDefault: "9000 埠為預設埠,請修改後重試"
ErrPHPRuntimePortFailed: "{{ .name }} 埠已被目前執行環境使用,請修改後重試"
#tool
ErrConfigNotFound: '設定檔不存在'
ErrConfigParse: '設定檔格式有誤'
ErrConfigIsNull: '設定檔不允許為空'
ErrConfigDirNotFound: '運行目錄不存在'
ErrConfigDirNotFound: '執行目錄不存在'
ErrConfigAlreadyExist: '已存在同名設定檔'
ErrUserFindErr: '使用者{{ .name }} 尋找失敗{{ .err }}'
@@ -202,6 +203,9 @@ HandleSystemClean: '系統快取清理'
SystemLog: '系統日誌'
CutWebsiteLog: '切割網站日誌'
FileOrDir: '目錄 / 檔案'
UploadFile: '上傳備份文件 {{ .file }} 到 {{ .backup }}'
IgnoreBackupErr: '備份失敗,錯誤:{{ .detail }},忽略本次錯誤...'
IgnoreUploadErr: '上傳失敗,錯誤:{{ .detail }},忽略本次錯誤...'
#toolbox
ErrNotExistUser: '目前使用者不存在,請修改後重試!'
@@ -216,7 +220,7 @@ ErrRuleNotExist: '規則不存在'
ErrParseIP: 'IP 格式錯誤'
ErrDefaultIP: 'default 為保留名稱,請更換其他名稱'
ErrGroupInUse: 'IP 群組被駭/白名單使用,無法刪除'
ErrIPGroupAclUse: "IP 群組被網站 {{ .name }} 自定義規則使用,無法刪除"
ErrIPGroupAclUse: "IP 群組被網站 {{ .name }} 自訂規則使用,無法刪除"
ErrGroupExist: 'IP 群組名稱已存在'
ErrIPRange: 'IP 範圍錯誤'
ErrIPExist: 'IP 已存在'
@@ -272,14 +276,14 @@ TaskRetry: '開始第{{ .name }} 次重試'
SubTaskSuccess: '{{ .name }} 成功'
SubTaskFailed: '{{ .name }} 失敗: {{ .err }}'
TaskInstall: '安裝'
TaskUninstall: '卸載'
TaskUninstall: '移除'
TaskCreate: '建立'
TaskDelete: '刪除'
TaskUpgrade: '升級'
TaskUpdate: '更新'
TaskRestart: '重啟'
TaskBackup: '備份'
TaskRecover: '恢復'
TaskRecover: '復原'
TaskRollback: '回滾'
TaskPull: '拉取'
TaskBuild: '建置'
@@ -292,7 +296,7 @@ Database: '資料庫'
ConfigFTP: '建立FTP 使用者{{ .name }}'
ConfigOpenresty: '建立Openresty 設定檔'
InstallAppSuccess: '應用程式{{ .name }} 安裝成功'
ConfigRuntime: '設定運行環境'
ConfigRuntime: '設定執行環境'
ConfigApp: '設定應用程式'
SuccessStatus: '{{ .name }} 成功'
FailedStatus: '{{ .name }} 失敗{{ .err }}'
@@ -310,15 +314,15 @@ AppLink: '關聯應用程式'
EnableSSL: '開啟HTTPS'
AppStore: '應用程式商店'
TaskSync: '同步'
LocalApp: '本地應用'
LocalApp: '本機應用'
SubTask: '子任務'
RuntimeExtension: '運行環境擴充'
RuntimeExtension: '執行環境擴充'
TaskIsExecuting: '任務正在運作'
CustomAppstore: '自訂應用程式倉庫'
# task - ai
OllamaModelPull: '拉取Ollama 模型{{ .name }} '
OllamaModelSize: '取得Ollama 模型{{ .name }} 大小'
OllamaModelPull: '拉取 Ollama 模型{{ .name }} '
OllamaModelSize: '取得 Ollama 模型{{ .name }} 大小'
# task - snapshot
Snapshot: '快照'
@@ -330,7 +334,8 @@ SnapDeleteLoginLog: '刪除存取日誌'
SnapDeleteMonitor: '刪除監控資料'
SnapRemoveSystemIP: '移除系統IP'
SnapBaseInfo: '寫入1Panel 基本資料'
SnapInstallApp: '備份1Panel 已安裝應用程式'
SnapInstallAppImageEmpty: '當前未勾選應用鏡像,跳過...'
SnapInstallApp: '備份 1Panel 已安裝應用'
SnapDockerSave: '壓縮已安裝應用'
SnapLocalBackup: '備份1Panel 本機備份目錄'
SnapCompressBackup: '壓縮本機備份目錄'
@@ -347,7 +352,7 @@ SnapLoadBackup: '取得備份帳號資訊'
SnapUploadTo: '上傳快照檔案到{{ .name }}'
SnapUploadRes: '上傳快照檔案到{{ .name }}'
SnapshotRecover: '快照恢復'
SnapshotRecover: '快照復原'
RecoverDownload: '下載快照檔案'
Download: '下載'
RecoverDownloadAccount: '取得快照下載備份帳號{{ .name }}'
@@ -358,19 +363,19 @@ Readjson: '讀取快照內Json 檔案'
ReadjsonPath: '取得快照內Json 檔案路徑'
ReadjsonContent: '讀取Json 檔案'
ReadjsonMarshal: 'Json 轉義處理'
RecoverApp: '恢復已安裝應用程式'
RecoverWebsite: '恢復網站目錄'
RecoverAppImage: '恢復快照鏡像備份'
RecoverCompose: '恢復其他編排內容'
RecoverComposeList: '取得所有待恢復編排'
RecoverComposeItem: '恢復編排{{ .name }}'
RecoverApp: '復原已安裝應用程式'
RecoverWebsite: '復原網站目錄'
RecoverAppImage: '復原快照鏡像備份'
RecoverCompose: '復原其他編排內容'
RecoverComposeList: '取得所有待復原編排'
RecoverComposeItem: '復原編排{{ .name }}'
RecoverAppEmpty: '快照檔案中未發現應用程式鏡像備份'
RecoverBaseData: '恢復基礎資料及檔案'
RecoverBaseData: '復原基礎資料及檔案'
RecoverDaemonJsonEmpty: '快照檔案及目前機器都不存在容器配置daemon.json 檔案'
RecoverDaemonJson: '恢復容器配置daemon.json 檔案'
RecoverDBData: '恢復資料庫資料'
RecoverDaemonJson: '復原容器配置daemon.json 檔案'
RecoverDBData: '復原資料庫資料'
RecoverBackups: '還原本機備份目錄'
RecoverPanelData: '恢復資料目錄'
RecoverPanelData: '復原資料目錄'
# task - container
ContainerNewCliet: '初始化Docker Client'
@@ -378,7 +383,7 @@ ContainerImagePull: '拉取容器鏡像{{ .name }}'
ContainerRemoveOld: '刪除原容器{{ .name }}'
ContainerImageCheck: '檢查鏡像是否正常拉取'
ContainerLoadInfo: '取得容器基本資訊'
ContainerRecreate: '容器更新失敗,現在開始恢復原容器'
ContainerRecreate: '容器更新失敗,現在開始復原原容器'
ContainerCreate: '建立新容器{{ .name }}'
ContainerCreateFailed: '容器建立失敗,刪除失敗容器'
ContainerStartCheck: '檢查容器是否已啟動'
@@ -405,8 +410,8 @@ DeCompressFile: '解壓縮檔案{{ .name }}'
ErrCheckValid: '校驗備份檔失敗,{{ .name }}'
Rollback: '回滾'
websiteDir: '網站目錄'
RecoverFailedStartRollBack: '恢復失敗,開始回溯'
AppBackupFileIncomplete: '備份檔案不完整缺少app.json 或app.tar.gz 檔案'
RecoverFailedStartRollBack: '復原失敗,開始回溯'
AppBackupFileIncomplete: '備份檔案不完整缺少 app.json 或 app.tar.gz 檔案'
AppAttributesNotMatch: '應用程式類型或名稱不一致'
#alert
@@ -417,4 +422,4 @@ ErrAlertSync: '警告訊息同步錯誤,請檢查後重試!'
ErrAlertRemote: '警告訊息遠端錯誤,請檢查後重試!'
#task - runtime
ErrInstallExtension: "已有安裝任務正在進行,請等待任務結束"
ErrInstallExtension: "已有安裝任務正在進行,請等待任務結束"
+6
View File
@@ -91,6 +91,7 @@ ErrCustomApps: "存在已经安装的应用,请先卸载"
ErrCustomRuntimes: "存在已经安装的运行环境,请先删除"
ErrAppVersionDeprecated: " {{ .name }} 应用不适配当前 1Panel 版本,跳过"
ErrDockerFailed: "Docker 状态异常,请检查服务状态"
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,请先在宿主机安装此命令"
#file
ErrFileCanNotRead: "此文件不支持预览"
@@ -106,6 +107,7 @@ ErrSourcePathNotFound: "源目录不存在"
ErrFavoriteExist: "已收藏此路径"
ErrInvalidChar: "禁止使用非法字符"
ErrPathNotDelete: "所选目录不可删除"
ErrLogFileToLarge: "日志文件超过 500M,无法打开"
#website
ErrAliasIsExist: "代号已存在"
@@ -201,6 +203,9 @@ HandleSystemClean: "系统缓存清理"
SystemLog: "系统日志"
CutWebsiteLog: "切割网站日志"
FileOrDir: "目录 / 文件"
UploadFile: "上传备份文件 {{ .file }} 到 {{ .backup }}"
IgnoreBackupErr: "备份失败,错误:{{ .detail }},忽略本次错误..."
IgnoreUploadErr: "上传失败,错误:{{ .detail }},忽略本次错误..."
#toolbox
ErrNotExistUser: "当前用户不存在,请修改后重试!"
@@ -329,6 +334,7 @@ SnapDeleteLoginLog: "删除访问日志"
SnapDeleteMonitor: "删除监控数据"
SnapRemoveSystemIP: "移除系统 IP"
SnapBaseInfo: "写入 1Panel 基本信息"
SnapInstallAppImageEmpty: "当前未勾选应用镜像,跳过..."
SnapInstallApp: "备份 1Panel 已安装应用"
SnapDockerSave: "压缩已安装应用"
SnapLocalBackup: "备份 1Panel 本地备份目录"
+15
View File
@@ -6,6 +6,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/service"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/common"
)
func Init() {
@@ -15,6 +16,7 @@ func Init() {
go syncSSL()
go syncTask()
go initAcmeAccount()
go checkDockerCompose()
}
func syncApp() {
@@ -71,3 +73,16 @@ func initAcmeAccount() {
}
}
}
func checkDockerCompose() {
dockerComposCmd := common.GetDockerComposeCommand()
if dockerComposCmd == "" {
global.LOG.Errorf("Docker Compose command not found, please install Docker Compose Plugin")
return
}
global.CONF.DockerConfig.Command = dockerComposCmd
if err := service.NewISettingService().Update("DockerComposeCommand", dockerComposCmd); err != nil {
global.LOG.Errorf("update docker compose command error: %s", err.Error())
return
}
}
+16 -5
View File
@@ -2,12 +2,14 @@ package hook
import (
"os"
"strings"
"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/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
)
@@ -18,6 +20,8 @@ func Init() {
handleOllamaModelStatus()
loadLocalDir()
initDockerConf()
}
func initGlobalData() {
@@ -92,11 +96,7 @@ func handleCronJobAlert(cronjob *model.Cronjob) {
EntryID: cronjob.ID,
Param: cronjob.Type,
}
err := xpack.PushAlert(pushAlert)
if err != nil {
global.LOG.Errorf("cronjob alert push failed, err: %v", err)
return
}
_ = xpack.PushAlert(pushAlert)
}
func loadLocalDir() {
@@ -113,3 +113,14 @@ func loadLocalDir() {
}
}
}
func initDockerConf() {
stdout, err := cmd.RunDefaultWithStdoutBashC("which docker")
if err != nil {
return
}
dockerPath := stdout
if strings.Contains(dockerPath, "snap") {
constant.DaemonJsonPath = "/var/snap/docker/current/config/daemon.json"
}
}
+5
View File
@@ -24,6 +24,11 @@ func InitAgentDB() {
migrations.InitBackup,
migrations.InitDefault,
migrations.UpdateWebsiteExpireDate,
migrations.UpdateRuntime,
migrations.AddSnapshotRule,
migrations.UpdatePHPRuntime,
migrations.AddSnapshotIgnore,
migrations.InitAppLauncher,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+49 -3
View File
@@ -185,9 +185,6 @@ var InitSetting = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "FileRecycleBin", Value: constant.StatusEnable}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "SnapshotIgnore", Value: "*.sock"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "LocalSSHConn", Value: ""}).Error; err != nil {
return err
@@ -314,3 +311,52 @@ var UpdateWebsiteExpireDate = &gormigrate.Migration{
return nil
},
}
var UpdateRuntime = &gormigrate.Migration{
ID: "20250624-update-runtime",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(
&model.Runtime{},
)
},
}
var AddSnapshotRule = &gormigrate.Migration{
ID: "20250703-add-snapshot-rule",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(
&model.Cronjob{},
)
},
}
var UpdatePHPRuntime = &gormigrate.Migration{
ID: "20250702-update-php-runtime",
Migrate: func(tx *gorm.DB) error {
service.HandleOldPHPRuntime()
return nil
},
}
var AddSnapshotIgnore = &gormigrate.Migration{
ID: "20250628-add-snapshot-ignore",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(
&model.Snapshot{},
)
},
}
var InitAppLauncher = &gormigrate.Migration{
ID: "20250702-init-app-launcher",
Migrate: func(tx *gorm.DB) error {
launchers := []string{"openresty", "mysql", "halo", "redis", "maxkb", "wordpress"}
for _, val := range launchers {
var item model.AppLauncher
_ = tx.Model(&model.AppLauncher{}).Where("key = ?", val).First(&item).Error
if item.ID == 0 {
item.Key = val
_ = tx.Create(&item).Error
}
}
return nil
},
}
-57
View File
@@ -1,57 +0,0 @@
package middleware
import (
"net/http"
"strings"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/gin-gonic/gin"
)
var whiteUrlList = map[string]struct{}{
"/api/v2/auth/login": {},
"/api/v2/websites/config": {},
"/api/v2/websites/waf/config": {},
"/api/v2/files/loadfile": {},
"/api/v2/files/size": {},
"/api/v2/logs/operation": {},
"/api/v2/logs/login": {},
"/api/v2/auth/logout": {},
"/api/v1/dashboard/current": {},
"/api/v2/apps/installed/loadport": {},
"/api/v2/apps/installed/check": {},
"/api/v2/apps/installed/conninfo": {},
"/api/v2/databases/load/file": {},
"/api/v2/databases/variables": {},
"/api/v2/databases/status": {},
"/api/v2/databases/baseinfo": {},
"/api/v2/waf/attack/stat": {},
"/api/v2/waf/config/website": {},
"/api/v2/monitor/stat": {},
"/api/v2/monitor/visitors": {},
"/api/v2/monitor/visitors/loc": {},
"/api/v2/monitor/qps": {},
}
func DemoHandle() gin.HandlerFunc {
return func(c *gin.Context) {
if strings.Contains(c.Request.URL.Path, "search") || c.Request.Method == http.MethodGet {
c.Next()
return
}
if _, ok := whiteUrlList[c.Request.URL.Path]; ok {
c.Next()
return
}
c.JSON(http.StatusInternalServerError, dto.Response{
Code: http.StatusInternalServerError,
Message: buserr.New("ErrDemoEnvironment").Error(),
})
c.Abort()
}
}
+2
View File
@@ -33,6 +33,8 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
baRouter.POST("/operate", baseApi.ContainerOperation)
baRouter.POST("/prune", baseApi.ContainerPrune)
baRouter.POST("/users", baseApi.LoadContainerUsers)
baRouter.GET("/repo", baseApi.ListRepo)
baRouter.POST("/repo/status", baseApi.CheckRepoStatus)
baRouter.POST("/repo/search", baseApi.SearchRepo)
+2
View File
@@ -48,5 +48,7 @@ func (r *RuntimeRouter) InitRouter(Router *gin.RouterGroup) {
groupRouter.GET("/supervisor/process/:id", baseApi.GetSupervisorProcess)
groupRouter.POST("/supervisor/process", baseApi.OperateSupervisorProcess)
groupRouter.POST("/supervisor/process/file", baseApi.OperateSupervisorProcessFile)
groupRouter.POST("/remark", baseApi.UpdateRuntimeRemark)
}
}
+2 -1
View File
@@ -497,6 +497,7 @@ func (a *aliClient) completeUpload(uploadID, fileID string) error {
type tokenResp struct {
RefreshToken string `json:"refresh_token"`
AccessToken string `json:"access_token"`
}
func loadToken(refresh_token string) (string, error) {
@@ -524,7 +525,7 @@ func loadToken(refresh_token string) (string, error) {
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
return "", err
}
return respItem.RefreshToken, nil
return respItem.AccessToken, nil
}
func RefreshALIToken(varMap map[string]interface{}) (string, error) {
+12 -2
View File
@@ -6,6 +6,7 @@ import (
"net/http"
"net/url"
"os"
"regexp"
cosSDK "github.com/tencentyun/cos-go-sdk-v5"
)
@@ -18,6 +19,7 @@ type cosClient struct {
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
region := loadParamFromVars("region", vars)
endpoint := loadParamFromVars("endpoint", vars)
accessKey := loadParamFromVars("accessKey", vars)
secretKey := loadParamFromVars("secretKey", vars)
bucket := loadParamFromVars("bucket", vars)
@@ -26,7 +28,15 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
scType = "Standard"
}
u, _ := url.Parse(fmt.Sprintf("https://cos.%s.myqcloud.com", region))
endpointType := "cos"
if len(endpoint) != 0 {
re := regexp.MustCompile(`.*cos-dualstack\..*`)
if re.MatchString(endpoint) {
endpointType = "cos-dualstack"
}
}
u, _ := url.Parse(fmt.Sprintf("https://%s.%s.myqcloud.com", endpointType, region))
b := &cosSDK.BaseURL{BucketURL: u}
client := cosSDK.NewClient(b, &http.Client{
Transport: &cosSDK.AuthorizationTransport{
@@ -36,7 +46,7 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
})
if len(bucket) != 0 {
u2, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, region))
u2, _ := url.Parse(fmt.Sprintf("https://%s.%s.%s.myqcloud.com", bucket, endpointType, region))
b2 := &cosSDK.BaseURL{BucketURL: u2}
clientWithBucket := cosSDK.NewClient(b2, &http.Client{
Transport: &cosSDK.AuthorizationTransport{
@@ -6,7 +6,6 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"strconv"
@@ -58,11 +57,9 @@ func (g *googleDriveClient) Delete(pathItem string) (bool, error) {
if len(fileInfo.ID) == 0 {
return false, fmt.Errorf("no such file %s", pathItem)
}
res, err := g.googleRequest("https://www.googleapis.com/drive/v3/files/"+fileInfo.ID, http.MethodDelete, nil, nil)
if err != nil {
if _, err = g.googleRequest("https://www.googleapis.com/drive/v3/files/"+fileInfo.ID, http.MethodDelete, nil, nil); err != nil {
return false, err
}
fmt.Println(string(res))
return true, nil
}
@@ -92,7 +89,6 @@ func (g *googleDriveClient) Upload(src, target string) (bool, error) {
}
urlItem := "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
client := resty.New()
client.SetProxy("http://127.0.0.1:7890")
resp, err := client.R().
SetHeader("Authorization", "Bearer "+g.accessToken).
SetBody(data).
@@ -101,14 +97,51 @@ func (g *googleDriveClient) Upload(src, target string) (bool, error) {
return false, err
}
uploadUrl := resp.Header().Get("location")
if _, err := g.googleRequest(uploadUrl, http.MethodPut, func(req *resty.Request) {
req.SetHeader("Content-Length", strconv.FormatInt(fileInfo.Size(), 10)).SetBody(file)
}, nil); err != nil {
return false, err
buf := make([]byte, 5*1024*1024)
var offset int64 = 0
for {
n, err := file.Read(buf)
if err != nil && err != io.EOF {
return false, fmt.Errorf("read file failed, err: %v", err)
}
if n == 0 {
break
}
chunk := buf[:n]
start := offset
end := offset + int64(n) - 1
if err = uploadChunk(client, uploadUrl, chunk, start, end, fileInfo.Size()); err != nil {
return false, err
}
offset += int64(n)
if err == io.EOF {
break
}
}
return true, nil
}
func uploadChunk(client *resty.Client, uploadURL string, chunk []byte, start, end, total int64) error {
contentRange := fmt.Sprintf("bytes %d-%d/%d", start, end, total)
resp, err := client.R().
SetHeader("Content-Length", strconv.Itoa(len(chunk))).
SetHeader("Content-Range", contentRange).
SetBody(chunk).
Put(uploadURL)
if err != nil {
return fmt.Errorf("new request for upload chunk failed: %v", err)
}
if resp.StatusCode() != 200 && resp.StatusCode() != 308 {
return fmt.Errorf("upload chunk failed: %s, %s", resp.Status(), string(resp.Body()))
}
return nil
}
func (g *googleDriveClient) Download(src, target string) (bool, error) {
src = path.Join("/root", src)
fileInfo, err := g.loadFileWithName(src)
@@ -220,12 +253,7 @@ func (g *googleDriveClient) handleDownload(urlItem string, target string) error
return err
}
req.Header.Add("Authorization", "Bearer "+g.accessToken)
proxyURL, _ := url.Parse("http://127.0.0.1:7890")
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}
client := &http.Client{}
defer client.CloseIdleConnections()
response, err := client.Do(req)
if err != nil {
@@ -343,7 +371,6 @@ type reqCallback func(req *resty.Request)
func (g *googleDriveClient) googleRequest(urlItem, method string, callback reqCallback, resp interface{}) ([]byte, error) {
client := resty.New()
client.SetProxy("http://127.0.0.1:7890")
req := client.R()
req.SetHeader("Authorization", "Bearer "+g.accessToken)
if callback != nil {
@@ -374,7 +401,6 @@ type googleTokenRes struct {
func RefreshGoogleToken(grantType string, tokenType string, varMap map[string]interface{}) (string, error) {
client := resty.New()
client.SetProxy("http://127.0.0.1:7890")
data := map[string]interface{}{
"client_id": loadParamFromVars("client_id", varMap),
"client_secret": loadParamFromVars("client_secret", varMap),
@@ -402,7 +428,6 @@ func RefreshGoogleToken(grantType string, tokenType string, varMap map[string]in
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
return "", err
}
fmt.Println(respItem)
if tokenType == "accessToken" {
return respItem.AccessToken, nil
}
@@ -41,6 +41,8 @@ func NewCloudStorageClient(backupType string, vars map[string]interface{}) (Clou
return client.NewUpClient(vars)
case constant.ALIYUN:
return client.NewALIClient(vars)
case constant.GoogleDrive:
return client.NewGoogleDriveClient(vars)
default:
return nil, buserr.WithName("ErrNotSupportType", backupType)
}
+29 -19
View File
@@ -9,6 +9,7 @@ import (
"os"
"os/exec"
"strings"
"syscall"
"time"
"github.com/1Panel-dev/1Panel/agent/app/task"
@@ -17,12 +18,13 @@ import (
)
type CommandHelper struct {
workDir string
outputFile string
scriptPath string
timeout time.Duration
taskItem *task.Task
logger *log.Logger
workDir string
outputFile string
scriptPath string
timeout time.Duration
taskItem *task.Task
logger *log.Logger
IgnoreExist1 bool
}
type Option func(*CommandHelper)
@@ -65,6 +67,7 @@ func (c *CommandHelper) RunBashCWithArgs(arg ...string) error {
_, err := c.run("bash", arg...)
return err
}
func (c *CommandHelper) RunBashC(command string) error {
std, err := c.run("bash", "-c", command)
if err != nil {
@@ -120,9 +123,9 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
cmd.Stdout = file
cmd.Stderr = file
} else if len(c.scriptPath) != 0 {
cmd = exec.Command("bash", c.scriptPath)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
cmd = exec.Command("bash", c.scriptPath)
} else {
cmd.Stdout = &stdout
cmd.Stderr = &stderr
@@ -133,23 +136,17 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
cmd.Dir = c.workDir
}
err := cmd.Run()
if c.taskItem != nil {
customWriter.Flush()
}
if c.timeout != 0 {
err := cmd.Run()
if c.taskItem != nil {
customWriter.Flush()
}
if ctx != nil && errors.Is(ctx.Err(), context.DeadlineExceeded) {
return "", buserr.New("ErrCmdTimeout")
}
if err != nil {
return handleErr(stdout, stderr, err)
}
return stdout.String(), nil
}
err := cmd.Run()
if err != nil {
return handleErr(stdout, stderr, err)
return handleErr(stdout, stderr, c.IgnoreExist1, err)
}
return stdout.String(), nil
}
@@ -184,6 +181,11 @@ func WithScriptPath(scriptPath string) Option {
s.scriptPath = scriptPath
}
}
func WithIgnoreExist1() Option {
return func(s *CommandHelper) {
s.IgnoreExist1 = true
}
}
type CustomWriter struct {
taskItem *task.Task
@@ -209,7 +211,15 @@ func (cw *CustomWriter) Flush() {
}
}
func handleErr(stdout, stderr bytes.Buffer, err error) (string, error) {
func handleErr(stdout, stderr bytes.Buffer, ignoreExist1 bool, err error) (string, error) {
var exitError *exec.ExitError
if ignoreExist1 && errors.As(err, &exitError) {
if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
if status.ExitStatus() == 1 {
return "", nil
}
}
}
errMsg := ""
if len(stderr.String()) != 0 {
errMsg = fmt.Sprintf("stderr: %s", stderr.String())
+17
View File
@@ -6,6 +6,7 @@ import (
"io"
mathRand "math/rand"
"net"
"os/exec"
"reflect"
"regexp"
"sort"
@@ -409,3 +410,19 @@ func GetSystemVersion(versionString string) string {
}
return ""
}
func isCommandAvailable(name string, args ...string) bool {
execCmd := exec.Command(name, args...)
err := execCmd.Run()
return err == nil
}
func GetDockerComposeCommand() string {
if isCommandAvailable("docker", "compose", "version") {
return "docker compose"
}
if isCommandAvailable("docker-compose", "version") {
return "docker-compose"
}
return ""
}
+39 -7
View File
@@ -1,40 +1,72 @@
package compose
import (
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/common"
"time"
)
func checkCmd() error {
if global.CONF.DockerConfig.Command == "" {
dockerComposCmd := common.GetDockerComposeCommand()
if dockerComposCmd == "" {
return buserr.New("ErrDockerComposeCmdNotFound")
}
global.CONF.DockerConfig.Command = dockerComposCmd
}
return nil
}
func Up(filePath string) (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut("docker compose -f %s up -d", 20*time.Minute, filePath)
if err := checkCmd(); err != nil {
return "", err
}
stdout, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut(global.CONF.DockerConfig.Command+" -f %s up -d", 20*time.Minute, filePath)
return stdout, err
}
func Down(filePath string) (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s down --remove-orphans", filePath)
if err := checkCmd(); err != nil {
return "", err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s down --remove-orphans", filePath)
return stdout, err
}
func Stop(filePath string) (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s stop", filePath)
if err := checkCmd(); err != nil {
return "", err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s stop", filePath)
return stdout, err
}
func Restart(filePath string) (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s restart", filePath)
if err := checkCmd(); err != nil {
return "", err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s restart", filePath)
return stdout, err
}
func Operate(filePath, operation string) (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s %s", filePath, operation)
if err := checkCmd(); err != nil {
return "", err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s %s", filePath, operation)
return stdout, err
}
func DownAndUp(filePath string) (string, error) {
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s down", filePath)
if err := checkCmd(); err != nil {
return "", err
}
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s down", filePath)
if err != nil {
return stdout, err
}
stdout, err = cmd.RunDefaultWithStdoutBashCf("docker compose -f %s up -d", filePath)
stdout, err = cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s up -d", filePath)
return stdout, err
}
+2 -2
View File
@@ -144,14 +144,14 @@ func (c Client) NetworkExist(name string) bool {
func CreateDefaultDockerNetwork() error {
cli, err := NewClient()
if err != nil {
global.LOG.Errorf("init docker client error %s", err.Error())
global.LOG.Warnf("init docker client error %s", err.Error())
return err
}
defer cli.Close()
if !cli.NetworkExist("1panel-network") {
if err := cli.CreateNetwork("1panel-network"); err != nil {
global.LOG.Errorf("create default docker network error %s", err.Error())
global.LOG.Warnf("create default docker network error %s", err.Error())
return err
}
}
+31 -27
View File
@@ -329,25 +329,30 @@ func (f FileOp) DownloadFile(url, dst string) error {
}
func (f FileOp) Cut(oldPaths []string, dst, name string, cover bool) error {
for _, p := range oldPaths {
var dstPath string
if name != "" {
dstPath = filepath.Join(dst, name)
if f.Stat(dstPath) {
dstPath = dst
}
} else {
base := filepath.Base(p)
dstPath = filepath.Join(dst, base)
if len(oldPaths) == 0 {
return nil
}
var dstPath string
coverFlag := ""
if name != "" {
dstPath = filepath.Join(dst, name)
if f.Stat(dstPath) {
dstPath = dst
}
coverFlag := ""
if cover {
coverFlag = "-f"
}
if err := cmd.RunDefaultBashCf(`mv %s '%s' '%s'`, coverFlag, p, dstPath); err != nil {
return err
}
} else {
dstPath = dst
coverFlag = "-f"
}
var quotedPaths []string
for _, p := range oldPaths {
quotedPaths = append(quotedPaths, fmt.Sprintf("'%s'", p))
}
mvCommand := fmt.Sprintf("mv %s %s '%s'", coverFlag, strings.Join(quotedPaths, " "), dstPath)
if err := cmd.RunDefaultBashCf(mvCommand); err != nil {
return err
}
return nil
}
@@ -810,6 +815,9 @@ func (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules
if len(exclude) == 0 {
continue
}
if strings.HasPrefix(exclude, "/") {
exclude, _ = filepath.Rel(src, exclude)
}
if _, ok := exMap[exclude]; ok {
continue
}
@@ -817,19 +825,14 @@ func (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules
exMap[exclude] = struct{}{}
}
itemPrefix := filepath.Base(src)
if itemPrefix == "/" {
itemPrefix = ""
}
if len(secret) != 0 {
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read %s --exclude-from=<(find %s -type s -printf '%s' | sed 's|^|%s/|') -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", exStr, src, "%P\n", itemPrefix, srcItem, secret, dst)
commands = fmt.Sprintf("tar %s -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", exStr, srcItem, secret, dst)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
} else {
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -printf '%s' | sed 's|^|%s/|') -zcf %s %s %s", src, "%P\n", itemPrefix, dst, exStr, srcItem)
commands = fmt.Sprintf("tar -zcf %s %s %s", dst, exStr, srcItem)
global.LOG.Debug(commands)
}
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(workdir))
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(workdir), cmd.WithIgnoreExist1())
return cmdMgr.RunBashC(commands)
}
@@ -846,13 +849,14 @@ func (f FileOp) TarGzFilesWithCompressPro(list []string, dst, secret string) err
}
commands := ""
if len(secret) != 0 {
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", strings.Join(filelist, " "), secret, dst)
commands = fmt.Sprintf("tar -zcf - %s | openssl enc -aes-256-cbc -salt -k '%s' -out %s", strings.Join(filelist, " "), secret, dst)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
} else {
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s", dst, strings.Join(filelist, " "))
commands = fmt.Sprintf("tar -zcf %s %s", dst, strings.Join(filelist, " "))
global.LOG.Debug(commands)
}
return cmd.RunDefaultBashC(commands)
cmdMgr := cmd.NewCommandMgr(cmd.WithIgnoreExist1())
return cmdMgr.RunBashC(commands)
}
func (f FileOp) TarGzExtractPro(src, dst string, secret string) error {
@@ -870,7 +874,7 @@ func (f FileOp) TarGzExtractPro(src, dst string, secret string) error {
commands = fmt.Sprintf("tar zxvf %s", src)
global.LOG.Debug(commands)
}
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(dst))
cmdMgr := cmd.NewCommandMgr(cmd.WithWorkDir(dst), cmd.WithIgnoreExist1())
return cmdMgr.RunBashC(commands)
}
func CopyCustomAppFile(srcPath, dstPath string) error {
+83
View File
@@ -4,8 +4,16 @@ import (
"bufio"
"bytes"
"fmt"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/charmap"
"golang.org/x/text/encoding/japanese"
"golang.org/x/text/encoding/korean"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/encoding/traditionalchinese"
"golang.org/x/text/encoding/unicode"
"io"
"net/http"
"os"
@@ -103,6 +111,16 @@ func ReadFileByLine(filename string, page, pageSize int, latest bool) (lines []s
}
defer file.Close()
fi, err := file.Stat()
if err != nil {
return
}
if fi.Size() > 500*1024*1024 {
err = buserr.New("ErrLogFileToLarge")
return
}
totalLines, err := countLines(filename)
if err != nil {
return
@@ -190,3 +208,68 @@ func DownloadFileWithProxy(url, dst string) error {
}
return nil
}
func GetDecoderByName(name string) encoding.Encoding {
switch strings.ToLower(name) {
case "gbk":
return simplifiedchinese.GBK
case "gb18030":
return simplifiedchinese.GB18030
case "big5":
return traditionalchinese.Big5
case "euc-jp":
return japanese.EUCJP
case "iso-2022-jp":
return japanese.ISO2022JP
case "shift_jis":
return japanese.ShiftJIS
case "euc-kr":
return korean.EUCKR
case "utf-16be":
return unicode.UTF16(unicode.BigEndian, unicode.ExpectBOM)
case "utf-16le":
return unicode.UTF16(unicode.LittleEndian, unicode.ExpectBOM)
case "windows-1250":
return charmap.Windows1250
case "windows-1251":
return charmap.Windows1251
case "windows-1252":
return charmap.Windows1252
case "windows-1253":
return charmap.Windows1253
case "windows-1254":
return charmap.Windows1254
case "windows-1255":
return charmap.Windows1255
case "windows-1256":
return charmap.Windows1256
case "windows-1257":
return charmap.Windows1257
case "windows-1258":
return charmap.Windows1258
case "iso-8859-1":
return charmap.ISO8859_1
case "iso-8859-2":
return charmap.ISO8859_2
case "iso-8859-3":
return charmap.ISO8859_3
case "iso-8859-4":
return charmap.ISO8859_4
case "iso-8859-5":
return charmap.ISO8859_5
case "iso-8859-6":
return charmap.ISO8859_6
case "iso-8859-7":
return charmap.ISO8859_7
case "iso-8859-8":
return charmap.ISO8859_8
case "iso-8859-9":
return charmap.ISO8859_9
case "iso-8859-13":
return charmap.ISO8859_13
case "iso-8859-15":
return charmap.ISO8859_15
default:
return encoding.Nop
}
}
+9
View File
@@ -33,3 +33,12 @@ type IptablesNatInfo struct {
SrcPort string `json:"srcPort"`
DestPort string `json:"destPort"`
}
type IptablesFilterInfo struct {
Num string `json:"num"`
Target string `json:"target"`
Protocol string `json:"protocol"`
Opt string `json:"opt"`
Source string `json:"source"`
Destination string `json:"destination"`
}
+144 -28
View File
@@ -10,9 +10,22 @@ import (
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
)
const (
PreRoutingChain = "1PANEL_PREROUTING"
PostRoutingChain = "1PANEL_POSTROUTING"
ForwardChain = "1PANEL_FORWARD"
)
const (
FilterTab = "filter"
NatTab = "nat"
)
const NatChain = "1PANEL"
var NatListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`)
var (
natListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`)
)
type Iptables struct {
CmdStr string
@@ -37,8 +50,24 @@ func (iptables *Iptables) run(rule string) error {
return nil
}
func (iptables *Iptables) runf(rule string, a ...any) error {
return iptables.run(fmt.Sprintf(rule, a...))
func (iptables *Iptables) outf(tab, rule string, a ...any) (stdout string, err error) {
stdout, err = cmd.RunDefaultWithStdoutBashCf("%s iptables -t %s %s", iptables.CmdStr, tab, fmt.Sprintf(rule, a...))
if err != nil && stdout != "" {
global.LOG.Errorf("iptables failed, err: %s", stdout)
}
return
}
func (iptables *Iptables) runf(tab, rule string, a ...any) error {
stdout, err := iptables.outf(tab, rule, a...)
if err != nil {
return fmt.Errorf("%s, %s", err, stdout)
}
if stdout != "" {
return fmt.Errorf("iptables error: %s", stdout)
}
return nil
}
func (iptables *Iptables) Check() error {
@@ -47,26 +76,30 @@ func (iptables *Iptables) Check() error {
return fmt.Errorf("%s, %s", err, stdout)
}
if stdout == "0" {
return fmt.Errorf("disable")
return fmt.Errorf("ipv4 forward disable")
}
chain, _ := iptables.outf(NatTab, "-L -n | grep 'Chain %s'", PreRoutingChain)
if len(strings.ReplaceAll(chain, "\n", "")) != 0 {
return fmt.Errorf("chain enabled")
}
return nil
}
func (iptables *Iptables) NatNewChain() error {
return iptables.runf("-N %s", NatChain)
func (iptables *Iptables) NewChain(tab, chain string) error {
return iptables.runf(tab, "-N %s", chain)
}
func (iptables *Iptables) NatAppendChain() error {
return iptables.runf("-A PREROUTING -j %s", NatChain)
func (iptables *Iptables) AppendChain(tab string, chain, chain1 string) error {
return iptables.runf(tab, "-A %s -j %s", chain, chain1)
}
func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
rule := fmt.Sprintf("%s iptables -t nat -nL %s --line", iptables.CmdStr, NatChain)
if len(chain) == 1 {
rule = fmt.Sprintf("%s iptables -t nat -nL %s --line", iptables.CmdStr, chain[0])
if len(chain) == 0 {
chain = append(chain, PreRoutingChain)
}
stdout, err := cmd.RunDefaultWithStdoutBashC(rule)
stdout, err := iptables.outf(NatTab, "-nL %s --line", chain[0])
if err != nil {
return nil, err
}
@@ -76,8 +109,8 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
line = strings.TrimFunc(line, func(r rune) bool {
return r <= 32
})
if NatListRegex.MatchString(line) {
match := NatListRegex.FindStringSubmatch(line)
if natListRegex.MatchString(line) {
match := natListRegex.FindStringSubmatch(line)
if !strings.Contains(match[9], ":") {
match[9] = fmt.Sprintf(":%s", match[9])
}
@@ -97,43 +130,126 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
return forwardList, nil
}
func (iptables *Iptables) NatAdd(protocol, src, destIp, destPort string, save bool) error {
rule := fmt.Sprintf("-A %s -p %s --dport %s -j REDIRECT --to-port %s", NatChain, protocol, src, destPort)
if destIp != "" && destIp != "127.0.0.1" && destIp != "localhost" {
rule = fmt.Sprintf("-A %s -p %s --dport %s -j DNAT --to-destination %s:%s", NatChain, protocol, src, destIp, destPort)
}
if err := iptables.run(rule); err != nil {
return err
func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save bool) error {
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
if err := iptables.runf(NatTab, fmt.Sprintf(
"-A %s -p %s --dport %s -j DNAT --to-destination %s:%s",
PreRoutingChain,
protocol,
srcPort,
dest,
destPort,
)); err != nil {
return err
}
if err := iptables.runf(NatTab, fmt.Sprintf(
"-A %s -d %s -p %s --dport %s -j MASQUERADE",
PostRoutingChain,
dest,
protocol,
destPort,
)); err != nil {
return err
}
if err := iptables.runf(FilterTab, fmt.Sprintf(
"-A %s -d %s -p %s --dport %s -j ACCEPT",
ForwardChain,
dest,
protocol,
destPort,
)); err != nil {
return err
}
if err := iptables.runf(FilterTab, fmt.Sprintf(
"-A %s -s %s -p %s --sport %s -j ACCEPT",
ForwardChain,
dest,
protocol,
destPort,
)); err != nil {
return err
}
} else {
if err := iptables.runf(NatTab, fmt.Sprintf(
"-A %s -p %s --dport %s -j REDIRECT --to-port %s",
PreRoutingChain,
protocol,
srcPort,
destPort,
)); err != nil {
return err
}
}
if save {
return global.DB.Save(&model.Forward{
Protocol: protocol,
Port: src,
TargetIP: destIp,
Port: srcPort,
TargetIP: dest,
TargetPort: destPort,
}).Error
}
return nil
}
func (iptables *Iptables) NatRemove(num string, protocol, src, destIp, destPort string) error {
if err := iptables.runf("-D %s %s", NatChain, num); err != nil {
func (iptables *Iptables) NatRemove(num string, protocol, srcPort, dest, destPort string) error {
if err := iptables.runf(NatTab, "-D %s %s", PreRoutingChain, num); err != nil {
return err
}
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
if err := iptables.runf(NatTab, fmt.Sprintf(
"-D %s -d %s -p %s --dport %s -j MASQUERADE",
PostRoutingChain,
dest,
protocol,
destPort,
)); err != nil {
return err
}
if err := iptables.runf(FilterTab, fmt.Sprintf(
"-D %s -d %s -p %s --dport %s -j ACCEPT",
ForwardChain,
dest,
protocol,
destPort,
)); err != nil {
return err
}
if err := iptables.runf(FilterTab, fmt.Sprintf(
"-D %s -s %s -p %s --sport %s -j ACCEPT",
ForwardChain,
dest,
protocol,
destPort,
)); err != nil {
return err
}
}
global.DB.Where(
"protocol = ? AND port = ? AND target_ip = ? AND target_port = ?",
protocol,
src,
destIp,
srcPort,
dest,
destPort,
).Delete(&model.Forward{})
return nil
}
func (iptables *Iptables) Reload() error {
if err := iptables.runf("-F %s", NatChain); err != nil {
if err := iptables.runf(NatTab, "-F %s", PreRoutingChain); err != nil {
return err
}
if err := iptables.runf(NatTab, "-F %s", PostRoutingChain); err != nil {
return err
}
if err := iptables.runf(FilterTab, "-F %s", ForwardChain); err != nil {
return err
}
+21 -15
View File
@@ -5,7 +5,6 @@ import (
"strings"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
)
@@ -100,16 +99,11 @@ func (f *Ufw) ListPort() ([]FireInfo, error) {
}
func (f *Ufw) ListForward() ([]FireInfo, error) {
_ = f.EnableForward()
iptables, err := NewIptables()
if err != nil {
return nil, err
}
panelChian, _ := cmd.RunDefaultWithStdoutBashCf("%s iptables -t nat -L -n | grep 'Chain 1PANEL'", iptables.CmdStr)
if len(strings.ReplaceAll(panelChian, "\n", "")) == 0 {
if err := f.EnableForward(); err != nil {
global.LOG.Errorf("init port forward failed, err: %v", err)
}
}
rules, err := iptables.NatList()
if err != nil {
return nil, err
@@ -304,21 +298,33 @@ func (f *Ufw) EnableForward() error {
if err != nil {
return err
}
_ = iptables.NatNewChain()
if err = iptables.Check(); err != nil {
return err
}
_ = iptables.NewChain(NatTab, PreRoutingChain)
_ = iptables.NewChain(NatTab, PostRoutingChain)
_ = iptables.NewChain(FilterTab, ForwardChain)
if err = f.enableChain(iptables); err != nil {
return err
}
return iptables.Reload()
}
func (f *Ufw) enableChain(iptables *Iptables) error {
rules, err := iptables.NatList("PREROUTING")
if err != nil {
return err
}
for _, rule := range rules {
if rule.Target == NatChain {
goto reload
if rule.Target == PreRoutingChain {
return nil
}
}
if err = iptables.NatAppendChain(); err != nil {
return err
}
reload:
return iptables.Reload()
_ = iptables.AppendChain(NatTab, "PREROUTING", PreRoutingChain)
_ = iptables.AppendChain(NatTab, "POSTROUTING", PostRoutingChain)
_ = iptables.AppendChain(FilterTab, "FORWARD", ForwardChain)
return nil
}
+1 -1
View File
@@ -33,7 +33,7 @@ func NewPostgresqlClient(conn client.DBInfo) (PostgresqlClient, error) {
}
escapedUsername := url.QueryEscape(conn.Username)
escapedPassword := url.QueryEscape(conn.Password)
connArgs := fmt.Sprintf("postgres://%s:%s@%s:%d/?sslmode=disable", escapedUsername, escapedPassword, conn.Address, conn.Port)
connArgs := fmt.Sprintf("postgres://%s:%s@%s:%d/postgres?sslmode=disable", escapedUsername, escapedPassword, conn.Address, conn.Port)
db, err := sql.Open("pgx", connArgs)
if err != nil {
return nil, err
+148 -116
View File
@@ -4,10 +4,12 @@ import (
"encoding/json"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/providers/dns/alidns"
"github.com/go-acme/lego/v4/providers/dns/baiducloud"
"github.com/go-acme/lego/v4/providers/dns/clouddns"
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
"github.com/go-acme/lego/v4/providers/dns/cloudns"
"github.com/go-acme/lego/v4/providers/dns/dnspod"
"github.com/go-acme/lego/v4/providers/dns/dynu"
"github.com/go-acme/lego/v4/providers/dns/freemyip"
"github.com/go-acme/lego/v4/providers/dns/godaddy"
"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
@@ -15,6 +17,7 @@ import (
"github.com/go-acme/lego/v4/providers/dns/namedotcom"
"github.com/go-acme/lego/v4/providers/dns/namesilo"
"github.com/go-acme/lego/v4/providers/dns/rainyun"
"github.com/go-acme/lego/v4/providers/dns/regru"
"github.com/go-acme/lego/v4/providers/dns/spaceship"
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
"github.com/go-acme/lego/v4/providers/dns/vercel"
@@ -43,6 +46,9 @@ const (
Spaceship DnsType = "Spaceship"
WestCN DnsType = "WestCN"
ClouDNS DnsType = "ClouDNS"
RegRu DnsType = "RegRu"
Dynu DnsType = "Dynu"
BaiduCloud DnsType = "BaiduCloud"
)
type DNSParam struct {
@@ -83,139 +89,165 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
}
switch dnsType {
case DnsPod:
dnsPodConfig := dnspod.NewDefaultConfig()
dnsPodConfig.LoginToken = param.ID + "," + param.Token
dnsPodConfig.PropagationTimeout = propagationTimeout
dnsPodConfig.PollingInterval = pollingInterval
dnsPodConfig.TTL = ttl
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
config := dnspod.NewDefaultConfig()
config.LoginToken = param.ID + "," + param.Token
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = dnspod.NewDNSProviderConfig(config)
case AliYun:
alidnsConfig := alidns.NewDefaultConfig()
alidnsConfig.SecretKey = param.SecretKey
alidnsConfig.APIKey = param.AccessKey
alidnsConfig.PropagationTimeout = propagationTimeout
alidnsConfig.PollingInterval = pollingInterval
alidnsConfig.TTL = ttl
p, err = alidns.NewDNSProviderConfig(alidnsConfig)
config := alidns.NewDefaultConfig()
config.SecretKey = param.SecretKey
config.APIKey = param.AccessKey
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = alidns.NewDNSProviderConfig(config)
case CloudFlare:
cloudflareConfig := cloudflare.NewDefaultConfig()
cloudflareConfig.AuthEmail = param.Email
cloudflareConfig.AuthToken = param.APIkey
cloudflareConfig.PropagationTimeout = propagationTimeout
cloudflareConfig.PollingInterval = pollingInterval
cloudflareConfig.TTL = ttl
p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
config := cloudflare.NewDefaultConfig()
config.AuthEmail = param.Email
config.AuthToken = param.APIkey
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = cloudflare.NewDNSProviderConfig(config)
case CloudDns:
clouddnsConfig := clouddns.NewDefaultConfig()
clouddnsConfig.ClientID = param.ClientID
clouddnsConfig.Email = param.Email
clouddnsConfig.Password = param.Password
clouddnsConfig.PropagationTimeout = propagationTimeout
clouddnsConfig.PollingInterval = pollingInterval
clouddnsConfig.TTL = ttl
p, err = clouddns.NewDNSProviderConfig(clouddnsConfig)
config := clouddns.NewDefaultConfig()
config.ClientID = param.ClientID
config.Email = param.Email
config.Password = param.Password
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = clouddns.NewDNSProviderConfig(config)
case NameCheap:
namecheapConfig := namecheap.NewDefaultConfig()
namecheapConfig.APIKey = param.APIkey
namecheapConfig.APIUser = param.APIUser
namecheapConfig.PropagationTimeout = propagationTimeout
namecheapConfig.PollingInterval = pollingInterval
namecheapConfig.TTL = ttl
p, err = namecheap.NewDNSProviderConfig(namecheapConfig)
config := namecheap.NewDefaultConfig()
config.APIKey = param.APIkey
config.APIUser = param.APIUser
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = namecheap.NewDNSProviderConfig(config)
case NameSilo:
nameSiloConfig := namesilo.NewDefaultConfig()
nameSiloConfig.APIKey = param.APIkey
nameSiloConfig.PropagationTimeout = propagationTimeout
nameSiloConfig.PollingInterval = pollingInterval
nameSiloConfig.TTL = ttl
p, err = namesilo.NewDNSProviderConfig(nameSiloConfig)
config := namesilo.NewDefaultConfig()
config.APIKey = param.APIkey
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = namesilo.NewDNSProviderConfig(config)
case Godaddy:
godaddyConfig := godaddy.NewDefaultConfig()
godaddyConfig.APIKey = param.APIkey
godaddyConfig.APISecret = param.APISecret
godaddyConfig.PropagationTimeout = propagationTimeout
godaddyConfig.PollingInterval = pollingInterval
godaddyConfig.TTL = ttl
p, err = godaddy.NewDNSProviderConfig(godaddyConfig)
config := godaddy.NewDefaultConfig()
config.APIKey = param.APIkey
config.APISecret = param.APISecret
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = godaddy.NewDNSProviderConfig(config)
case NameCom:
nameComConfig := namedotcom.NewDefaultConfig()
nameComConfig.APIToken = param.Token
nameComConfig.Username = param.APIUser
nameComConfig.PropagationTimeout = propagationTimeout
nameComConfig.PollingInterval = pollingInterval
nameComConfig.TTL = ttl
p, err = namedotcom.NewDNSProviderConfig(nameComConfig)
config := namedotcom.NewDefaultConfig()
config.APIToken = param.Token
config.Username = param.APIUser
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = namedotcom.NewDNSProviderConfig(config)
case TencentCloud:
tencentCloudConfig := tencentcloud.NewDefaultConfig()
tencentCloudConfig.SecretID = param.SecretID
tencentCloudConfig.SecretKey = param.SecretKey
tencentCloudConfig.PropagationTimeout = propagationTimeout
tencentCloudConfig.PollingInterval = pollingInterval
tencentCloudConfig.TTL = ttl
p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig)
config := tencentcloud.NewDefaultConfig()
config.SecretID = param.SecretID
config.SecretKey = param.SecretKey
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = tencentcloud.NewDNSProviderConfig(config)
case RainYun:
rainyunConfig := rainyun.NewDefaultConfig()
rainyunConfig.APIKey = param.APIkey
rainyunConfig.PropagationTimeout = propagationTimeout
rainyunConfig.PollingInterval = pollingInterval
rainyunConfig.TTL = ttl
p, err = rainyun.NewDNSProviderConfig(rainyunConfig)
config := rainyun.NewDefaultConfig()
config.APIKey = param.APIkey
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = rainyun.NewDNSProviderConfig(config)
case Volcengine:
volcConfig := volcengine.NewDefaultConfig()
volcConfig.SecretKey = param.SecretKey
volcConfig.AccessKey = param.AccessKey
volcConfig.PropagationTimeout = propagationTimeout
volcConfig.PollingInterval = pollingInterval
volcConfig.TTL = ttl
p, err = volcengine.NewDNSProviderConfig(volcConfig)
config := volcengine.NewDefaultConfig()
config.SecretKey = param.SecretKey
config.AccessKey = param.AccessKey
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = volcengine.NewDNSProviderConfig(config)
case HuaweiCloud:
huaweiCloudConfig := huaweicloud.NewDefaultConfig()
huaweiCloudConfig.AccessKeyID = param.AccessKey
huaweiCloudConfig.SecretAccessKey = param.SecretKey
huaweiCloudConfig.Region = param.Region
huaweiCloudConfig.PropagationTimeout = propagationTimeout
huaweiCloudConfig.PollingInterval = pollingInterval
huaweiCloudConfig.TTL = int32(ttl)
p, err = huaweicloud.NewDNSProviderConfig(huaweiCloudConfig)
config := huaweicloud.NewDefaultConfig()
config.AccessKeyID = param.AccessKey
config.SecretAccessKey = param.SecretKey
config.Region = param.Region
if config.Region == "" {
config.Region = "cn-north-1"
}
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = int32(ttl)
p, err = huaweicloud.NewDNSProviderConfig(config)
case FreeMyIP:
freeMyIpConfig := freemyip.NewDefaultConfig()
freeMyIpConfig.Token = param.Token
freeMyIpConfig.PropagationTimeout = propagationTimeout
freeMyIpConfig.PollingInterval = pollingInterval
p, err = freemyip.NewDNSProviderConfig(freeMyIpConfig)
config := freemyip.NewDefaultConfig()
config.Token = param.Token
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
p, err = freemyip.NewDNSProviderConfig(config)
case Vercel:
vercelConfig := vercel.NewDefaultConfig()
vercelConfig.AuthToken = param.Token
vercelConfig.PropagationTimeout = propagationTimeout
vercelConfig.PollingInterval = pollingInterval
p, err = vercel.NewDNSProviderConfig(vercelConfig)
config := vercel.NewDefaultConfig()
config.AuthToken = param.Token
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
p, err = vercel.NewDNSProviderConfig(config)
case Spaceship:
spaceshipConfig := spaceship.NewDefaultConfig()
spaceshipConfig.APIKey = param.APIkey
spaceshipConfig.APISecret = param.APISecret
spaceshipConfig.PropagationTimeout = propagationTimeout
spaceshipConfig.PollingInterval = pollingInterval
spaceshipConfig.TTL = ttl
p, err = spaceship.NewDNSProviderConfig(spaceshipConfig)
config := spaceship.NewDefaultConfig()
config.APIKey = param.APIkey
config.APISecret = param.APISecret
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = spaceship.NewDNSProviderConfig(config)
case WestCN:
westcnConfig := westcn.NewDefaultConfig()
westcnConfig.Username = param.Username
westcnConfig.Password = param.Password
westcnConfig.PropagationTimeout = propagationTimeout
westcnConfig.PollingInterval = pollingInterval
westcnConfig.TTL = ttl
p, err = westcn.NewDNSProviderConfig(westcnConfig)
config := westcn.NewDefaultConfig()
config.Username = param.Username
config.Password = param.Password
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = westcn.NewDNSProviderConfig(config)
case ClouDNS:
cloudnsConfig := cloudns.NewDefaultConfig()
cloudnsConfig.AuthID = param.AuthID
cloudnsConfig.SubAuthID = param.SubAuthID
cloudnsConfig.AuthPassword = param.AuthPassword
cloudnsConfig.PropagationTimeout = propagationTimeout
cloudnsConfig.PollingInterval = pollingInterval
cloudnsConfig.TTL = ttl
p, err = cloudns.NewDNSProviderConfig(cloudnsConfig)
config := cloudns.NewDefaultConfig()
config.AuthID = param.AuthID
config.SubAuthID = param.SubAuthID
config.AuthPassword = param.AuthPassword
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = cloudns.NewDNSProviderConfig(config)
case RegRu:
config := regru.NewDefaultConfig()
config.Username = param.Username
config.Password = param.Password
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = regru.NewDNSProviderConfig(config)
case Dynu:
config := dynu.NewDefaultConfig()
config.APIKey = param.APIkey
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = dynu.NewDNSProviderConfig(config)
case BaiduCloud:
config := baiducloud.NewDefaultConfig()
config.AccessKeyID = param.AccessKey
config.SecretAccessKey = param.SecretKey
config.PropagationTimeout = propagationTimeout
config.PollingInterval = pollingInterval
config.TTL = ttl
p, err = baiducloud.NewDNSProviderConfig(config)
}
if err != nil {
return nil, err
}
+64 -12
View File
@@ -1,6 +1,7 @@
package systemctl
import (
"bytes"
"fmt"
"github.com/pkg/errors"
"os/exec"
@@ -16,31 +17,65 @@ func RunSystemCtl(args ...string) (string, error) {
return string(output), nil
}
func isSnapService(serviceName string) bool {
cmd := exec.Command("snap", "services")
var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
return false
}
return strings.Contains(out.String(), serviceName)
}
func isSnapServiceActive(serviceName string) bool {
cmd := exec.Command("snap", "services")
var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
return false
}
lines := strings.Split(out.String(), "\n")
for _, line := range lines {
if strings.Contains(line, serviceName) && strings.Contains(line, "active") {
return true
}
}
return false
}
func IsActive(serviceName string) (bool, error) {
out, err := RunSystemCtl("is-active", serviceName)
if err != nil {
return false, err
if err == nil {
return strings.TrimSpace(out) == "active", nil
}
return out == "active\n", nil
if isSnapServiceActive(serviceName) {
return true, nil
}
return false, fmt.Errorf("service %s is not active: %v", serviceName, err)
}
func IsEnable(serviceName string) (bool, error) {
out, err := RunSystemCtl("is-enabled", serviceName)
if err != nil {
return false, err
if err == nil {
return strings.TrimSpace(out) == "enabled", nil
}
return out == "enabled\n", nil
if isSnapServiceActive(serviceName) {
return true, nil
}
return false, fmt.Errorf("failed to determine if service %s is enabled: %v", serviceName, err)
}
func IsExist(serviceName string) (bool, error) {
out, err := RunSystemCtl("is-enabled", serviceName)
if err != nil {
if strings.Contains(out, "disabled") {
return true, nil
}
return false, nil
if err == nil || strings.Contains(out, "disabled") {
return true, nil
}
return true, nil
if isSnapService(serviceName) {
return true, nil
}
return false, nil
}
func handlerErr(out string, err error) error {
@@ -55,10 +90,27 @@ func handlerErr(out string, err error) error {
func Restart(serviceName string) error {
out, err := RunSystemCtl("restart", serviceName)
if err == nil {
return nil
}
if isSnapService(serviceName) {
cmd := exec.Command("snap", "restart", serviceName)
output, snapErr := cmd.CombinedOutput()
return handlerErr(string(output), snapErr)
}
return handlerErr(out, err)
}
func Operate(operate, serviceName string) error {
out, err := RunSystemCtl(operate, serviceName)
if err == nil {
return nil
}
if isSnapService(serviceName) && (operate == "start" || operate == "stop" || operate == "restart") {
cmd := exec.Command("snap", operate, serviceName)
output, snapErr := cmd.CombinedOutput()
return handlerErr(string(output), snapErr)
}
return handlerErr(out, err)
}
+10 -1
View File
@@ -6,6 +6,7 @@ import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/utils/captcha"
"github.com/gin-gonic/gin"
)
@@ -133,7 +134,15 @@ func (b *BaseApi) GetLoginSetting(c *gin.Context) {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, settingInfo)
res := &dto.LoginSetting{
IsDemo: global.CONF.Base.IsDemo,
IsIntl: global.CONF.Base.IsIntl,
Language: settingInfo.Language,
MenuTabs: settingInfo.MenuTabs,
PanelName: settingInfo.PanelName,
Theme: settingInfo.Theme,
}
helper.SuccessWithData(c, res)
}
func saveLoginLogs(c *gin.Context, err error) {
+14 -8
View File
@@ -3,7 +3,6 @@ package v2
import (
"encoding/json"
"fmt"
"path"
"strconv"
"strings"
"time"
@@ -185,26 +184,33 @@ func (b *BaseApi) RunScript(c *gin.Context) {
tty.Start(quitChan)
go slave.Wait(quitChan)
} else {
connInfo, installDir, err := xpack.LoadNodeInfo(currentNode)
connInfo, _, err := xpack.LoadNodeInfo(currentNode)
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
return
}
fileDir := path.Join(installDir, "1panel/tmp/script")
fileName := ""
var translations = make(map[string]string)
_ = json.Unmarshal([]byte(scriptItem.Name), &translations)
if name, ok := translations["en"]; ok {
fileName = path.Join(fileDir, strings.ReplaceAll(name, " ", "_"))
fileName = strings.ReplaceAll(name, " ", "_")
} else {
fileName = path.Join(fileDir, strings.ReplaceAll(scriptItem.Name, " ", "_"))
fileName = strings.ReplaceAll(scriptItem.Name, " ", "_")
}
initCmd := fmt.Sprintf("mkdir -p %s && cat > %s <<'MYMARKER'\n%s\nMYMARKER\n bash %s", fileDir, fileName, scriptItem.Script, fileName)
client, err := ssh.NewClient(*connInfo)
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
if wshandleError(wsConn, errors.WithMessage(err, "set up the connection failed. Please check the host information")) {
return
}
defer client.Close()
sudoItem := client.SudoHandleCmd()
defer func() {
_, _ = client.Runf("%s rm -rf %s", sudoItem, fileName)
client.Close()
}()
std, err := client.Runf("%s touch %s && %s chmod 777 %s && %s cat > %s <<'MYMARKER'\n%s\nMYMARKER\n", sudoItem, fileName, sudoItem, fileName, sudoItem, fileName, scriptItem.Script)
if wshandleError(wsConn, errors.WithMessage(err, fmt.Sprintf("touch script file failed, err: %s. Please check and retry", std))) {
return
}
initCmd := fmt.Sprintf("%s bash %s", sudoItem, fileName)
sws, err := terminal.NewLogicSshWsSession(cols, rows, client.Client, wsConn, initCmd)
if wshandleError(wsConn, err) {
+9
View File
@@ -219,3 +219,12 @@ type AppstoreConfig struct {
UpgradeBackup string `json:"upgradeBackup"`
UninstallDeleteBackup string `json:"uninstallDeleteBackup"`
}
type LoginSetting struct {
IsDemo bool `json:"isDemo"`
IsIntl bool `json:"isIntl"`
Language string `json:"language"`
MenuTabs string `json:"menuTabs"`
PanelName string `json:"panelName"`
Theme string `json:"theme"`
}
+7
View File
@@ -16,6 +16,7 @@ type IUpgradeLogRepo interface {
Delete(opts ...global.DBOption) error
WithByNodeID(nodeID uint) global.DBOption
WithByUpgradeVersion(oldVersion, newVersion string) global.DBOption
}
func NewIUpgradeLogRepo() IUpgradeLogRepo {
@@ -79,3 +80,9 @@ func (c *UpgradeLogRepo) WithByNodeID(nodeID uint) global.DBOption {
return g.Where("node_id = ?", nodeID)
}
}
func (c *UpgradeLogRepo) WithByUpgradeVersion(oldVersion, newVersion string) global.DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("old_version = ? AND new_version = ?", oldVersion, newVersion)
}
}
+2 -2
View File
@@ -37,7 +37,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
return nil, "", buserr.New("ErrRecordNotFound")
}
if nameSetting.Value != info.Name {
return nil, "ErrAuth", nil
return nil, "ErrAuth", buserr.New("ErrAuth")
}
if err = checkPassword(info.Password); err != nil {
return nil, "ErrAuth", err
@@ -47,7 +47,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
return nil, "", err
}
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
return nil, "ErrEntrance", nil
return nil, "ErrEntrance", buserr.New("ErrEntrance")
}
mfa, err := settingRepo.Get(repo.WithByKey("MFAStatus"))
if err != nil {
+9 -3
View File
@@ -268,8 +268,6 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) 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)
}
@@ -284,7 +282,15 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) error {
varsItem, _ := json.Marshal(varMap)
backup.Vars = string(varsItem)
return backupRepo.Save(&backup)
if err := backupRepo.Save(&backup); err != nil {
return err
}
go func() {
if err := xpack.Sync(constant.SyncBackupAccounts); err != nil {
global.LOG.Errorf("sync backup account to node failed, err: %v", err)
}
}()
return nil
}
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
-1
View File
@@ -11,7 +11,6 @@ var (
groupRepo = repo.NewIGroupRepo()
upgradeLogRepo = repo.NewIUpgradeLogRepo()
taskRepo = repo.NewITaskRepo()
agentRepo = repo.NewIAgentRepo()
scriptRepo = repo.NewIScriptRepo()
)
+11 -8
View File
@@ -188,34 +188,37 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
}
var scriptSetting model.Setting
_ = global.DB.Where("key = ?", "ScriptVersion").First(&scriptSetting).Error
if scriptSetting.Value == string(versionRes) {
syncTask.Logf("The local-%s and remote-%s versions are detected to be consistent. Skip...", scriptSetting.Value, versionRes)
localVersion := strings.ReplaceAll(string(versionRes), "\n", "")
remoteVersion := strings.ReplaceAll(scriptSetting.Value, "\n", "")
if localVersion == remoteVersion {
syncTask.Log(i18n.GetMsgByKey("ScriptSyncSkip"))
return nil
}
syncTask.Log("start to download data.yaml")
dataUrl := fmt.Sprintf("%s/scripts/data.yaml", global.CONF.RemoteURL.ResourceURL)
_, dataRes, err := req_helper.HandleRequestWithProxy(dataUrl, http.MethodGet, constant.TimeOut20s)
syncTask.LogWithStatus(i18n.GetMsgByKey("DownloadData"), err)
if err != nil {
return fmt.Errorf("load scripts data.yaml from remote failed, err: %v", err)
}
syncTask.Log("download successful!")
var scripts Scripts
if err = yaml.Unmarshal(dataRes, &scripts); err != nil {
return fmt.Errorf("the format of data.yaml is err: %v", err)
}
syncTask.Log("start to download scripts.tar.gz")
tmpDir := path.Join(global.CONF.Base.InstallDir, "1panel/tmp/script")
if _, err := os.Stat(tmpDir); err != nil {
_ = os.MkdirAll(tmpDir, 0755)
}
scriptsUrl := fmt.Sprintf("%s/scripts/scripts.tar.gz", global.CONF.RemoteURL.ResourceURL)
if err := files.DownloadFileWithProxy(scriptsUrl, tmpDir+"/scripts.tar.gz"); err != nil {
err = files.DownloadFileWithProxy(scriptsUrl, tmpDir+"/scripts.tar.gz")
syncTask.LogWithStatus(i18n.GetMsgByKey("DownloadPackage"), err)
if err != nil {
return fmt.Errorf("download scripts.tar.gz failed, err: %v", err)
}
syncTask.Log("download successful! now start to decompress...")
if err := files.HandleUnTar(tmpDir+"/scripts.tar.gz", tmpDir, ""); err != nil {
return fmt.Errorf("handle decompress scripts.tar.gz failed, err: %v", err)
}
@@ -234,7 +237,7 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
scriptsForDB = append(scriptsForDB, scriptItem)
}
syncTask.Log("analytic completion! now start to refresh database...")
syncTask.Log(i18n.GetMsgByKey("AnalyticCompletion"))
if err := scriptRepo.SyncAll(scriptsForDB); err != nil {
return fmt.Errorf("sync script with db failed, err: %v", err)
}

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