From a627aa9915ebe07c236342e09e79a5fdab743200 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:59:21 +0800 Subject: [PATCH] feat: merge from dev (#7366) * feat: merge from dev * feat: Merge mobile style code from the dev branch --- agent/app/api/v2/app.go | 2 +- agent/app/api/v2/cronjob.go | 2 +- agent/app/api/v2/runtime.go | 2 +- agent/app/api/v2/snapshot.go | 4 +- agent/app/dto/clam.go | 2 +- agent/app/dto/command.go | 2 +- agent/app/dto/container.go | 29 +- agent/app/dto/cronjob.go | 2 +- agent/app/dto/database.go | 4 +- agent/app/dto/database_postgresql.go | 2 +- agent/app/dto/logs.go | 46 --- agent/app/dto/request/website.go | 2 +- agent/app/dto/snapshot.go | 7 + agent/app/model/compose_template.go | 1 + agent/app/repo/common.go | 6 + agent/app/repo/compose_template.go | 4 + agent/app/service/clam.go | 31 +- agent/app/service/container.go | 99 ++++-- agent/app/service/container_compose.go | 123 ++++++- agent/app/service/container_volume.go | 2 +- agent/app/service/cronjob_backup.go | 10 + agent/app/service/cronjob_helper.go | 48 +-- agent/app/service/dashboard.go | 8 +- agent/app/service/database_mysql.go | 4 + agent/app/service/docker.go | 106 +++++- agent/app/service/firewall.go | 8 - agent/app/service/image_repo.go | 62 ++-- agent/app/service/monitor.go | 2 +- agent/app/service/snapshot.go | 6 +- agent/app/service/snapshot_recover.go | 11 +- agent/app/service/ssh.go | 12 +- agent/constant/errs.go | 3 +- agent/cron/cron.go | 4 +- agent/cron/job/ssl.go | 2 +- agent/cron/job/website.go | 2 +- agent/i18n/lang/en.yaml | 3 +- agent/i18n/lang/zh-Hant.yaml | 3 +- agent/i18n/lang/zh.yaml | 3 +- agent/utils/cloud_storage/client/s3.go | 13 +- agent/utils/cmd/cmd.go | 2 +- agent/utils/common/common.go | 7 - agent/utils/files/file_op.go | 1 + agent/utils/firewall/client.go | 16 +- agent/utils/firewall/client/firewalld.go | 4 + agent/utils/firewall/client/ufw.go | 7 + agent/utils/mysql/helper/dump.go | 303 ------------------ agent/utils/mysql/helper/source.go | 244 -------------- agent/utils/postgresql/client/remote.go | 7 +- backend/router/entry_xpack.go | 1 + backend/server/init_xpack.go | 1 + core/app/dto/command.go | 2 +- core/app/dto/logs.go | 2 +- core/app/repo/common.go | 6 + core/app/service/setting.go | 2 +- core/init/cron/cron.go | 2 +- core/init/session/psession/psession.go | 9 +- core/middleware/operation.go | 18 +- core/middleware/password_expired.go | 2 +- core/utils/cloud_storage/client/s3.go | 6 +- core/utils/common/common.go | 21 +- frontend/package.json | 2 +- frontend/src/App.vue | 2 + frontend/src/api/interface/backup.ts | 1 + frontend/src/api/interface/container.ts | 2 + frontend/src/components/app-status/index.vue | 214 ++++++++----- frontend/src/components/compose-log/index.vue | 10 +- frontend/src/components/drawer-pro/index.vue | 10 +- frontend/src/components/group/index.vue | 2 +- .../src/components/layout-content/index.vue | 55 ++-- frontend/src/components/log-dialog/index.vue | 18 +- .../src/components/router-button/index.vue | 34 +- .../src/components/system-upgrade/index.vue | 95 +++--- frontend/src/components/upload/index.vue | 34 +- frontend/src/global/form-rules.ts | 2 +- frontend/src/lang/modules/en.ts | 40 ++- frontend/src/lang/modules/tw.ts | 38 ++- frontend/src/lang/modules/zh.ts | 35 +- frontend/src/layout/components/AppFooter.vue | 22 +- .../src/layout/components/Sidebar/index.vue | 7 + frontend/src/layout/index.vue | 1 + frontend/src/routers/modules/setting.ts | 10 +- frontend/src/styles/common.scss | 12 +- frontend/src/utils/util.ts | 13 + .../views/container/compose/create/index.vue | 13 + .../views/container/compose/delete/index.vue | 17 +- .../views/container/compose/detail/index.vue | 32 +- .../views/container/compose/edit/index.vue | 54 +++- .../src/views/container/compose/index.vue | 59 ++-- .../src/views/container/container/index.vue | 14 +- .../views/container/container/log/index.vue | 13 +- .../container/container/operate/confirm.vue | 55 ++++ .../container/container/operate/index.vue | 173 +++++----- .../src/views/container/network/index.vue | 17 +- .../src/views/container/setting/index.vue | 13 +- .../views/container/template/detail/index.vue | 6 +- .../src/views/container/template/index.vue | 5 +- frontend/src/views/cronjob/index.vue | 6 +- frontend/src/views/cronjob/operate/index.vue | 1 + .../src/views/database/mysql/conn/index.vue | 32 +- frontend/src/views/database/mysql/index.vue | 16 +- .../src/views/database/mysql/remote/index.vue | 2 +- .../views/database/postgresql/conn/index.vue | 36 ++- .../src/views/database/postgresql/index.vue | 8 +- .../database/postgresql/remote/index.vue | 4 +- .../postgresql/remote/operate/index.vue | 1 + .../src/views/database/redis/conn/index.vue | 32 +- frontend/src/views/database/redis/index.vue | 6 +- .../src/views/database/redis/remote/index.vue | 2 +- .../redis/setting/persistence/index.vue | 7 +- frontend/src/views/home/index.vue | 10 +- frontend/src/views/home/status/index.vue | 95 +++--- .../file-management/code-editor/index.vue | 2 +- .../src/views/host/file-management/index.vue | 8 +- .../host/firewall/forward/operate/index.vue | 12 +- .../views/host/firewall/ip/operate/index.vue | 12 +- .../host/firewall/port/operate/index.vue | 12 +- frontend/src/views/setting/about/index.vue | 6 +- .../setting/backup-account/operate/index.vue | 86 ++--- frontend/src/views/setting/panel/index.vue | 18 +- frontend/src/views/setting/safe/index.vue | 37 ++- frontend/src/views/setting/snapshot/index.vue | 11 +- frontend/src/views/toolbox/clam/index.vue | 30 +- .../src/views/toolbox/clam/status/index.vue | 29 +- frontend/src/views/toolbox/device/index.vue | 15 +- .../views/toolbox/device/time-zone/index.vue | 2 + frontend/src/views/toolbox/fail2ban/index.vue | 18 +- frontend/src/views/toolbox/ftp/index.vue | 32 +- .../views/toolbox/supervisor/file/index.vue | 6 +- .../views/website/runtime/dotnet/index.vue | 16 +- .../src/views/website/runtime/go/index.vue | 4 +- .../src/views/website/runtime/java/index.vue | 4 +- .../src/views/website/runtime/node/index.vue | 4 +- .../views/website/runtime/python/index.vue | 4 +- frontend/src/views/website/website/index.vue | 2 +- 134 files changed, 1624 insertions(+), 1479 deletions(-) delete mode 100644 agent/utils/mysql/helper/dump.go delete mode 100644 agent/utils/mysql/helper/source.go create mode 120000 backend/router/entry_xpack.go create mode 120000 backend/server/init_xpack.go create mode 100644 frontend/src/views/container/container/operate/confirm.vue diff --git a/agent/app/api/v2/app.go b/agent/app/api/v2/app.go index d1d16a357..83d1a7a6a 100644 --- a/agent/app/api/v2/app.go +++ b/agent/app/api/v2/app.go @@ -171,7 +171,7 @@ func (b *BaseApi) GetIgnoredApp(c *gin.Context) { // @Success 200 {object} model.AppInstall // @Security ApiKeyAuth // @Router /apps/install [post] -// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[{"input_column":"name","input_value":"name","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"} +// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"安装应用 [name]","formatEN":"Install app [name]"} func (b *BaseApi) InstallApp(c *gin.Context) { var req request.AppInstallCreate if err := helper.CheckBindAndValidate(&req, c); err != nil { diff --git a/agent/app/api/v2/cronjob.go b/agent/app/api/v2/cronjob.go index 2424a38e2..1578da5d3 100644 --- a/agent/app/api/v2/cronjob.go +++ b/agent/app/api/v2/cronjob.go @@ -93,7 +93,7 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) { return } - loc, _ := time.LoadLocation(common.LoadTimeZone()) + loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) req.StartTime = req.StartTime.In(loc) req.EndTime = req.EndTime.In(loc) diff --git a/agent/app/api/v2/runtime.go b/agent/app/api/v2/runtime.go index 09d9993f5..e7912db40 100644 --- a/agent/app/api/v2/runtime.go +++ b/agent/app/api/v2/runtime.go @@ -61,7 +61,7 @@ func (b *BaseApi) CreateRuntime(c *gin.Context) { // @Success 200 // @Security ApiKeyAuth // @Router /runtimes/del [post] -// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除网站 [name]","formatEN":"Delete website [name]"} +// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除运行环境 [name]","formatEN":"Delete runtime [name]"} func (b *BaseApi) DeleteRuntime(c *gin.Context) { var req request.RuntimeDelete if err := helper.CheckBindAndValidate(&req, c); err != nil { diff --git a/agent/app/api/v2/snapshot.go b/agent/app/api/v2/snapshot.go index de9e809b0..9eeb28753 100644 --- a/agent/app/api/v2/snapshot.go +++ b/agent/app/api/v2/snapshot.go @@ -113,12 +113,12 @@ func (b *BaseApi) UpdateSnapDescription(c *gin.Context) { // @Summary Page system snapshot // @Description 获取系统快照列表分页 // @Accept json -// @Param request body dto.SearchWithPage true "request" +// @Param request body dto.PageSnapshot true "request" // @Success 200 {object} dto.PageResult // @Security ApiKeyAuth // @Router /settings/snapshot/search [post] func (b *BaseApi) SearchSnapshot(c *gin.Context) { - var req dto.SearchWithPage + var req dto.PageSnapshot if err := helper.CheckBindAndValidate(&req, c); err != nil { return } diff --git a/agent/app/dto/clam.go b/agent/app/dto/clam.go index 964db15ee..317d84637 100644 --- a/agent/app/dto/clam.go +++ b/agent/app/dto/clam.go @@ -7,7 +7,7 @@ import ( type SearchClamWithPage struct { PageInfo Info string `json:"info"` - OrderBy string `json:"orderBy" validate:"required,oneof=name status created_at"` + OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"` Order string `json:"order" validate:"required,oneof=null ascending descending"` } diff --git a/agent/app/dto/command.go b/agent/app/dto/command.go index b085ce66f..2cc14d98d 100644 --- a/agent/app/dto/command.go +++ b/agent/app/dto/command.go @@ -2,7 +2,7 @@ package dto type SearchCommandWithPage struct { PageInfo - OrderBy string `json:"orderBy" validate:"required,oneof=name command created_at"` + OrderBy string `json:"orderBy" validate:"required,oneof=name command createdAt"` Order string `json:"order" validate:"required,oneof=null ascending descending"` GroupID uint `json:"groupID"` Info string `json:"info"` diff --git a/agent/app/dto/container.go b/agent/app/dto/container.go index f2851f058..01f0871bf 100644 --- a/agent/app/dto/container.go +++ b/agent/app/dto/container.go @@ -8,7 +8,7 @@ type PageContainer struct { PageInfo Name string `json:"name"` State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"` - OrderBy string `json:"orderBy" validate:"required,oneof=name created_at"` + OrderBy string `json:"orderBy" validate:"required,oneof=name createdAt"` Order string `json:"order" validate:"required,oneof=null ascending descending"` Filters string `json:"filters"` ExcludeAppStore bool `json:"excludeAppStore"` @@ -148,7 +148,7 @@ type PortHelper struct { type ContainerOperation struct { Names []string `json:"names" validate:"required"` - Operation string `json:"operation" validate:"required,oneof=start stop restart kill pause unpause remove"` + Operation string `json:"operation" validate:"required,oneof=up start stop restart kill pause unpause remove"` } type ContainerRename struct { @@ -232,6 +232,7 @@ type ComposeInfo struct { Workdir string `json:"workdir"` Path string `json:"path"` Containers []ComposeContainer `json:"containers"` + Env []string `json:"env"` } type ComposeContainer struct { ContainerID string `json:"containerID"` @@ -240,23 +241,25 @@ type ComposeContainer struct { State string `json:"state"` } type ComposeCreate struct { - TaskID string `json:"taskID"` - Name string `json:"name"` - From string `json:"from" validate:"required,oneof=edit path template"` - File string `json:"file"` - Path string `json:"path"` - Template uint `json:"template"` + TaskID string `json:"taskID"` + Name string `json:"name"` + From string `json:"from" validate:"required,oneof=edit path template"` + File string `json:"file"` + Path string `json:"path"` + Template uint `json:"template"` + Env []string `json:"env"` } type ComposeOperation struct { Name string `json:"name" validate:"required"` - Path string `json:"path" validate:"required"` - Operation string `json:"operation" validate:"required,oneof=start stop down"` + Path string `json:"path"` + Operation string `json:"operation" validate:"required,oneof=up start stop down delete"` WithFile bool `json:"withFile"` } type ComposeUpdate struct { - Name string `json:"name" validate:"required"` - Path string `json:"path" validate:"required"` - Content string `json:"content" validate:"required"` + Name string `json:"name" validate:"required"` + Path string `json:"path" validate:"required"` + Content string `json:"content" validate:"required"` + Env []string `json:"env"` } type ContainerLog struct { diff --git a/agent/app/dto/cronjob.go b/agent/app/dto/cronjob.go index 9bfe49c90..7787bf8fe 100644 --- a/agent/app/dto/cronjob.go +++ b/agent/app/dto/cronjob.go @@ -7,7 +7,7 @@ import ( type PageCronjob struct { PageInfo Info string `json:"info"` - OrderBy string `json:"orderBy" validate:"required,oneof=name status created_at"` + OrderBy string `json:"orderBy" validate:"required,oneof=name status createdAt"` Order string `json:"order" validate:"required,oneof=null ascending descending"` } diff --git a/agent/app/dto/database.go b/agent/app/dto/database.go index acfb3b09e..e24b421c9 100644 --- a/agent/app/dto/database.go +++ b/agent/app/dto/database.go @@ -27,7 +27,7 @@ type MysqlDBSearch struct { PageInfo Info string `json:"info"` Database string `json:"database" validate:"required"` - OrderBy string `json:"orderBy" validate:"required,oneof=name created_at"` + OrderBy string `json:"orderBy" validate:"required,oneof=name createdAt"` Order string `json:"order" validate:"required,oneof=null ascending descending"` } @@ -236,7 +236,7 @@ type DatabaseSearch struct { PageInfo Info string `json:"info"` Type string `json:"type"` - OrderBy string `json:"orderBy" validate:"required,oneof=name created_at"` + OrderBy string `json:"orderBy" validate:"required,oneof=name createdAt"` Order string `json:"order" validate:"required,oneof=null ascending descending"` } diff --git a/agent/app/dto/database_postgresql.go b/agent/app/dto/database_postgresql.go index 825ff3af5..e468448f9 100644 --- a/agent/app/dto/database_postgresql.go +++ b/agent/app/dto/database_postgresql.go @@ -6,7 +6,7 @@ type PostgresqlDBSearch struct { PageInfo Info string `json:"info"` Database string `json:"database" validate:"required"` - OrderBy string `json:"orderBy" validate:"required,oneof=name created_at"` + OrderBy string `json:"orderBy" validate:"required,oneof=name createdAt"` Order string `json:"order" validate:"required,oneof=null ascending descending"` } diff --git a/agent/app/dto/logs.go b/agent/app/dto/logs.go index 21c569f3b..315985521 100644 --- a/agent/app/dto/logs.go +++ b/agent/app/dto/logs.go @@ -2,54 +2,8 @@ package dto import ( "github.com/1Panel-dev/1Panel/agent/app/model" - "time" ) -type OperationLog struct { - ID uint `json:"id"` - Source string `json:"source"` - - IP string `json:"ip"` - Path string `json:"path"` - Method string `json:"method"` - UserAgent string `json:"userAgent"` - - Latency time.Duration `json:"latency"` - Status string `json:"status"` - Message string `json:"message"` - - DetailZH string `json:"detailZH"` - DetailEN string `json:"detailEN"` - CreatedAt time.Time `json:"createdAt"` -} - -type SearchOpLogWithPage struct { - PageInfo - Source string `json:"source"` - Status string `json:"status"` - Operation string `json:"operation"` -} - -type SearchLgLogWithPage struct { - PageInfo - IP string `json:"ip"` - Status string `json:"status"` -} - -type LoginLog struct { - ID uint `json:"id"` - IP string `json:"ip"` - Address string `json:"address"` - Agent string `json:"agent"` - Status string `json:"status"` - Message string `json:"message"` - CreatedAt time.Time `json:"createdAt"` -} - -type CleanLog struct { - LogType string `json:"logType" validate:"required,oneof=login operation"` -} - type SearchTaskLogReq struct { Status string `json:"status"` Type string `json:"type"` diff --git a/agent/app/dto/request/website.go b/agent/app/dto/request/website.go index 526c7c6e4..fd89d8ca7 100644 --- a/agent/app/dto/request/website.go +++ b/agent/app/dto/request/website.go @@ -7,7 +7,7 @@ import ( type WebsiteSearch struct { dto.PageInfo Name string `json:"name"` - OrderBy string `json:"orderBy" validate:"required,oneof=primary_domain type status created_at expire_date"` + OrderBy string `json:"orderBy" validate:"required,oneof=primary_domain type status createdAt expire_date"` Order string `json:"order" validate:"required,oneof=null ascending descending"` WebsiteGroupID uint `json:"websiteGroupId"` } diff --git a/agent/app/dto/snapshot.go b/agent/app/dto/snapshot.go index d4b928d95..9fe53e70d 100644 --- a/agent/app/dto/snapshot.go +++ b/agent/app/dto/snapshot.go @@ -13,6 +13,13 @@ type SnapshotStatus struct { Upload string `json:"upload"` } +type PageSnapshot struct { + PageInfo + Info string `json:"info"` + OrderBy string `json:"orderBy" validate:"required,oneof=name createdAt"` + Order string `json:"order" validate:"required,oneof=null ascending descending"` +} + type SnapshotCreate struct { ID uint `json:"id"` Name string `json:"name"` diff --git a/agent/app/model/compose_template.go b/agent/app/model/compose_template.go index 89f153b63..aa127f2ac 100644 --- a/agent/app/model/compose_template.go +++ b/agent/app/model/compose_template.go @@ -12,4 +12,5 @@ type Compose struct { BaseModel Name string `json:"name"` + Path string `json:"path"` } diff --git a/agent/app/repo/common.go b/agent/app/repo/common.go index 6d13f0bc9..1dc9444a6 100644 --- a/agent/app/repo/common.go +++ b/agent/app/repo/common.go @@ -113,11 +113,17 @@ func (c *CommonRepo) WithByCreatedAt(startTime, endTime time.Time) DBOption { } func (c *CommonRepo) WithOrderBy(orderStr string) DBOption { + if orderStr == "createdAt" { + orderStr = "created_at" + } return func(g *gorm.DB) *gorm.DB { return g.Order(orderStr) } } func (c *CommonRepo) WithOrderRuleBy(orderBy, order string) DBOption { + if orderBy == "createdAt" { + orderBy = "created_at" + } switch order { case constant.OrderDesc: order = "desc" diff --git a/agent/app/repo/compose_template.go b/agent/app/repo/compose_template.go index 8233e162f..186466810 100644 --- a/agent/app/repo/compose_template.go +++ b/agent/app/repo/compose_template.go @@ -19,6 +19,7 @@ type IComposeTemplateRepo interface { CreateRecord(compose *model.Compose) error DeleteRecord(opts ...DBOption) error ListRecord() ([]model.Compose, error) + UpdateRecord(name string, vars map[string]interface{}) error } func NewIComposeTemplateRepo() IComposeTemplateRepo { @@ -102,3 +103,6 @@ func (u *ComposeTemplateRepo) DeleteRecord(opts ...DBOption) error { } return db.Delete(&model.Compose{}).Error } +func (u *ComposeTemplateRepo) UpdateRecord(name string, vars map[string]interface{}) error { + return global.DB.Model(&model.Compose{}).Where("name = ?", name).Updates(vars).Error +} diff --git a/agent/app/service/clam.go b/agent/app/service/clam.go index 8bdead646..ae915ee37 100644 --- a/agent/app/service/clam.go +++ b/agent/app/service/clam.go @@ -92,6 +92,8 @@ func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) { baseInfo.Version = strings.TrimPrefix(version, "ClamAV ") } } + } else { + _ = StopAllCronJob(false) } if baseInfo.FreshIsActive { version, err := cmd.Exec("freshclam --version") @@ -139,7 +141,7 @@ func (c *ClamService) SearchWithPage(req dto.SearchClamWithPage) (int64, interfa item.LastHandleDate = "-" datas = append(datas, item) } - nyc, _ := time.LoadLocation(common.LoadTimeZone()) + nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) for i := 0; i < len(datas); i++ { logPaths := loadFileByName(datas[i].Name) sort.Slice(logPaths, func(i, j int) bool { @@ -268,7 +270,7 @@ func (c *ClamService) Delete(req dto.ClamDelete) error { } func (c *ClamService) HandleOnce(req dto.OperateByID) error { - if !cmd.Which("clamdscan") { + if cleaned := StopAllCronJob(true); cleaned { return buserr.New("ErrClamdscanNotFound") } clam, _ := clamRepo.Get(commonRepo.WithByID(req.ID)) @@ -321,7 +323,7 @@ func (c *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, er } var filterFiles []string - nyc, _ := time.LoadLocation(common.LoadTimeZone()) + nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) for _, item := range logPaths { t1, err := time.ParseInLocation(constant.DateTimeSlimLayout, item, nyc) if err != nil { @@ -473,6 +475,29 @@ func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error { return nil } +func StopAllCronJob(withCheck bool) bool { + if withCheck { + isActive := false + exist1, _ := systemctl.IsExist(clamServiceNameCentOs) + if exist1 { + isActive, _ = systemctl.IsActive(clamServiceNameCentOs) + } + exist2, _ := systemctl.IsExist(clamServiceNameUbuntu) + if exist2 { + isActive, _ = systemctl.IsActive(clamServiceNameUbuntu) + } + if isActive { + return false + } + } + clams, _ := clamRepo.List(commonRepo.WithByStatus(constant.StatusEnable)) + for i := 0; i < len(clams); i++ { + global.Cron.Remove(cron.EntryID(clams[i].EntryID)) + _ = clamRepo.Update(clams[i].ID, map[string]interface{}{"status": constant.StatusDisable, "entry_id": 0}) + } + return true +} + func loadFileByName(name string) []string { var logPaths []string pathItem := path.Join(global.CONF.System.DataDir, resultDir, name) diff --git a/agent/app/service/container.go b/agent/app/service/container.go index f5d754b8e..88491c7d3 100644 --- a/agent/app/service/container.go +++ b/agent/app/service/container.go @@ -186,7 +186,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro IsFromApp = true } - ports := loadContainerPort(item.Ports) + exposePorts := transPortToStr(records[i].Ports) info := dto.ContainerInfo{ ContainerID: item.ID, CreateTime: time.Unix(item.Created, 0).Format(constant.DateTimeLayout), @@ -195,7 +195,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro ImageName: item.Image, State: item.State, RunTime: item.Status, - Ports: ports, + Ports: exposePorts, IsFromApp: IsFromApp, IsFromCompose: IsFromCompose, } @@ -553,6 +553,8 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai } } + exposePorts, _ := loadPortByInspect(oldContainer.ID, client) + data.ExposedPorts = loadContainerPortForInfo(exposePorts) networkSettings := oldContainer.NetworkSettings bridgeNetworkSettings := networkSettings.Networks[data.Network] if bridgeNetworkSettings.IPAMConfig != nil { @@ -579,19 +581,6 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai for key, val := range oldContainer.Config.Labels { data.Labels = append(data.Labels, fmt.Sprintf("%s=%s", key, val)) } - for key, val := range oldContainer.HostConfig.PortBindings { - var itemPort dto.PortHelper - if !strings.Contains(string(key), "/") { - continue - } - itemPort.ContainerPort = strings.Split(string(key), "/")[0] - itemPort.Protocol = strings.Split(string(key), "/")[1] - for _, binds := range val { - itemPort.HostIP = binds.HostIP - itemPort.HostPort = binds.HostPort - data.ExposedPorts = append(data.ExposedPorts, itemPort) - } - } data.AutoRemove = oldContainer.HostConfig.AutoRemove data.Privileged = oldContainer.HostConfig.Privileged data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts @@ -1021,6 +1010,10 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s break } } + if len(containers) == 0 { + composeItem, _ := composeRepo.GetRecord(commonRepo.WithByName(req.Name)) + filePath = composeItem.Path + } } if _, err := os.Stat(filePath); err != nil { return "" @@ -1109,22 +1102,6 @@ func checkImageExist(client *client.Client, imageItem string) bool { return false } -func checkImage(client *client.Client, imageItem string) bool { - images, err := client.ImageList(context.Background(), image.ListOptions{}) - if err != nil { - return false - } - - for _, img := range images { - for _, tag := range img.RepoTags { - if tag == imageItem || tag == imageItem+":latest" { - return true - } - } - } - return false -} - func pullImages(ctx context.Context, client *client.Client, imageName string) error { options := image.PullOptions{} repos, _ := imageRepoRepo.List() @@ -1340,7 +1317,7 @@ func reCreateAfterUpdate(name string, client *client.Client, config *container.C if err := client.ContainerStart(ctx, oldContainer.ID, container.StartOptions{}); err != nil { global.LOG.Errorf("restart after container update failed, err: %v", err) } - global.LOG.Errorf("recreate after container update successful") + global.LOG.Info("recreate after container update successful") } func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper { @@ -1363,7 +1340,27 @@ func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper { return datas } -func loadContainerPort(ports []types.Port) []string { +func loadPortByInspect(id string, client *client.Client) ([]types.Port, error) { + container, err := client.ContainerInspect(context.Background(), id) + if err != nil { + return nil, err + } + var itemPorts []types.Port + for key, val := range container.ContainerJSONBase.HostConfig.PortBindings { + if !strings.Contains(string(key), "/") { + continue + } + item := strings.Split(string(key), "/") + itemPort, _ := strconv.ParseUint(item[0], 10, 16) + + 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}) + } + } + return itemPorts, nil +} +func transPortToStr(ports []types.Port) []string { var ( ipv4Ports []types.Port ipv6Ports []types.Port @@ -1475,3 +1472,39 @@ func loadComposeCount(client *client.Client) int { return len(composeMap) } +func loadContainerPortForInfo(itemPorts []types.Port) []dto.PortHelper { + var exposedPorts []dto.PortHelper + samePortMap := make(map[string]dto.PortHelper) + ports := transPortToStr(itemPorts) + var itemPort dto.PortHelper + for _, item := range ports { + itemStr := strings.Split(item, "->") + if len(itemStr) < 2 { + continue + } + lastIndex := strings.LastIndex(itemStr[0], ":") + if lastIndex == -1 { + itemPort.HostPort = itemStr[0] + } else { + itemPort.HostIP = itemStr[0][0:lastIndex] + itemPort.HostPort = itemStr[0][lastIndex+1:] + } + itemContainer := strings.Split(itemStr[1], "/") + if len(itemContainer) != 2 { + continue + } + itemPort.ContainerPort = itemContainer[0] + itemPort.Protocol = itemContainer[1] + keyItem := fmt.Sprintf("%s->%s/%s", itemPort.HostPort, itemPort.ContainerPort, itemPort.Protocol) + if val, ok := samePortMap[keyItem]; ok { + val.HostIP = "" + samePortMap[keyItem] = val + } else { + samePortMap[keyItem] = itemPort + } + } + for _, val := range samePortMap { + exposedPorts = append(exposedPorts, val) + } + return exposedPorts +} diff --git a/agent/app/service/container_compose.go b/agent/app/service/container_compose.go index 2c1769498..b3bfb08e8 100644 --- a/agent/app/service/container_compose.go +++ b/agent/app/service/container_compose.go @@ -53,6 +53,20 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface } composeCreatedByLocal, _ := composeRepo.ListRecord() + + composeLocalMap := make(map[string]dto.ComposeInfo) + for _, localItem := range composeCreatedByLocal { + composeItemLocal := dto.ComposeInfo{ + ContainerNumber: 0, + CreatedAt: localItem.CreatedAt.Format(constant.DateTimeLayout), + ConfigFile: localItem.Path, + Workdir: strings.TrimSuffix(localItem.Path, "/docker-compose.yml"), + } + composeItemLocal.CreatedBy = "1Panel" + composeItemLocal.Path = localItem.Path + composeLocalMap[localItem.Name] = composeItemLocal + } + composeMap := make(map[string]dto.ComposeInfo) for _, container := range list { if name, ok := container.Labels[composeProjectLabel]; ok { @@ -96,12 +110,24 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface } } } - for _, item := range composeCreatedByLocal { - if err := composeRepo.DeleteRecord(commonRepo.WithByID(item.ID)); err != nil { - global.LOG.Error(err) + + mergedMap := make(map[string]dto.ComposeInfo) + for key, localItem := range composeLocalMap { + mergedMap[key] = localItem + } + for key, item := range composeMap { + if existingItem, exists := mergedMap[key]; exists { + if item.ContainerNumber > 0 { + if existingItem.ContainerNumber <= 0 { + mergedMap[key] = item + } + } + } else { + mergedMap[key] = item } } - for key, value := range composeMap { + + for key, value := range mergedMap { value.Name = key records = append(records, value) } @@ -128,7 +154,8 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface } BackDatas = records[start:end] } - return int64(total), BackDatas, nil + listItem := loadEnv(BackDatas) + return int64(total), listItem, nil } func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) { @@ -142,6 +169,9 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) { if err := u.loadPath(&req); err != nil { return false, err } + if err := newComposeEnv(req.Path, req.Env); err != nil { + return false, err + } cmd := exec.Command("docker", "compose", "-f", req.Path, "config") stdout, err := cmd.CombinedOutput() if err != nil { @@ -164,6 +194,9 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error { if err != nil { return fmt.Errorf("new task for image build failed, err: %v", err) } + if err := newComposeEnv(req.Path, req.Env); err != nil { + return err + } go func() { taskItem.AddSubTask(i18n.GetMsgByKey("ComposeCreate"), func(t *task.Task) error { cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d") @@ -173,7 +206,7 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error { _, _ = compose.Down(req.Path) return err } - _ = composeRepo.CreateRecord(&model.Compose{Name: req.Name}) + _ = composeRepo.CreateRecord(&model.Compose{Name: req.Name, Path: req.Path}) return nil }, nil) _ = taskItem.Execute() @@ -183,23 +216,43 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error { } func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error { + if len(req.Path) == 0 && req.Operation == "delete" { + _ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name)) + return nil + } if cmd.CheckIllegal(req.Path, req.Operation) { return buserr.New(constant.ErrCmdIllegal) } if _, err := os.Stat(req.Path); err != nil { return fmt.Errorf("load file with path %s failed, %v", req.Path, err) } - if stdout, err := compose.Operate(req.Path, req.Operation); err != nil { - return errors.New(string(stdout)) - } - global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name) - if req.Operation == "down" { - _ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name)) + if req.Operation == "delete" { + if stdout, err := compose.Operate(req.Path, "down"); err != nil { + return errors.New(string(stdout)) + } if req.WithFile { + _ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name)) _ = os.RemoveAll(path.Dir(req.Path)) + } else { + composeItem, _ := composeRepo.GetRecord(commonRepo.WithByName(req.Name)) + if composeItem.Path == "" { + upMap := make(map[string]interface{}) + upMap["path"] = req.Path + _ = composeRepo.UpdateRecord(req.Name, upMap) + } + } + return nil + } + if req.Operation == "up" { + if stdout, err := compose.Up(req.Path); err != nil { + return errors.New(string(stdout)) + } + } else { + if stdout, err := compose.Operate(req.Path, req.Operation); err != nil { + return errors.New(string(stdout)) } } - + global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name) return nil } @@ -221,6 +274,10 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error { write.Flush() global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.Path) + if err := newComposeEnv(req.Path, req.Env); err != nil { + return err + } + if stdout, err := compose.Up(req.Path); err != nil { if err := recreateCompose(string(oldFile), req.Path); err != nil { return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err) @@ -269,3 +326,43 @@ func recreateCompose(content, path string) error { } return nil } + +func loadEnv(list []dto.ComposeInfo) []dto.ComposeInfo { + for i := 0; i < len(list); i++ { + envFilePath := path.Join(path.Dir(list[i].Path), "1panel.env") + file, err := os.ReadFile(envFilePath) + if err != nil { + continue + } + lines := strings.Split(string(file), "\n") + for _, line := range lines { + lineItem := strings.TrimSpace(line) + if len(lineItem) != 0 && !strings.HasPrefix(lineItem, "#") { + list[i].Env = append(list[i].Env, lineItem) + } + } + } + return list +} + +func newComposeEnv(pathItem string, env []string) error { + if len(env) == 0 { + return nil + } + envFilePath := path.Join(path.Dir(pathItem), "1panel.env") + file, err := os.OpenFile(envFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + if err != nil { + global.LOG.Errorf("failed to create env file: %v", err) + return err + } + defer file.Close() + for _, env := range env { + envItem := strings.TrimSpace(env) + if _, err := file.WriteString(fmt.Sprintf("%s\n", envItem)); err != nil { + global.LOG.Errorf("failed to write env to file: %v", err) + return err + } + } + global.LOG.Infof("1panel.env file successfully created or updated with env variables in %s", envFilePath) + return nil +} diff --git a/agent/app/service/container_volume.go b/agent/app/service/container_volume.go index 8e822d548..5ba34dfe6 100644 --- a/agent/app/service/container_volume.go +++ b/agent/app/service/container_volume.go @@ -52,7 +52,7 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{ records = list.Volumes[start:end] } - nyc, _ := time.LoadLocation(common.LoadTimeZone()) + nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) for _, item := range records { tag := make([]string, 0) for _, val := range item.Labels { diff --git a/agent/app/service/cronjob_backup.go b/agent/app/service/cronjob_backup.go index f74254edb..4f5b6b1b2 100644 --- a/agent/app/service/cronjob_backup.go +++ b/agent/app/service/cronjob_backup.go @@ -14,6 +14,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/files" + "github.com/pkg/errors" ) func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) error { @@ -28,6 +29,9 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) e } apps = append(apps, app) } + if len(apps) == 0 { + return errors.New("no such app in database!") + } accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ",")) if err != nil { return err @@ -61,6 +65,9 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) e func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Time) error { webs := loadWebsForJob(cronjob) + if len(webs) == 0 { + return errors.New("no such website in database!") + } accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ",")) if err != nil { return err @@ -94,6 +101,9 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Time) error { dbs := loadDbsForJob(cronjob) + if len(dbs) == 0 { + return errors.New("no such db in database!") + } accountMap, err := NewBackupClientMap(strings.Split(cronjob.SourceAccountIDs, ",")) if err != nil { return err diff --git a/agent/app/service/cronjob_helper.go b/agent/app/service/cronjob_helper.go index bbb1a664c..1891defd3 100644 --- a/agent/app/service/cronjob_helper.go +++ b/agent/app/service/cronjob_helper.go @@ -4,7 +4,7 @@ import ( "context" "fmt" "os" - "path" + pathUtils "path" "strings" "time" @@ -104,8 +104,8 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, logPath string) erro cronjob.Executor = "bash" } if cronjob.ScriptMode == "input" { - fileItem := path.Join(global.CONF.System.BaseDir, "1panel", "task", "shell", cronjob.Name, cronjob.Name+".sh") - _ = os.MkdirAll(path.Dir(fileItem), os.ModePerm) + fileItem := pathUtils.Join(global.CONF.System.BaseDir, "1panel", "task", "shell", cronjob.Name, cronjob.Name+".sh") + _ = os.MkdirAll(pathUtils.Dir(fileItem), os.ModePerm) shellFile, err := os.OpenFile(fileItem, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) if err != nil { return err @@ -149,7 +149,6 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string, secret string) excludes := strings.Split(exclusionRules, ",") excludeRules := "" - excludes = append(excludes, "*.sock") for _, exclude := range excludes { if len(exclude) == 0 { continue @@ -172,10 +171,14 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string, secret string) if len(secret) != 0 { extraCmd := "| openssl enc -aes-256-cbc -salt -k '" + secret + "' -out" - commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s %s %s", " -"+excludeRules, path, extraCmd, targetDir+"/"+name) + commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -print) -zcf %s %s %s %s", sourceDir, " -"+excludeRules, path, extraCmd, targetDir+"/"+name) 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 %s", targetDir+"/"+name, excludeRules, path) + itemPrefix := pathUtils.Base(sourceDir) + if itemPrefix == "/" { + itemPrefix = "" + } + 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", sourceDir, "%P\n", itemPrefix, targetDir+"/"+name, excludeRules, path) global.LOG.Debug(commands) } stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour) @@ -223,19 +226,19 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t if err != nil { return msgs, "", nil } - baseDir := path.Join(nginx.GetPath(), "www", "sites") + baseDir := pathUtils.Join(nginx.GetPath(), "www", "sites") fileOp := files.NewFileOp() for _, website := range websites { - websiteLogDir := path.Join(baseDir, website.Alias, "log") - srcAccessLogPath := path.Join(websiteLogDir, "access.log") - srcErrorLogPath := path.Join(websiteLogDir, "error.log") - dstLogDir := path.Join(global.CONF.System.Backup, "log", "website", website.Alias) + websiteLogDir := pathUtils.Join(baseDir, website.Alias, "log") + srcAccessLogPath := pathUtils.Join(websiteLogDir, "access.log") + srcErrorLogPath := pathUtils.Join(websiteLogDir, "error.log") + dstLogDir := pathUtils.Join(global.CONF.System.Backup, "log", "website", website.Alias) if !fileOp.Stat(dstLogDir) { _ = os.MkdirAll(dstLogDir, 0755) } dstName := fmt.Sprintf("%s_log_%s.gz", website.PrimaryDomain, startTime.Format(constant.DateTimeSlimLayout)) - dstFilePath := path.Join(dstLogDir, dstName) + dstFilePath := pathUtils.Join(dstLogDir, dstName) filePaths = append(filePaths, dstFilePath) if err = backupLogFile(dstFilePath, websiteLogDir, fileOp); err != nil { @@ -249,7 +252,6 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t _ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755) } msg := i18n.GetMsgWithMap("CutWebsiteLogSuccess", map[string]interface{}{"name": website.PrimaryDomain, "path": dstFilePath}) - global.LOG.Infof(msg) msgs = append(msgs, msg) } u.removeExpiredLog(*cronjob) @@ -258,18 +260,18 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t func backupLogFile(dstFilePath, websiteLogDir string, fileOp files.FileOp) error { if err := cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, websiteLogDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil { - dstDir := path.Dir(dstFilePath) - if err = fileOp.Copy(path.Join(websiteLogDir, "access.log"), dstDir); err != nil { + dstDir := pathUtils.Dir(dstFilePath) + if err = fileOp.Copy(pathUtils.Join(websiteLogDir, "access.log"), dstDir); err != nil { return err } - if err = fileOp.Copy(path.Join(websiteLogDir, "error.log"), dstDir); err != nil { + if err = fileOp.Copy(pathUtils.Join(websiteLogDir, "error.log"), dstDir); err != nil { return err } if err = cmd.ExecCmd(fmt.Sprintf("tar -czf %s -C %s %s", dstFilePath, dstDir, strings.Join([]string{"access.log", "error.log"}, " "))); err != nil { return err } - _ = fileOp.DeleteFile(path.Join(dstDir, "access.log")) - _ = fileOp.DeleteFile(path.Join(dstDir, "error.log")) + _ = fileOp.DeleteFile(pathUtils.Join(dstDir, "access.log")) + _ = fileOp.DeleteFile(pathUtils.Join(dstDir, "error.log")) return nil } return nil @@ -287,8 +289,8 @@ func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap cloudSrc := strings.TrimPrefix(file, global.CONF.System.TmpDir+"/") for _, account := range accounts { if len(account) != 0 { - global.LOG.Debugf("start upload file to %s, dir: %s", accountMap[account].name, path.Join(accountMap[account].backupPath, cloudSrc)) - if _, err := accountMap[account].client.Upload(file, path.Join(accountMap[account].backupPath, cloudSrc)); err != nil { + 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 { return "", err } global.LOG.Debugf("upload successful!") @@ -298,7 +300,6 @@ func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap } func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]backupClientHelper, record model.BackupRecord) { - global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies) var opts []repo.DBOption opts = append(opts, commonRepo.WithByFrom("cronjob")) opts = append(opts, backupRepo.WithByCronID(cronjob.ID)) @@ -317,14 +318,14 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m if cronjob.Type == "snapshot" { for _, account := range accounts { if len(account) != 0 { - _, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName)) + _, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName)) } } _ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(records[i].FileName, ".tar.gz"))) } else { for _, account := range accounts { if len(account) != 0 { - _, _ = accountMap[account].client.Delete(path.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName)) + _, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName)) } } } @@ -333,7 +334,6 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m } func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) { - global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies) records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc")) if len(records) <= int(cronjob.RetainCopies) { return diff --git a/agent/app/service/dashboard.go b/agent/app/service/dashboard.go index 74176bd97..78002b300 100644 --- a/agent/app/service/dashboard.go +++ b/agent/app/service/dashboard.go @@ -176,12 +176,12 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d currentInfo.Procs = hostInfo.Procs currentInfo.CPUTotal, _ = cpu.Counts(true) - totalPercent, _ := cpu.Percent(0, false) + totalPercent, _ := cpu.Percent(100*time.Millisecond, false) if len(totalPercent) == 1 { currentInfo.CPUUsedPercent = totalPercent[0] currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal) } - currentInfo.CPUPercent, _ = cpu.Percent(0, true) + currentInfo.CPUPercent, _ = cpu.Percent(100*time.Millisecond, true) loadInfo, _ := load.Avg() currentInfo.Load1 = loadInfo.Load1 @@ -375,13 +375,13 @@ func loadDiskInfo() []dto.DiskInfo { if strings.HasPrefix(fields[6], "/snap") || len(strings.Split(fields[6], "/")) > 10 { continue } - if strings.TrimSpace(fields[1]) == "tmpfs" { + if strings.TrimSpace(fields[1]) == "tmpfs" || strings.TrimSpace(fields[1]) == "overlay" { continue } if strings.Contains(fields[2], "K") { continue } - if strings.Contains(fields[6], "docker") { + if strings.Contains(fields[6], "docker") || strings.Contains(fields[6], "podman") || strings.Contains(fields[6], "containerd") || strings.HasPrefix(fields[6], "/var/lib/containers") { continue } isExclude := false diff --git a/agent/app/service/database_mysql.go b/agent/app/service/database_mysql.go index aa62cf066..9a6939dba 100644 --- a/agent/app/service/database_mysql.go +++ b/agent/app/service/database_mysql.go @@ -141,6 +141,10 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode } func (u *MysqlService) BindUser(req dto.BindUser) error { + if cmd.CheckIllegal(req.Username, req.Password, req.Permission) { + return buserr.New(constant.ErrCmdIllegal) + } + dbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(req.Database), commonRepo.WithByName(req.DB)) if err != nil { return err diff --git a/agent/app/service/docker.go b/agent/app/service/docker.go index 45d2bad30..beb6e0b84 100644 --- a/agent/app/service/docker.go +++ b/agent/app/service/docker.go @@ -4,14 +4,17 @@ import ( "bufio" "context" "encoding/json" + "fmt" "os" "path" "strings" "github.com/1Panel-dev/1Panel/agent/app/dto" "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/docker" + "github.com/1Panel-dev/1Panel/agent/utils/systemctl" "github.com/pkg/errors" ) @@ -198,6 +201,9 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error { } if len(daemonMap) == 0 { _ = os.Remove(constant.DaemonJsonPath) + if err := restartDocker(); err != nil { + return err + } return nil } newJson, err := json.MarshalIndent(daemonMap, "", "\t") @@ -207,10 +213,12 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error { if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil { return err } + if err := validateDockerConfig(); err != nil { + return err + } - stdout, err := cmd.Exec("systemctl restart docker") - if err != nil { - return errors.New(string(stdout)) + if err := restartDocker(); err != nil { + return err } return nil } @@ -244,6 +252,7 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error { changeLogOption(daemonMap, req.LogMaxFile, req.LogMaxSize) if len(daemonMap) == 0 { _ = os.Remove(constant.DaemonJsonPath) + _ = restartDocker() return nil } newJson, err := json.MarshalIndent(daemonMap, "", "\t") @@ -254,9 +263,12 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error { return err } - stdout, err := cmd.Exec("systemctl restart docker") - if err != nil { - return errors.New(string(stdout)) + if err := validateDockerConfig(); err != nil { + return err + } + + if err := restartDocker(); err != nil { + return err } return nil } @@ -284,6 +296,7 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error { } if len(daemonMap) == 0 { _ = os.Remove(constant.DaemonJsonPath) + _ = restartDocker() return nil } newJson, err := json.MarshalIndent(daemonMap, "", "\t") @@ -294,9 +307,12 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error { return err } - stdout, err := cmd.Exec("systemctl restart docker") - if err != nil { - return errors.New(string(stdout)) + if err := validateDockerConfig(); err != nil { + return err + } + + if err := restartDocker(); err != nil { + return err } return nil } @@ -304,6 +320,9 @@ 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() @@ -319,19 +338,38 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error { _, _ = write.WriteString(req.File) write.Flush() - stdout, err := cmd.Exec("systemctl restart docker") - if err != nil { - return errors.New(string(stdout)) + if err := validateDockerConfig(); err != nil { + return err + } + + if err := restartDocker(); err != nil { + return err } return nil } func (u *DockerService) OperateDocker(req dto.DockerOperation) error { service := "docker" - if req.Operation == "stop" { - service = "docker.socket" + sudo := cmd.SudoHandleCmd() + dockerCmd, err := getDockerRestartCommand() + if err != nil { + return err } - stdout, err := cmd.Execf("systemctl %s %s ", req.Operation, service) + if req.Operation == "stop" { + isSocketActive, _ := systemctl.IsActive("docker.socket") + if isSocketActive { + std, err := cmd.Execf("%s systemctl stop docker.socket", sudo) + if err != nil { + global.LOG.Errorf("handle systemctl stop docker.socket failed, err: %v", std) + } + } + } + if req.Operation == "restart" { + if err := validateDockerConfig(); err != nil { + return err + } + } + stdout, err := cmd.Execf("%s %s %s ", dockerCmd, req.Operation, service) if err != nil { return errors.New(string(stdout)) } @@ -386,3 +424,41 @@ func changeLogOption(daemonMap map[string]interface{}, logMaxFile, logMaxSize st } } } + +func validateDockerConfig() error { + if !cmd.Which("dockerd") { + return nil + } + stdout, err := cmd.Exec("dockerd --validate") + if strings.Contains(stdout, "unknown flag: --validate") { + return nil + } + if err != nil || (stdout != "" && strings.TrimSpace(stdout) != "configuration OK") { + return fmt.Errorf("Docker configuration validation failed, err: %v", stdout) + } + return nil +} + +func getDockerRestartCommand() (string, error) { + stdout, err := cmd.Exec("which docker") + if err != nil { + return "", fmt.Errorf("failed to find docker: %v", err) + } + dockerPath := stdout + if strings.Contains(dockerPath, "snap") { + return "snap", nil + } + return "systemctl", nil +} + +func restartDocker() error { + restartCmd, err := getDockerRestartCommand() + if err != nil { + return err + } + stdout, err := cmd.Execf("%s restart docker", restartCmd) + if err != nil { + return fmt.Errorf("failed to restart Docker: %s", stdout) + } + return nil +} diff --git a/agent/app/service/firewall.go b/agent/app/service/firewall.go index 6bcd7a41b..744adc4d3 100644 --- a/agent/app/service/firewall.go +++ b/agent/app/service/firewall.go @@ -588,14 +588,6 @@ func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) er if err := client.Port(fireClient.FireInfo{Port: "443", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil { return err } - apps := u.loadPortByApp() - for _, app := range apps { - if len(app.HttpPort) != 0 && app.HttpPort != "0" { - if err := client.Port(fireClient.FireInfo{Port: app.HttpPort, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil { - return err - } - } - } return client.Reload() } diff --git a/agent/app/service/image_repo.go b/agent/app/service/image_repo.go index 8061e5a4b..406650dc7 100644 --- a/agent/app/service/image_repo.go +++ b/agent/app/service/image_repo.go @@ -84,8 +84,11 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error { if imageRepo.ID != 0 { return constant.ErrRecordExist } + if req.Protocol == "http" { - _ = u.handleRegistries(req.DownloadUrl, "", "create") + if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil { + return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err) + } stdout, err := cmd.Exec("systemctl restart docker") if err != nil { return errors.New(string(stdout)) @@ -113,22 +116,18 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error { } } + if req.Auth { + if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { + return err + } + } + if err := copier.Copy(&imageRepo, &req); err != nil { return errors.WithMessage(constant.ErrStructTransform, err.Error()) } imageRepo.Status = constant.StatusSuccess - if req.Auth { - if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { - imageRepo.Status = constant.StatusFailed - imageRepo.Message = err.Error() - } - } - if err := imageRepoRepo.Create(&imageRepo); err != nil { - return err - } - - return nil + return imageRepoRepo.Create(&imageRepo) } func (u *ImageRepoService) BatchDelete(req dto.ImageRepoDelete) error { @@ -154,32 +153,47 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error { if err != nil { return err } - if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) { - _ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update") + 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) + } + } + 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) + } + } + 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) + } + } + if repo.Auth != req.Auth || repo.DownloadUrl != req.DownloadUrl { if repo.Auth { _, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl) } - stdout, err := cmd.Exec("systemctl restart docker") - if err != nil { - return errors.New(string(stdout)) + if req.Auth { + if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { + return err + } } } + if err := validateDockerConfig(); err != nil { + return err + } + if err := restartDocker(); err != nil { + return err + } + upMap := make(map[string]interface{}) upMap["download_url"] = req.DownloadUrl upMap["protocol"] = req.Protocol upMap["username"] = req.Username upMap["password"] = req.Password upMap["auth"] = req.Auth - upMap["status"] = constant.StatusSuccess upMap["message"] = "" - if req.Auth { - if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil { - upMap["status"] = constant.StatusFailed - upMap["message"] = err.Error() - } - } return imageRepoRepo.Update(req.ID, upMap) } diff --git a/agent/app/service/monitor.go b/agent/app/service/monitor.go index 2d87e2a5b..9a804f5b2 100644 --- a/agent/app/service/monitor.go +++ b/agent/app/service/monitor.go @@ -46,7 +46,7 @@ func NewIMonitorService() IMonitorService { } func (m *MonitorService) LoadMonitorData(req dto.MonitorSearch) ([]dto.MonitorData, error) { - loc, _ := time.LoadLocation(common.LoadTimeZone()) + loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) req.StartTime = req.StartTime.In(loc) req.EndTime = req.EndTime.In(loc) diff --git a/agent/app/service/snapshot.go b/agent/app/service/snapshot.go index 6fd966e3a..fce7dfd95 100644 --- a/agent/app/service/snapshot.go +++ b/agent/app/service/snapshot.go @@ -27,7 +27,7 @@ type SnapshotService struct { } type ISnapshotService interface { - SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) + SearchWithPage(req dto.PageSnapshot) (int64, interface{}, error) LoadSize(req dto.SearchWithPage) ([]dto.SnapshotFile, error) LoadSnapshotData() (dto.SnapshotData, error) SnapshotCreate(req dto.SnapshotCreate) error @@ -46,8 +46,8 @@ func NewISnapshotService() ISnapshotService { return &SnapshotService{} } -func (u *SnapshotService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) { - total, records, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithByLikeName(req.Info)) +func (u *SnapshotService) SearchWithPage(req dto.PageSnapshot) (int64, interface{}, error) { + total, records, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithByLikeName(req.Info), commonRepo.WithOrderRuleBy(req.OrderBy, req.Order)) if err != nil { return 0, nil, err } diff --git a/agent/app/service/snapshot_recover.go b/agent/app/service/snapshot_recover.go index 5b737bada..7351ddb7c 100644 --- a/agent/app/service/snapshot_recover.go +++ b/agent/app/service/snapshot_recover.go @@ -306,16 +306,7 @@ func recoverAppData(src string, itemHelper *snapRecoverHelper) error { go func(app model.AppInstall) { defer wg.Done() dockerComposePath := app.GetComposePath() - out, err := compose.Down(dockerComposePath) - if err != nil { - _ = handleErr(app, err, out) - return - } - out, err = compose.Up(dockerComposePath) - if err != nil { - _ = handleErr(app, err, out) - return - } + _, _ = compose.Up(dockerComposePath) app.Status = constant.Running _ = appInstallRepo.Save(context.Background(), &app) }(appInstalls[i]) diff --git a/agent/app/service/ssh.go b/agent/app/service/ssh.go index b9a887047..02e3bf297 100644 --- a/agent/app/service/ssh.go +++ b/agent/app/service/ssh.go @@ -124,6 +124,16 @@ func (u *SSHService) OperateSSH(operation string) error { if operation == "enable" || operation == "disable" { serviceName += ".service" } + if operation == "stop" { + isSocketActive, _ := systemctl.IsActive(serviceName + ".socket") + if isSocketActive { + std, err := cmd.Execf("%s systemctl stop %s", sudo, serviceName+".socket") + if err != nil { + global.LOG.Errorf("handle systemctl stop %s.socket failed, err: %v", serviceName, std) + } + } + } + stdout, err := cmd.Execf("%s systemctl %s %s", sudo, operation, serviceName) if err != nil { if strings.Contains(stdout, "alias name or linked unit file") { @@ -308,7 +318,7 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) { showCountFrom := (req.Page - 1) * req.PageSize showCountTo := req.Page * req.PageSize - nyc, _ := time.LoadLocation(common.LoadTimeZone()) + nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) qqWry, err := qqwry.NewQQwry() if err != nil { global.LOG.Errorf("load qqwry datas failed: %s", err) diff --git a/agent/constant/errs.go b/agent/constant/errs.go index 93291f061..ff3456aac 100644 --- a/agent/constant/errs.go +++ b/agent/constant/errs.go @@ -128,7 +128,8 @@ var ( ) var ( - ErrFirewall = "ErrFirewall" + ErrFirewallNone = "ErrFirewallNone" + ErrFirewallBoth = "ErrFirewallBoth" ) var ( diff --git a/agent/cron/cron.go b/agent/cron/cron.go index 39746de4b..6d8cca0b1 100644 --- a/agent/cron/cron.go +++ b/agent/cron/cron.go @@ -17,14 +17,14 @@ import ( ) func Run() { - nyc, _ := time.LoadLocation(common.LoadTimeZone()) + nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) global.Cron = cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger))) var ( interval model.Setting status model.Setting ) - syncBeforeStart() + go syncBeforeStart() if err := global.DB.Where("key = ?", "MonitorStatus").Find(&status).Error; err != nil { global.LOG.Errorf("load monitor status from db failed, err: %v", err) } diff --git a/agent/cron/job/ssl.go b/agent/cron/job/ssl.go index ece746033..9f4bde347 100644 --- a/agent/cron/job/ssl.go +++ b/agent/cron/job/ssl.go @@ -22,7 +22,7 @@ func (ssl *ssl) Run() { sslRepo := repo.NewISSLRepo() sslService := service.NewIWebsiteSSLService() sslList, _ := sslRepo.List() - nyc, _ := time.LoadLocation(common.LoadTimeZone()) + nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) global.LOG.Info("The scheduled certificate update task is currently in progress ...") now := time.Now().Add(10 * time.Second) for _, s := range sslList { diff --git a/agent/cron/job/website.go b/agent/cron/job/website.go index 703d329b0..f0821e111 100644 --- a/agent/cron/job/website.go +++ b/agent/cron/job/website.go @@ -20,7 +20,7 @@ func NewWebsiteJob() *website { } func (w *website) Run() { - nyc, _ := time.LoadLocation(common.LoadTimeZone()) + nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) websites, _ := repo.NewIWebsiteRepo().List() global.LOG.Info("Website scheduled task in progress ...") now := time.Now().Add(10 * time.Minute) diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 517882e8f..a015362e8 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -181,7 +181,8 @@ ErrConfigAlreadyExist: "A configuration file with the same name already exists" ErrUserFindErr: "Failed to find user {{ .name }} {{ .err }}" #ssh -ErrFirewall: "No firewalld or ufw service is detected. Please check and try again!" +ErrFirewallNone: "No firewalld or ufw service detected on the system. Please check and try again!" +ErrFirewallBoth: "Both firewalld and ufw services are detected on the system. To avoid conflicts, please uninstall one and try again!" #cronjob ErrCutWebsiteLog: "{{ .name }} website log cutting failed, error {{ .err }}" diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index 28418c23d..a8c0295c9 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -182,7 +182,8 @@ ErrConfigAlreadyExist: "已存在同名配置文件" ErrUserFindErr: "用戶 {{ .name }} 查找失敗 {{ .err }}" #ssh -ErrFirewall: "當前未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!" +ErrFirewallNone: "未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!" +ErrFirewallBoth: "檢測到系統同時存在 firewalld 或 ufw 服務,為避免衝突,請卸載後重試!" #cronjob ErrCutWebsiteLog: "{{ .name }} 網站日誌切割失敗,錯誤 {{ .err }}" diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 820f793bc..2ad4a0f9a 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -180,7 +180,8 @@ ErrConfigAlreadyExist: "已存在同名配置文件" ErrUserFindErr: "用户 {{ .name }} 查找失败 {{ .err }}" #ssh -ErrFirewall: "当前未检测到系统 firewalld 或 ufw 服务,请检查后重试!" +ErrFirewallNone: "未检测到系统 firewalld 或 ufw 服务,请检查后重试!" +ErrFirewallBoth: "检测到系统同时存在 firewalld 或 ufw 服务,为避免冲突,请卸载后重试!" #cronjob ErrCutWebsiteLog: "{{ .name }} 网站日志切割失败,错误 {{ .err }}" diff --git a/agent/utils/cloud_storage/client/s3.go b/agent/utils/cloud_storage/client/s3.go index 9fc7ebadd..2afc2fe1d 100644 --- a/agent/utils/cloud_storage/client/s3.go +++ b/agent/utils/cloud_storage/client/s3.go @@ -27,12 +27,15 @@ func NewS3Client(vars map[string]interface{}) (*s3Client, error) { if len(scType) == 0 { scType = "Standard" } + mode := loadParamFromVars("mode", vars) + if len(mode) == 0 { + mode = "virtual hosted" + } sess, err := session.NewSession(&aws.Config{ - Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""), - Endpoint: aws.String(endpoint), - Region: aws.String(region), - DisableSSL: aws.Bool(true), - S3ForcePathStyle: aws.Bool(false), + Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""), + Endpoint: aws.String(endpoint), + Region: aws.String(region), + DisableSSL: aws.Bool(true), S3ForcePathStyle: aws.Bool(mode == "path"), }) if err != nil { return nil, err diff --git a/agent/utils/cmd/cmd.go b/agent/utils/cmd/cmd.go index 8b31105ce..b41368d2f 100644 --- a/agent/utils/cmd/cmd.go +++ b/agent/utils/cmd/cmd.go @@ -238,7 +238,7 @@ func SudoHandleCmd() string { func Which(name string) bool { stdout, err := Execf("which %s", name) - if err != nil || (len(strings.ReplaceAll(stdout, "\n", "")) == 0 && strings.HasPrefix(stdout, "/")) { + if err != nil || (len(strings.ReplaceAll(stdout, "\n", "")) == 0) { return false } return true diff --git a/agent/utils/common/common.go b/agent/utils/common/common.go index e9dc087d5..8b3c2f9bd 100644 --- a/agent/utils/common/common.go +++ b/agent/utils/common/common.go @@ -268,13 +268,6 @@ func LoadSizeUnit2F(value float64) string { return fmt.Sprintf("%.2f", value) } -func LoadTimeZone() string { - loc := time.Now().Location() - if _, err := time.LoadLocation(loc.String()); err != nil { - return "Asia/Shanghai" - } - return loc.String() -} func LoadTimeZoneByCmd() string { loc := time.Now().Location().String() if _, err := time.LoadLocation(loc); err != nil { diff --git a/agent/utils/files/file_op.go b/agent/utils/files/file_op.go index 3ef93be6b..45cf71104 100644 --- a/agent/utils/files/file_op.go +++ b/agent/utils/files/file_op.go @@ -729,6 +729,7 @@ func (f FileOp) TarGzCompressPro(withDir bool, src, dst, secret, exclusionRules exStr := "" excludes := strings.Split(exclusionRules, ";") excludes = append(excludes, "*.sock") + excludes = append(excludes, "*.socket") for _, exclude := range excludes { if len(exclude) == 0 { continue diff --git a/agent/utils/firewall/client.go b/agent/utils/firewall/client.go index 82223ced9..80f421962 100644 --- a/agent/utils/firewall/client.go +++ b/agent/utils/firewall/client.go @@ -1,10 +1,9 @@ package firewall import ( - "os" - "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/utils/cmd" "github.com/1Panel-dev/1Panel/agent/utils/firewall/client" ) @@ -29,11 +28,18 @@ type FirewallClient interface { } func NewFirewallClient() (FirewallClient, error) { - if _, err := os.Stat("/usr/sbin/firewalld"); err == nil { + firewalld := cmd.Which("firewalld") + ufw := cmd.Which("ufw") + + if firewalld && ufw { + return nil, buserr.New(constant.ErrFirewallBoth) + } + + if firewalld { return client.NewFirewalld() } - if _, err := os.Stat("/usr/sbin/ufw"); err == nil { + if ufw { return client.NewUfw() } - return nil, buserr.New(constant.ErrFirewall) + return nil, buserr.New(constant.ErrFirewallNone) } diff --git a/agent/utils/firewall/client/firewalld.go b/agent/utils/firewall/client/firewalld.go index 9da5cc746..84db69373 100644 --- a/agent/utils/firewall/client/firewalld.go +++ b/agent/utils/firewall/client/firewalld.go @@ -8,6 +8,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/cmd" ) @@ -118,6 +119,9 @@ func (f *Firewall) ListPort() ([]FireInfo, error) { } func (f *Firewall) ListForward() ([]FireInfo, error) { + if err := f.EnableForward(); err != nil { + global.LOG.Errorf("init port forward failed, err: %v", err) + } stdout, err := cmd.Exec("firewall-cmd --zone=public --list-forward-ports") if err != nil { return nil, err diff --git a/agent/utils/firewall/client/ufw.go b/agent/utils/firewall/client/ufw.go index ecc5a1869..e90c4b81a 100644 --- a/agent/utils/firewall/client/ufw.go +++ b/agent/utils/firewall/client/ufw.go @@ -6,6 +6,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/buserr" "github.com/1Panel-dev/1Panel/agent/constant" + "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/cmd" ) @@ -108,6 +109,12 @@ func (f *Ufw) ListForward() ([]FireInfo, error) { if err != nil { return nil, err } + panelChian, _ := cmd.Execf("%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 diff --git a/agent/utils/mysql/helper/dump.go b/agent/utils/mysql/helper/dump.go deleted file mode 100644 index 3961db5e5..000000000 --- a/agent/utils/mysql/helper/dump.go +++ /dev/null @@ -1,303 +0,0 @@ -package helper - -import ( - "bufio" - "database/sql" - "fmt" - "io" - "os" - "strings" - "time" - - "github.com/1Panel-dev/1Panel/agent/constant" - "github.com/1Panel-dev/1Panel/agent/global" - _ "github.com/go-sql-driver/mysql" -) - -func init() {} - -type dumpOption struct { - isData bool - - tables []string - isAllTable bool - isDropTable bool - writer io.Writer -} - -type DumpOption func(*dumpOption) - -func WithDropTable() DumpOption { - return func(option *dumpOption) { - option.isDropTable = true - } -} - -func WithData() DumpOption { - return func(option *dumpOption) { - option.isData = true - } -} - -func WithWriter(writer io.Writer) DumpOption { - return func(option *dumpOption) { - option.writer = writer - } -} - -func Dump(dns string, opts ...DumpOption) error { - start := time.Now() - global.LOG.Infof("dump start at %s\n", start.Format(constant.DateTimeLayout)) - defer func() { - end := time.Now() - global.LOG.Infof("dump end at %s, cost %s\n", end.Format(constant.DateTimeLayout), end.Sub(start)) - }() - - var err error - - var o dumpOption - - for _, opt := range opts { - opt(&o) - } - - if len(o.tables) == 0 { - o.isAllTable = true - } - - if o.writer == nil { - o.writer = os.Stdout - } - - buf := bufio.NewWriter(o.writer) - defer buf.Flush() - - itemFile, lineNumber := "", 0 - - itemFile += "-- ----------------------------\n" - itemFile += "-- MySQL Database Dump\n" - itemFile += "-- Start Time: " + start.Format(constant.DateTimeLayout) + "\n" - itemFile += "-- ----------------------------\n\n\n" - - db, err := sql.Open("mysql", dns) - if err != nil { - global.LOG.Errorf("open mysql db failed, err: %v", err) - return err - } - defer db.Close() - - dbName, err := getDBNameFromDNS(dns) - if err != nil { - global.LOG.Errorf("get db name from dns failed, err: %v", err) - return err - } - _, err = db.Exec(fmt.Sprintf("USE `%s`", dbName)) - if err != nil { - global.LOG.Errorf("exec `use %s` failed, err: %v", dbName, err) - return err - } - - var tables []string - if o.isAllTable { - tmp, err := getAllTables(db) - if err != nil { - global.LOG.Errorf("get all tables failed, err: %v", err) - return err - } - tables = tmp - } else { - tables = o.tables - } - - for _, table := range tables { - if o.isDropTable { - itemFile += fmt.Sprintf("DROP TABLE IF EXISTS `%s`;\n", table) - } - - itemFile += "-- ----------------------------\n" - itemFile += fmt.Sprintf("-- Table structure for %s\n", table) - itemFile += "-- ----------------------------\n" - - createTableSQL, err := getCreateTableSQL(db, table) - if err != nil { - global.LOG.Errorf("get create table sql failed, err: %v", err) - return err - } - itemFile += createTableSQL - itemFile += ";\n\n\n\n" - - if o.isData { - itemFile += "-- ----------------------------\n" - itemFile += fmt.Sprintf("-- Records of %s\n", table) - itemFile += "-- ----------------------------\n" - - lineRows, err := db.Query(fmt.Sprintf("SELECT * FROM `%s`", table)) - if err != nil { - global.LOG.Errorf("exec `select * from %s` failed, err: %v", table, err) - return err - } - defer lineRows.Close() - - var columns []string - columns, err = lineRows.Columns() - if err != nil { - global.LOG.Errorf("get columes failed, err: %v", err) - return err - } - columnTypes, err := lineRows.ColumnTypes() - if err != nil { - global.LOG.Errorf("get colume types failed, err: %v", err) - return err - } - for lineRows.Next() { - row := make([]interface{}, len(columns)) - rowPointers := make([]interface{}, len(columns)) - for i := range columns { - rowPointers[i] = &row[i] - } - if err = lineRows.Scan(rowPointers...); err != nil { - global.LOG.Errorf("scan row data failed, err: %v", err) - return err - } - ssql := loadDataSql(row, columnTypes, table) - if len(ssql) != 0 { - itemFile += ssql - lineNumber++ - } - if lineNumber > 500 { - _, _ = buf.WriteString(itemFile) - itemFile = "" - lineNumber = 0 - } - } - - itemFile += "\n\n" - } - } - - itemFile += "-- ----------------------------\n" - itemFile += "-- Dumped by mysqldump\n" - itemFile += "-- Cost Time: " + time.Since(start).String() + "\n" - itemFile += "-- ----------------------------\n" - - _, _ = buf.WriteString(itemFile) - _ = buf.Flush() - - return nil -} - -func getCreateTableSQL(db *sql.DB, table string) (string, error) { - var createTableSQL string - err := db.QueryRow(fmt.Sprintf("SHOW CREATE TABLE `%s`", table)).Scan(&table, &createTableSQL) - if err != nil { - return "", err - } - createTableSQL = strings.Replace(createTableSQL, "CREATE TABLE", "CREATE TABLE IF NOT EXISTS", 1) - return createTableSQL, nil -} - -func getAllTables(db *sql.DB) ([]string, error) { - var tables []string - rows, err := db.Query("SHOW TABLES") - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var table string - err = rows.Scan(&table) - if err != nil { - return nil, err - } - tables = append(tables, table) - } - return tables, nil -} - -func loadDataSql(row []interface{}, columnTypes []*sql.ColumnType, table string) string { - ssql := "INSERT INTO `" + table + "` VALUES (" - for i, col := range row { - if col == nil { - ssql += "NULL" - } else { - Type := columnTypes[i].DatabaseTypeName() - Type = strings.Replace(Type, "UNSIGNED", "", -1) - Type = strings.Replace(Type, " ", "", -1) - switch Type { - case "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "INTEGER", "BIGINT": - if bs, ok := col.([]byte); ok { - ssql += string(bs) - } else { - ssql += fmt.Sprintf("%d", col) - } - case "FLOAT", "DOUBLE": - if bs, ok := col.([]byte); ok { - ssql += string(bs) - } else { - ssql += fmt.Sprintf("%f", col) - } - case "DECIMAL", "DEC": - ssql += fmt.Sprintf("%s", col) - - case "DATE": - t, ok := col.(time.Time) - if !ok { - global.LOG.Errorf("the DATE type conversion failed, err value: %v", col) - return "" - } - ssql += fmt.Sprintf("'%s'", t.Format("2006-01-02")) - case "DATETIME": - t, ok := col.(time.Time) - if !ok { - global.LOG.Errorf("the DATETIME type conversion failed, err value: %v", col) - return "" - } - ssql += fmt.Sprintf("'%s'", t.Format(constant.DateTimeLayout)) - case "TIMESTAMP": - t, ok := col.(time.Time) - if !ok { - global.LOG.Errorf("the TIMESTAMP type conversion failed, err value: %v", col) - return "" - } - ssql += fmt.Sprintf("'%s'", t.Format(constant.DateTimeLayout)) - case "TIME": - t, ok := col.([]byte) - if !ok { - global.LOG.Errorf("the TIME type conversion failed, err value: %v", col) - return "" - } - ssql += fmt.Sprintf("'%s'", string(t)) - case "YEAR": - t, ok := col.([]byte) - if !ok { - global.LOG.Errorf("the YEAR type conversion failed, err value: %v", col) - return "" - } - ssql += string(t) - case "CHAR", "VARCHAR", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT": - ssql += fmt.Sprintf("'%s'", strings.Replace(fmt.Sprintf("%s", col), "'", "''", -1)) - case "BIT", "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB": - ssql += fmt.Sprintf("0x%X", col) - case "ENUM", "SET": - ssql += fmt.Sprintf("'%s'", col) - case "BOOL", "BOOLEAN": - if col.(bool) { - ssql += "true" - } else { - ssql += "false" - } - case "JSON": - ssql += fmt.Sprintf("'%s'", col) - default: - global.LOG.Errorf("unsupported colume type: %s", Type) - return "" - } - } - if i < len(row)-1 { - ssql += "," - } - } - ssql += ");\n" - return ssql -} diff --git a/agent/utils/mysql/helper/source.go b/agent/utils/mysql/helper/source.go deleted file mode 100644 index f4b37ed98..000000000 --- a/agent/utils/mysql/helper/source.go +++ /dev/null @@ -1,244 +0,0 @@ -package helper - -import ( - "bufio" - "database/sql" - "errors" - "fmt" - "io" - "strings" - "time" - - "github.com/1Panel-dev/1Panel/agent/constant" - "github.com/1Panel-dev/1Panel/agent/global" -) - -type sourceOption struct { - dryRun bool - mergeInsert int - debug bool -} -type SourceOption func(*sourceOption) - -func WithMergeInsert(size int) SourceOption { - return func(o *sourceOption) { - o.mergeInsert = size - } -} - -type dbWrapper struct { - DB *sql.DB - debug bool - dryRun bool -} - -func newDBWrapper(db *sql.DB, dryRun, debug bool) *dbWrapper { - - return &dbWrapper{ - DB: db, - dryRun: dryRun, - debug: debug, - } -} - -func (db *dbWrapper) Exec(query string, args ...interface{}) (sql.Result, error) { - if db.debug { - global.LOG.Debugf("query %s", query) - } - - if db.dryRun { - return nil, nil - } - return db.DB.Exec(query, args...) -} - -func Source(dns string, reader io.Reader, opts ...SourceOption) error { - start := time.Now() - global.LOG.Infof("source start at %s", start.Format(constant.DateTimeLayout)) - defer func() { - end := time.Now() - global.LOG.Infof("source end at %s, cost %s", end.Format(constant.DateTimeLayout), end.Sub(start)) - }() - - var err error - var db *sql.DB - var o sourceOption - for _, opt := range opts { - opt(&o) - } - - dbName, err := getDBNameFromDNS(dns) - if err != nil { - global.LOG.Errorf("get db name from dns failed, err: %v", err) - return err - } - - db, err = sql.Open("mysql", dns) - if err != nil { - global.LOG.Errorf("open mysql db failed, err: %v", err) - return err - } - defer db.Close() - - dbWrapper := newDBWrapper(db, o.dryRun, o.debug) - - _, err = dbWrapper.Exec(fmt.Sprintf("USE `%s`;", dbName)) - if err != nil { - global.LOG.Errorf("exec `use %s` failed, err: %v", dbName, err) - return err - } - - db.SetConnMaxLifetime(3600) - - r := bufio.NewReader(reader) - _, err = dbWrapper.Exec("SET autocommit=0;") - if err != nil { - global.LOG.Errorf("exec `set autocommit=0` failed, err: %v", err) - return err - } - - for { - line, err := readLine(r) - if err != nil { - if err == io.EOF { - break - } - global.LOG.Errorf("read sql failed, err: %v", err) - return err - } - - ssql, err := trim(line) - if err != nil { - global.LOG.Errorf("trim sql failed, err: %v", err) - return err - } - - afterInsertSql := "" - if o.mergeInsert > 1 && strings.HasPrefix(ssql, "INSERT INTO") { - var insertSQLs []string - insertSQLs = append(insertSQLs, ssql) - for i := 0; i < o.mergeInsert-1; i++ { - line, err := readLine(r) - if err != nil { - if err == io.EOF { - break - } - return err - } - ssql2, err := trim(line) - if err != nil { - global.LOG.Errorf("trim merge insert sql failed, err: %v", err) - return err - } - if strings.HasPrefix(ssql2, "INSERT INTO") { - insertSQLs = append(insertSQLs, ssql2) - continue - } - afterInsertSql = ssql2 - break - } - ssql, err = mergeInsert(insertSQLs) - if err != nil { - global.LOG.Errorf("do merge insert failed, err: %v", err) - return err - } - } - - _, err = dbWrapper.Exec(ssql) - if err != nil { - global.LOG.Errorf("exec sql failed, err: %v", err) - return err - } - if len(afterInsertSql) != 0 { - _, err = dbWrapper.Exec(afterInsertSql) - if err != nil { - global.LOG.Errorf("exec sql failed, err: %v", err) - return err - } - } - } - - _, err = dbWrapper.Exec("COMMIT;") - if err != nil { - global.LOG.Errorf("exec `commit` failed, err: %v", err) - return err - } - - _, err = dbWrapper.Exec("SET autocommit=1;") - if err != nil { - global.LOG.Errorf("exec `autocommit=1` failed, err: %v", err) - return err - } - - return nil -} - -func mergeInsert(insertSQLs []string) (string, error) { - if len(insertSQLs) == 0 { - return "", errors.New("no input provided") - } - builder := strings.Builder{} - sql1 := insertSQLs[0] - sql1 = strings.TrimSuffix(sql1, ";") - builder.WriteString(sql1) - for i, insertSQL := range insertSQLs[1:] { - if i < len(insertSQLs)-1 { - builder.WriteString(",") - } - - valuesIdx := strings.Index(insertSQL, "VALUES") - if valuesIdx == -1 { - return "", errors.New("invalid SQL: missing VALUES keyword") - } - sqln := insertSQL[valuesIdx:] - sqln = strings.TrimPrefix(sqln, "VALUES") - sqln = strings.TrimSuffix(sqln, ";") - builder.WriteString(sqln) - - } - builder.WriteString(";") - - return builder.String(), nil -} - -func trim(s string) (string, error) { - s = strings.TrimLeft(s, "\n") - s = strings.TrimSpace(s) - return s, nil -} - -func getDBNameFromDNS(dns string) (string, error) { - ss1 := strings.Split(dns, "/") - if len(ss1) == 2 { - ss2 := strings.Split(ss1[1], "?") - if len(ss2) == 2 { - return ss2[0], nil - } - } - - return "", fmt.Errorf("dns error: %s", dns) -} - -func readLine(r *bufio.Reader) (string, error) { - lineItem, err := r.ReadString('\n') - if err != nil { - if err == io.EOF { - return lineItem, err - } - global.LOG.Errorf("read merge insert sql failed, err: %v", err) - return "", err - } - if strings.HasSuffix(lineItem, ";\n") { - return lineItem, nil - } - lineAppend, err := readLine(r) - if err != nil { - if err == io.EOF { - return lineItem, err - } - global.LOG.Errorf("read merge insert sql failed, err: %v", err) - return "", err - } - - return lineItem + lineAppend, nil -} diff --git a/agent/utils/postgresql/client/remote.go b/agent/utils/postgresql/client/remote.go index 4ffdd57c9..4f4eb83fa 100644 --- a/agent/utils/postgresql/client/remote.go +++ b/agent/utils/postgresql/client/remote.go @@ -8,6 +8,7 @@ import ( "io" "os" "os/exec" + "sort" "strings" "time" @@ -296,7 +297,11 @@ func loadImageTag() (string, error) { return itemTag, nil } - itemTag = "postgres:16.1-alpine" + sort.Strings(versions) + if len(versions) != 0 { + itemTag = versions[len(versions)-1] + } + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) defer cancel() if _, err := client.ImagePull(ctx, itemTag, image.PullOptions{}); err != nil { diff --git a/backend/router/entry_xpack.go b/backend/router/entry_xpack.go new file mode 120000 index 000000000..4dbb3d247 --- /dev/null +++ b/backend/router/entry_xpack.go @@ -0,0 +1 @@ +/usr/songliu/xpack-backend/other/entry_xpack.go \ No newline at end of file diff --git a/backend/server/init_xpack.go b/backend/server/init_xpack.go new file mode 120000 index 000000000..a036f79e4 --- /dev/null +++ b/backend/server/init_xpack.go @@ -0,0 +1 @@ +/usr/songliu/xpack-backend/other/init_xpack.go \ No newline at end of file diff --git a/core/app/dto/command.go b/core/app/dto/command.go index 1369de72e..261624427 100644 --- a/core/app/dto/command.go +++ b/core/app/dto/command.go @@ -2,7 +2,7 @@ package dto type SearchCommandWithPage struct { PageInfo - OrderBy string `json:"orderBy" validate:"required,oneof=name command created_at"` + OrderBy string `json:"orderBy" validate:"required,oneof=name command createdAt"` Order string `json:"order" validate:"required,oneof=null ascending descending"` GroupID uint `json:"groupID"` Type string `josn:"type" validate:"required,oneof=redis command"` diff --git a/core/app/dto/logs.go b/core/app/dto/logs.go index 8831801db..4282b5c77 100644 --- a/core/app/dto/logs.go +++ b/core/app/dto/logs.go @@ -42,7 +42,7 @@ type LoginLog struct { Agent string `json:"agent"` Status string `json:"status"` Message string `json:"message"` - CreatedAt time.Time `json:"createdAt"` + CreatedAt time.Time `json:"created_at"` } type CleanLog struct { diff --git a/core/app/repo/common.go b/core/app/repo/common.go index 4483aab93..627da80dd 100644 --- a/core/app/repo/common.go +++ b/core/app/repo/common.go @@ -62,12 +62,18 @@ func WithByGroupBelong(group string) global.DBOption { } func WithOrderBy(orderStr string) global.DBOption { + if orderStr == "createdAt" { + orderStr = "created_at" + } return func(g *gorm.DB) *gorm.DB { return g.Order(orderStr) } } func WithOrderRuleBy(orderBy, order string) global.DBOption { + if orderBy == "createdAt" { + orderBy = "created_at" + } switch order { case constant.OrderDesc: order = "desc" diff --git a/core/app/service/setting.go b/core/app/service/setting.go index b74133826..113c8b4c6 100644 --- a/core/app/service/setting.go +++ b/core/app/service/setting.go @@ -289,7 +289,7 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) { if err != nil { return nil, err } - case "import": + case "import-paste", "import-local": data, err = loadInfoFromCert() if err != nil { return nil, err diff --git a/core/init/cron/cron.go b/core/init/cron/cron.go index 33e12d03a..357a2fda2 100644 --- a/core/init/cron/cron.go +++ b/core/init/cron/cron.go @@ -10,7 +10,7 @@ import ( ) func Init() { - nyc, _ := time.LoadLocation(common.LoadTimeZone()) + nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) global.Cron = cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger))) _ = service.StartRefreshForToken() diff --git a/core/init/session/psession/psession.go b/core/init/session/psession/psession.go index ba565453c..3bf610649 100644 --- a/core/init/session/psession/psession.go +++ b/core/init/session/psession/psession.go @@ -3,6 +3,10 @@ package psession import ( "encoding/json" "errors" + "log" + "os" + "time" + "github.com/1Panel-dev/1Panel/core/constant" "github.com/gin-gonic/gin" "github.com/glebarez/sqlite" @@ -11,9 +15,6 @@ import ( "github.com/wader/gormstore/v2" "gorm.io/gorm" "gorm.io/gorm/logger" - "log" - "os" - "time" ) type SessionUser struct { @@ -99,6 +100,6 @@ func (p *PSession) Delete(c *gin.Context) error { } func (p *PSession) Clean() error { - p.db.Debug().Table("sessions").Where("1=1").Delete(nil) + p.db.Table("sessions").Where("1=1").Delete(nil) return nil } diff --git a/core/middleware/operation.go b/core/middleware/operation.go index dc46580e2..6838b9222 100644 --- a/core/middleware/operation.go +++ b/core/middleware/operation.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "path" + "reflect" "strings" "time" @@ -92,12 +93,21 @@ func OperationLog() gin.HandlerFunc { } for key, value := range formatMap { if strings.Contains(operationDic.FormatEN, "["+key+"]") { - if arrays, ok := value.([]string); ok { - operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(arrays, ","))) - operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(arrays, ","))) - } else { + t := reflect.TypeOf(value) + if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, "["+key+"]", fmt.Sprintf("[%v]", value)) operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, "["+key+"]", fmt.Sprintf("[%v]", value)) + } else { + val := reflect.ValueOf(value) + length := val.Len() + + var elements []string + for i := 0; i < length; i++ { + element := val.Index(i).Interface().(string) + elements = append(elements, element) + } + operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(elements, ","))) + operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(elements, ","))) } } } diff --git a/core/middleware/password_expired.go b/core/middleware/password_expired.go index 9794e7996..2a2dc9c50 100644 --- a/core/middleware/password_expired.go +++ b/core/middleware/password_expired.go @@ -37,7 +37,7 @@ func PasswordExpired() gin.HandlerFunc { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err) return } - loc, _ := time.LoadLocation(common.LoadTimeZone()) + loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd()) expiredTime, err := time.ParseInLocation(constant.DateTimeLayout, extime.Value, loc) if err != nil { helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err) diff --git a/core/utils/cloud_storage/client/s3.go b/core/utils/cloud_storage/client/s3.go index ed1118c54..285d1248b 100644 --- a/core/utils/cloud_storage/client/s3.go +++ b/core/utils/cloud_storage/client/s3.go @@ -26,12 +26,16 @@ func NewS3Client(vars map[string]interface{}) (*s3Client, error) { if len(scType) == 0 { scType = "Standard" } + mode := loadParamFromVars("mode", vars) + if len(mode) == 0 { + mode = "virtual hosted" + } sess, err := session.NewSession(&aws.Config{ Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""), Endpoint: aws.String(endpoint), Region: aws.String(region), DisableSSL: aws.Bool(true), - S3ForcePathStyle: aws.Bool(false), + S3ForcePathStyle: aws.Bool(mode == "path"), }) if err != nil { return nil, err diff --git a/core/utils/common/common.go b/core/utils/common/common.go index 43026a0e6..2761f355c 100644 --- a/core/utils/common/common.go +++ b/core/utils/common/common.go @@ -34,12 +34,23 @@ func RandStrAndNum(n int) string { return (string(b)) } -func LoadTimeZone() string { - loc := time.Now().Location() - if _, err := time.LoadLocation(loc.String()); err != nil { - return "Asia/Shanghai" +func LoadTimeZoneByCmd() string { + loc := time.Now().Location().String() + if _, err := time.LoadLocation(loc); err != nil { + loc = "Asia/Shanghai" } - return loc.String() + std, err := cmd.Exec("timedatectl | grep 'Time zone'") + if err != nil { + return loc + } + fields := strings.Fields(string(std)) + if len(fields) != 5 { + return loc + } + if _, err := time.LoadLocation(fields[2]); err != nil { + return loc + } + return fields[2] } func ScanPort(port int) bool { diff --git a/frontend/package.json b/frontend/package.json index d911425c0..b7e4d0bf2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,7 +42,7 @@ "highlight.js": "^11.9.0", "js-base64": "^3.7.7", "md-editor-v3": "^2.11.3", - "monaco-editor": "^0.34.1", + "monaco-editor": "^0.50.0", "nprogress": "^0.2.0", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^1.6.1", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 85784cd93..a95d3595c 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -8,6 +8,7 @@ import { reactive, computed, ref, nextTick, provide } from 'vue'; import { GlobalStore } from '@/store'; import zhCn from 'element-plus/es/locale/lang/zh-cn'; +import zhTw from 'element-plus/es/locale/lang/zh-tw'; import en from 'element-plus/es/locale/lang/en'; import { useTheme } from '@/global/use-theme'; useTheme(); @@ -19,6 +20,7 @@ const config = reactive({ const i18nLocale = computed(() => { if (globalStore.language === 'zh') return zhCn; + if (globalStore.language === 'tw') return zhTw; if (globalStore.language === 'en') return en; return zhCn; }); diff --git a/frontend/src/api/interface/backup.ts b/frontend/src/api/interface/backup.ts index ed77d1742..dd9783580 100644 --- a/frontend/src/api/interface/backup.ts +++ b/frontend/src/api/interface/backup.ts @@ -19,6 +19,7 @@ export namespace Backup { credential: string; rememberAuth: boolean; backupPath: string; + bucketInput: boolean; vars: string; varsJson: object; createdAt: Date; diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index de028d779..0f564a7ca 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -268,6 +268,8 @@ export namespace Container { path: string; containers: Array; expand: boolean; + envStr: string; + env: Array; } export interface ComposeContainer { name: string; diff --git a/frontend/src/components/app-status/index.vue b/frontend/src/components/app-status/index.vue index a23190330..31ad2a7ff 100644 --- a/frontend/src/components/app-status/index.vue +++ b/frontend/src/components/app-status/index.vue @@ -2,67 +2,68 @@
-
-
+
+
{{ data.app }} + + {{ $t('app.version') }}{{ $t('commons.colon') }}{{ data.version }}
-
- -
-
- {{ $t('app.version') }}:{{ data.version }} -
-
- - - {{ $t('app.start') }} - - - {{ $t('app.stop') }} - - - - {{ $t('app.restart') }} - - - - {{ $t('app.reload') }} - - - - {{ $t('commons.button.set') }} - - - - {{ $t('nginx.clearProxyCache') }} - - + +
+ + {{ $t('app.start') }} + + + {{ $t('app.stop') }} + + + + {{ $t('app.restart') }} + + + + {{ $t('app.reload') }} + + + + {{ $t('commons.button.set') }} + + + + {{ $t('nginx.clearProxyCache') }} +
@@ -74,20 +75,40 @@
+
+ + + +
+ diff --git a/frontend/src/components/compose-log/index.vue b/frontend/src/components/compose-log/index.vue index 0d6a87ca9..680124357 100644 --- a/frontend/src/components/compose-log/index.vue +++ b/frontend/src/components/compose-log/index.vue @@ -79,15 +79,14 @@ const logSearch = reactive({ const handleClose = () => { logSocket.value?.send('close conn'); open.value = false; + globalStore.isFullScreen = false; }; function toggleFullscreen() { - if (screenfull.isEnabled) { - screenfull.toggle(); - } + globalStore.isFullScreen = !globalStore.isFullScreen; } const loadTooltip = () => { - return i18n.global.t('commons.button.' + (screenfull.isFullscreen ? 'quitFullscreen' : 'fullscreen')); + return i18n.global.t('commons.button.' + (globalStore.isFullScreen ? 'quitFullscreen' : 'fullscreen')); }; watch(logVisible, (val) => { @@ -204,6 +203,9 @@ defineExpose({ diff --git a/frontend/src/components/upload/index.vue b/frontend/src/components/upload/index.vue index b3f41f9c2..e87dd6880 100644 --- a/frontend/src/components/upload/index.vue +++ b/frontend/src/components/upload/index.vue @@ -15,7 +15,15 @@
- +
{{ $t('database.dropHelper') }} @@ -46,7 +54,7 @@
- + {{ $t('commons.button.upload') }} @@ -57,7 +65,7 @@ v-model:selects="selects" :data="data" > -