package service import ( "encoding/json" "fmt" "net/http" "os" "path" "strconv" "strings" "github.com/1Panel-dev/1Panel/core/app/dto" "github.com/1Panel-dev/1Panel/core/app/model" "github.com/1Panel-dev/1Panel/core/app/repo" "github.com/1Panel-dev/1Panel/core/app/task" "github.com/1Panel-dev/1Panel/core/buserr" "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" "github.com/1Panel-dev/1Panel/core/i18n" "github.com/1Panel-dev/1Panel/core/utils/common" "github.com/1Panel-dev/1Panel/core/utils/files" "github.com/1Panel-dev/1Panel/core/utils/req_helper" "github.com/gin-gonic/gin" "github.com/jinzhu/copier" "gopkg.in/yaml.v2" ) type ScriptService struct{} type IScriptService interface { Search(ctx *gin.Context, req dto.SearchPageWithGroup) (int64, interface{}, error) Create(req dto.ScriptOperate) error Update(req dto.ScriptOperate) error Delete(ids dto.OperateByIDs) error Sync() error } func NewIScriptService() IScriptService { return &ScriptService{} } func (u *ScriptService) Search(ctx *gin.Context, req dto.SearchPageWithGroup) (int64, interface{}, error) { options := []global.DBOption{repo.WithOrderBy("created_at desc")} if len(req.Info) != 0 { options = append(options, scriptRepo.WithByInfo(req.Info)) } list, err := scriptRepo.GetList(options...) if err != nil { return 0, nil, err } groups, _ := groupRepo.GetList(repo.WithByType("script")) groupMap := make(map[uint]string) for _, item := range groups { groupMap[item.ID] = item.Name } var data []dto.ScriptInfo for _, itemData := range list { var item dto.ScriptInfo if err := copier.Copy(&item, &itemData); err != nil { global.LOG.Errorf("copy backup account to dto backup info failed, err: %v", err) } if item.IsSystem { lang := strings.ToLower(common.GetLang(ctx)) var nameMap = make(map[string]string) _ = json.Unmarshal([]byte(item.Name), &nameMap) var descriptionMap = make(map[string]string) _ = json.Unmarshal([]byte(item.Description), &descriptionMap) if val, ok := nameMap[lang]; ok { item.Name = val } if val, ok := descriptionMap[lang]; ok { item.Description = val } } matchGroup := false groupIDs := strings.Split(itemData.Groups, ",") for _, idItem := range groupIDs { id, _ := strconv.Atoi(idItem) if id == 0 { continue } if uint(id) == req.GroupID { matchGroup = true } item.GroupList = append(item.GroupList, uint(id)) item.GroupBelong = append(item.GroupBelong, groupMap[uint(id)]) } if req.GroupID == 0 { data = append(data, item) continue } if matchGroup { data = append(data, item) } } var records []dto.ScriptInfo total, start, end := len(data), (req.Page-1)*req.PageSize, req.Page*req.PageSize if start > total { records = make([]dto.ScriptInfo, 0) } else { if end >= total { end = total } records = data[start:end] } return int64(total), records, nil } func (u *ScriptService) Create(req dto.ScriptOperate) error { itemData, _ := scriptRepo.Get(repo.WithByName(req.Name)) if itemData.ID != 0 { return buserr.New("ErrRecordExist") } if err := copier.Copy(&itemData, &req); err != nil { return buserr.WithDetail("ErrStructTransform", err.Error(), nil) } if err := scriptRepo.Create(&itemData); err != nil { return err } return nil } func (u *ScriptService) Delete(req dto.OperateByIDs) error { for _, item := range req.IDs { scriptItem, _ := scriptRepo.Get(repo.WithByID(item)) if scriptItem.ID == 0 || scriptItem.IsSystem { continue } if err := scriptRepo.Delete(repo.WithByID(item)); err != nil { return err } } return nil } func (u *ScriptService) Update(req dto.ScriptOperate) error { itemData, _ := scriptRepo.Get(repo.WithByID(req.ID)) if itemData.ID == 0 { return buserr.New("ErrRecordNotFound") } updateMap := make(map[string]interface{}) updateMap["name"] = req.Name updateMap["script"] = req.Script updateMap["groups"] = req.Groups updateMap["description"] = req.Description if err := scriptRepo.Update(req.ID, updateMap); err != nil { return err } return nil } func LoadScriptInfo(id uint) (model.ScriptLibrary, error) { return scriptRepo.Get(repo.WithByID(id)) } func (u *ScriptService) Sync() error { syncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("ScriptLibrary"), task.TaskSync, task.TaskScopeScript, "", 0) if err != nil { global.LOG.Errorf("create sync task failed %v", err) return err } syncTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey("ScriptLibrary"), task.TaskSync, task.TaskScopeScript), func(t *task.Task) (err error) { versionUrl := fmt.Sprintf("%s/scripts/version.txt", global.CONF.RemoteURL.ResourceURL) _, versionRes, err := req_helper.HandleRequest(versionUrl, http.MethodGet, constant.TimeOut20s) if err != nil { return fmt.Errorf("load scripts version from remote failed, err: %v", err) } var scriptSetting model.Setting _ = global.DB.Where("key = ?", "ScriptVersion").First(&scriptSetting).Error if scriptSetting.Value == string(versionRes) { syncTask.Log("The local and remote versions are detected to be consistent. Skip...") return nil } syncTask.Log("start to download data.yaml") dataUrl := fmt.Sprintf("%s/scripts/data.yaml", global.CONF.RemoteURL.ResourceURL) _, dataRes, err := req_helper.HandleRequest(dataUrl, http.MethodGet, constant.TimeOut20s) if err != nil { return fmt.Errorf("load scripts data.yaml from remote failed, err: %v", err) } syncTask.Log("download successful!") var scripts Scripts if err = yaml.Unmarshal(dataRes, &scripts); err != nil { return fmt.Errorf("the format of data.yaml is err: %v", err) } syncTask.Log("start to download scripts.tar.gz") tmpDir := path.Join(global.CONF.Base.InstallDir, "1panel/tmp/script") if _, err := os.Stat(tmpDir); err != nil { _ = os.MkdirAll(tmpDir, 0755) } scriptsUrl := fmt.Sprintf("%s/scripts/scripts.tar.gz", global.CONF.RemoteURL.ResourceURL) if err := files.DownloadFile(scriptsUrl, tmpDir+"/scripts.tar.gz"); err != nil { return fmt.Errorf("download scripts.tar.gz failed, err: %v", err) } syncTask.Log("download successful! now start to decompress...") if err := files.HandleUnTar(tmpDir+"/scripts.tar.gz", tmpDir, ""); err != nil { return fmt.Errorf("handle decompress scripts.tar.gz failed, err: %v", err) } var scriptsForDB []model.ScriptLibrary for _, item := range scripts.Scripts.Sh { itemName, _ := json.Marshal(item.Name) itemDescription, _ := json.Marshal(item.Description) shell, _ := os.ReadFile(fmt.Sprintf("%s/scripts/sh/%s.sh", tmpDir, item.Key)) scriptItem := model.ScriptLibrary{ Name: string(itemName), IsSystem: true, Script: string(shell), Description: string(itemDescription), } scriptsForDB = append(scriptsForDB, scriptItem) } syncTask.Log("analytic completion! now start to refresh database...") if err := scriptRepo.SyncAll(scriptsForDB); err != nil { return fmt.Errorf("sync script with db failed, err: %v", err) } _ = os.RemoveAll(tmpDir) if err := global.DB.Model(&model.Setting{}).Where("key = ?", "ScriptVersion").Updates(map[string]interface{}{"value": string(versionRes)}).Error; err != nil { return fmt.Errorf("update script version in db failed, err: %v", err) } return nil }, nil) if err := syncTask.Execute(); err != nil { return fmt.Errorf("sync scripts from remote failed, err: %v", err) } return nil } type Scripts struct { Scripts ScriptDetail `json:"scripts"` } type ScriptDetail struct { Sh []ScriptHelper `json:"sh"` } type ScriptHelper struct { Key string `json:"key"` Sort uint `json:"sort"` Groups string `json:"groups"` Name map[string]string `json:"name"` Description map[string]string `json:"description"` }