diff --git a/agent/app/api/v2/file.go b/agent/app/api/v2/file.go index 0d36292fe..179e065d8 100644 --- a/agent/app/api/v2/file.go +++ b/agent/app/api/v2/file.go @@ -11,6 +11,7 @@ import ( "path/filepath" "strconv" "strings" + "syscall" "github.com/1Panel-dev/1Panel/agent/app/api/v2/helper" "github.com/1Panel-dev/1Panel/agent/app/dto" @@ -338,7 +339,11 @@ func (b *BaseApi) UploadFiles(c *gin.Context) { mode := info.Mode() fileOp := files.NewFileOp() - + stat, ok := info.Sys().(*syscall.Stat_t) + uid, gid := -1, -1 + if ok { + uid, gid = int(stat.Uid), int(stat.Gid) + } success := 0 failures := make(buserr.MultiErr) for _, file := range uploadFiles { @@ -351,6 +356,7 @@ func (b *BaseApi) UploadFiles(c *gin.Context) { global.LOG.Error(e) continue } + _ = os.Chown(dstDir, uid, gid) } tmpFilename := dstFilename + ".tmp" if err := c.SaveUploadedFile(file, tmpFilename); err != nil { @@ -378,6 +384,9 @@ func (b *BaseApi) UploadFiles(c *gin.Context) { } else { _ = os.Chmod(dstFilename, mode) } + if uid != -1 && gid != -1 { + _ = os.Chown(dstFilename, uid, gid) + } success++ } if success == 0 { diff --git a/agent/app/dto/backup.go b/agent/app/dto/backup.go index 555e65fa1..6170f2de3 100644 --- a/agent/app/dto/backup.go +++ b/agent/app/dto/backup.go @@ -10,6 +10,7 @@ type CommonBackup struct { DetailName string `json:"detailName"` Secret string `json:"secret"` TaskID string `json:"taskID"` + FileName string `json:"fileName"` } type CommonRecover struct { DownloadAccountID uint `json:"downloadAccountID" validate:"required"` diff --git a/agent/app/dto/response/file.go b/agent/app/dto/response/file.go index 56c17bac6..b087f81ba 100644 --- a/agent/app/dto/response/file.go +++ b/agent/app/dto/response/file.go @@ -36,10 +36,11 @@ type FileWgetRes struct { } type FileLineContent struct { - Content string `json:"content"` - End bool `json:"end"` - Path string `json:"path"` - Total int `json:"total"` + Content string `json:"content"` + End bool `json:"end"` + Path string `json:"path"` + Total int `json:"total"` + Lines []string `json:"lines"` } type FileExist struct { diff --git a/agent/app/repo/backup.go b/agent/app/repo/backup.go index 695e3a741..e01a3f40a 100644 --- a/agent/app/repo/backup.go +++ b/agent/app/repo/backup.go @@ -16,8 +16,10 @@ type IBackupRepo interface { CreateRecord(record *model.BackupRecord) error DeleteRecord(ctx context.Context, opts ...DBOption) error UpdateRecord(record *model.BackupRecord) error + WithByDetailName(detailName string) DBOption WithByFileName(fileName string) DBOption WithByCronID(cronjobID uint) DBOption + WithFileNameStartWith(filePrefix string) DBOption } func NewIBackupRepo() IBackupRepo { @@ -55,6 +57,21 @@ func (u *BackupRepo) WithByFileName(fileName string) DBOption { } } +func (u *BackupRepo) WithByDetailName(detailName string) DBOption { + return func(g *gorm.DB) *gorm.DB { + if len(detailName) == 0 { + return g + } + return g.Where("detail_name = ?", detailName) + } +} + +func (u *BackupRepo) WithFileNameStartWith(filePrefix string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("file_name LIKE ?", filePrefix+"%") + } +} + func (u *BackupRepo) CreateRecord(record *model.BackupRecord) error { return global.DB.Create(record).Error } diff --git a/agent/app/service/app.go b/agent/app/service/app.go index bad1caa68..398f48f76 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -161,6 +161,9 @@ func (a AppService) GetAppTags() ([]response.TagDTO, error) { func (a AppService) GetApp(key string) (*response.AppDTO, error) { var appDTO response.AppDTO + if key == "postgres" { + key = "postgresql" + } app, err := appRepo.GetFirst(appRepo.WithKey(key)) if err != nil { return nil, err diff --git a/agent/app/service/app_install.go b/agent/app/service/app_install.go index 4cf9dc26f..cf354aaae 100644 --- a/agent/app/service/app_install.go +++ b/agent/app/service/app_install.go @@ -571,6 +571,12 @@ func (a *AppInstallService) GetUpdateVersions(req request.AppUpdateVersion) ([]d if err != nil { return versions, err } + if app.Key == constant.AppMysql { + majorVersion := getMajorVersion(install.Version) + if !strings.HasPrefix(detail.Version, majorVersion) { + continue + } + } versions = append(versions, dto.AppVersion{ Version: detail.Version, DetailId: detail.ID, @@ -740,7 +746,24 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) { } } } + } else if form.Type == "apps" { + if m, ok := form.Child.(map[string]interface{}); ok { + result := make(map[string]string) + for key, value := range m { + if strVal, ok := value.(string); ok { + result[key] = strVal + } + } + if envKey, ok := result["envKey"]; ok { + serviceName := envs[envKey] + if serviceName != nil { + appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(serviceName.(string))) + appParam.ShowValue = appInstall.Name + } + } + } } + params = append(params, appParam) } else { params = append(params, response.AppParam{ diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 2eee09a64..4a274290d 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -199,17 +199,18 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI } case constant.AppRedis: if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok { - if password != "" { - authParam := dto.RedisAuthParam{ - RootPassword: password.(string), - } - authByte, err := json.Marshal(authParam) - if err != nil { - return err - } - appInstall.Param = string(authByte) + authParam := dto.RedisAuthParam{ + RootPassword: "", } - database.Password = password.(string) + if password != "" { + authParam.RootPassword = password.(string) + database.Password = password.(string) + } + authByte, err := json.Marshal(authParam) + if err != nil { + return err + } + appInstall.Param = string(authByte) } } return databaseRepo.Create(ctx, database) @@ -558,11 +559,23 @@ func upgradeInstall(req request.AppInstallUpgrade) error { ) backUpApp := func(t *task.Task) error { if req.Backup { - backupRecord, err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name}) - if err != nil { + backupService := NewIBackupService() + fileName := fmt.Sprintf("upgrade_backup_%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)) + backupRecord, err := backupService.AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name, FileName: fileName}) + if err == nil { + backups, _ := backupService.ListAppRecords(install.App.Key, install.Name, "upgrade_backup") + if len(backups) > 3 { + backupsToDelete := backups[:len(backups)-3] + var deleteIDs []uint + for _, backup := range backupsToDelete { + deleteIDs = append(deleteIDs, backup.ID) + } + _ = backupService.BatchDeleteRecord(deleteIDs) + } + backupFile = path.Join(global.CONF.System.Backup, backupRecord.FileDir, backupRecord.FileName) + } else { return buserr.WithNameAndErr("ErrAppBackup", install.Name, err) } - backupFile = path.Join(global.CONF.System.Backup, backupRecord.FileDir, backupRecord.FileName) } return nil } @@ -1390,7 +1403,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool) } for _, installed := range appInstallList { - if updated && (installed.App.Type == "php" || installed.Status == constant.Installing || (installed.App.Key == constant.AppMysql && installed.Version == "5.6.51")) { + if updated && ignoreUpdate(installed) { continue } if sync && !doNotNeedSync(installed) { @@ -1691,3 +1704,28 @@ func isHostModel(dockerCompose string) bool { } return false } + +func getMajorVersion(version string) string { + parts := strings.Split(version, ".") + if len(parts) >= 2 { + return parts[0] + "." + parts[1] + } + return version +} + +func ignoreUpdate(installed model.AppInstall) bool { + if installed.App.Type == "php" || installed.Status == constant.Installing { + return true + } + if installed.App.Key == constant.AppMysql { + majorVersion := getMajorVersion(installed.Version) + appDetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID)) + for _, appDetail := range appDetails { + if strings.HasPrefix(appDetail.Version, majorVersion) && common.CompareVersion(appDetail.Version, installed.Version) { + return false + } + } + return true + } + return false +} diff --git a/agent/app/service/backup.go b/agent/app/service/backup.go index af3188555..161241dc5 100644 --- a/agent/app/service/backup.go +++ b/agent/app/service/backup.go @@ -36,6 +36,7 @@ type IBackupService interface { DownloadRecord(info dto.DownloadRecord) (string, error) DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error BatchDeleteRecord(ids []uint) error + ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error) ListFiles(req dto.OperateByID) []string @@ -194,6 +195,20 @@ func (u *BackupService) BatchDeleteRecord(ids []uint) error { return backupRepo.DeleteRecord(context.Background(), commonRepo.WithByIDs(ids)) } +func (u *BackupService) ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error) { + records, err := backupRepo.ListRecord( + commonRepo.WithOrderBy("created_at asc"), + commonRepo.WithByName(name), + commonRepo.WithByType("app"), + backupRepo.WithFileNameStartWith(fileName), + backupRepo.WithByDetailName(detailName), + ) + if err != nil { + return nil, err + } + return records, err +} + func (u *BackupService) ListFiles(req dto.OperateByID) []string { var datas []string account, client, err := NewBackupClientWithID(req.ID) diff --git a/agent/app/service/backup_app.go b/agent/app/service/backup_app.go index e2a35add8..284ab865d 100644 --- a/agent/app/service/backup_app.go +++ b/agent/app/service/backup_app.go @@ -38,7 +38,10 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, er itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName) backupDir := path.Join(global.CONF.System.Backup, itemDir) - fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5)) + fileName := req.FileName + if req.FileName == "" { + fileName = fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5)) + } backupApp := func() (*model.BackupRecord, error) { if err = handleAppBackup(&install, nil, backupDir, fileName, "", req.Secret, req.TaskID); err != nil { diff --git a/agent/app/service/container.go b/agent/app/service/container.go index 35730354d..f5d754b8e 100644 --- a/agent/app/service/container.go +++ b/agent/app/service/container.go @@ -1087,6 +1087,29 @@ func calculateNetwork(network map[string]container.NetworkStats) (float64, float } func checkImageExist(client *client.Client, imageItem string) bool { + if client == nil { + var err error + client, err = docker.NewDockerClient() + if err != nil { + return false + } + } + images, err := client.ImageList(context.Background(), image.ListOptions{}) + if err != nil { + return false + } + + for _, img := range images { + for _, tag := range img.RepoTags { + if tag == imageItem || tag == imageItem+":latest" { + return true + } + } + } + return false +} + +func checkImage(client *client.Client, imageItem string) bool { images, err := client.ImageList(context.Background(), image.ListOptions{}) if err != nil { return false diff --git a/agent/app/service/file.go b/agent/app/service/file.go index 32e0eb414..ef4f6e541 100644 --- a/agent/app/service/file.go +++ b/agent/app/service/file.go @@ -467,11 +467,19 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi if err != nil { return nil, err } + if req.Latest && req.Page == 1 && len(lines) < 1000 && total > 1 { + preLines, _, _, err := files.ReadFileByLine(logFilePath, total-1, req.PageSize, false) + if err != nil { + return nil, err + } + lines = append(preLines, lines...) + } res := &response.FileLineContent{ Content: strings.Join(lines, "\n"), End: isEndOfFile, Path: logFilePath, Total: total, + Lines: lines, } return res, nil } diff --git a/agent/app/service/website.go b/agent/app/service/website.go index b512aad95..867ec1075 100644 --- a/agent/app/service/website.go +++ b/agent/app/service/website.go @@ -378,6 +378,9 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error) switch runtime.Type { case constant.RuntimePHP: if runtime.Resource == constant.ResourceAppstore { + if !checkImageExist(nil, runtime.Image) { + return buserr.WithName("ErrImageNotExist", runtime.Name) + } website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port) } else { website.ProxyType = create.ProxyType @@ -1085,6 +1088,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH websiteSSL.Provider = constant.Manual websiteSSL.PrivateKey = privateKey websiteSSL.Pem = certificate + websiteSSL.Status = constant.SSLReady res.SSL = websiteSSL } diff --git a/agent/cmd/server/nginx_conf/proxy.conf b/agent/cmd/server/nginx_conf/proxy.conf index d1914fb69..4bb43e706 100644 --- a/agent/cmd/server/nginx_conf/proxy.conf +++ b/agent/cmd/server/nginx_conf/proxy.conf @@ -5,9 +5,11 @@ location ^~ /test { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; + proxy_set_header Connection $http_connection; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; add_header X-Cache $upstream_cache_status; + add_header Cache-Control no-cache; + proxy_ssl_server_name off; } diff --git a/agent/i18n/lang/en.yaml b/agent/i18n/lang/en.yaml index 405639841..ea2f1583b 100644 --- a/agent/i18n/lang/en.yaml +++ b/agent/i18n/lang/en.yaml @@ -104,6 +104,7 @@ ErrDomainFormat: "{{ .name }} domain format error" ErrDefaultAlias: "default is a reserved code name, please use another code name" ErrParentWebsite: "You need to delete the subsite(s) {{ .name }} first" ErrBuildDirNotFound: "Build directory does not exist" +ErrImageNotExist: "Running environment {{.name}} image does not exist, please re-edit the running environment" #ssl ErrSSLCannotDelete: "The certificate {{ .name }} is being used by the website and cannot be removed" diff --git a/agent/i18n/lang/zh-Hant.yaml b/agent/i18n/lang/zh-Hant.yaml index e22b1547d..663abc191 100644 --- a/agent/i18n/lang/zh-Hant.yaml +++ b/agent/i18n/lang/zh-Hant.yaml @@ -104,6 +104,7 @@ ErrDomainFormat: "{{ .name }} 域名格式不正確" ErrDefaultAlias: "default 為保留代號,請使用其他代號" ErrParentWebsite: "需要先刪除子網站 {{ .name }}" ErrBuildDirNotFound: "編譯目錄不存在" +ErrImageNotExist: "執行環境 {{.name}} 鏡像不存在,請重新編輯執行環境" #ssl ErrSSLCannotDelete: "{{ .name }} 證書正在被網站使用,無法刪除" diff --git a/agent/i18n/lang/zh.yaml b/agent/i18n/lang/zh.yaml index 29674e043..719b65f0a 100644 --- a/agent/i18n/lang/zh.yaml +++ b/agent/i18n/lang/zh.yaml @@ -103,6 +103,7 @@ ErrDomainFormat: "{{ .name }} 域名格式不正确" ErrDefaultAlias: "default 为保留代号,请使用其他代号" ErrParentWebsite: "需要先删除子网站 {{ .name }}" ErrBuildDirNotFound: "构建目录不存在" +ErrImageNotExist: "运行环境 {{.name}} 镜像不存在,请重新编辑运行环境" #ssl ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除" diff --git a/agent/utils/ssl/client.go b/agent/utils/ssl/client.go index 2d4515e64..38199ccc2 100644 --- a/agent/utils/ssl/client.go +++ b/agent/utils/ssl/client.go @@ -4,6 +4,7 @@ import ( "crypto" "encoding/json" "os" + "strings" "time" "github.com/1Panel-dev/1Panel/agent/app/model" @@ -296,8 +297,12 @@ func (c *AcmeClient) GetDNSResolve(domains []string) (map[string]Resolve, error) continue } challengeInfo := dns01.GetChallengeInfo(domain, keyAuth) + fqdn := challengeInfo.FQDN + if strings.HasPrefix(domain, "*.") && strings.Contains(fqdn, "*.") { + fqdn = strings.Replace(fqdn, "*.", "", 1) + } resolves[domain] = Resolve{ - Key: challengeInfo.FQDN, + Key: fqdn, Value: challengeInfo.Value, } } diff --git a/frontend/src/components/log-file/index.vue b/frontend/src/components/log-file/index.vue index 1095a5f9e..3e21542c2 100644 --- a/frontend/src/components/log-file/index.vue +++ b/frontend/src/components/log-file/index.vue @@ -1,5 +1,5 @@