diff --git a/api/project/v1/project.go b/api/project/v1/project.go index 6efc5e2..cdd4852 100644 --- a/api/project/v1/project.go +++ b/api/project/v1/project.go @@ -82,3 +82,22 @@ type UploadReq struct { type UploadRes struct { FileUrl string `json:"fileurl"` } + +// DeployReq 项目部署 +type DeployReq struct { + g.Meta `path:"/project/deploy" method:"post" tags:"项目" summary:"项目部署"` + adminin.ProjectDeployInp +} + +type DeployRes struct { + FileUrl string `json:"fileurl"` +} + +type DeployListReq struct { + g.Meta `path:"/project/deploy/list" method:"get" tags:"项目" summary:"获取项目列表"` + adminin.ProjectDeployListInp +} + +type DeployListRes struct { + List []*adminin.ProjectDeployListModel `json:"list" dc:"数据列表"` +} diff --git a/go.mod b/go.mod index 2045595..e172440 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,12 @@ module hotgo go 1.21 require ( + github.com/bndr/gojenkins v1.1.0 github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.3 github.com/gogf/gf/contrib/nosql/redis/v2 v2.7.2 github.com/gogf/gf/v2 v2.7.2 github.com/golang-jwt/jwt/v5 v5.2.0 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 ) require ( diff --git a/go.sum b/go.sum index 446918b..89df3a0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/bndr/gojenkins v1.1.0 h1:TWyJI6ST1qDAfH33DQb3G4mD8KkrBfyfSUoZBHQAvPI= +github.com/bndr/gojenkins v1.1.0/go.mod h1:QeskxN9F/Csz0XV/01IC8y37CapKKWvOHa0UHLLX1fM= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -9,6 +11,7 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= @@ -71,6 +74,8 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= @@ -81,12 +86,18 @@ go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucg go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -94,5 +105,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index fe4daf7..e385513 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -47,6 +47,12 @@ var ( project.NewV1(), ) }) + s.Group("/attachment", func(group *ghttp.RouterGroup) { + group.Hook("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) { + r.Response.CORSDefault() + }) + + }) s.Run() return nil }, diff --git a/internal/controller/project/project_v1_delete.go b/internal/controller/project/project_v1_delete.go index 8367660..1e24b25 100644 --- a/internal/controller/project/project_v1_delete.go +++ b/internal/controller/project/project_v1_delete.go @@ -2,13 +2,12 @@ package project import ( "context" - - "github.com/gogf/gf/v2/errors/gcode" - "github.com/gogf/gf/v2/errors/gerror" + "hotgo/internal/service" "hotgo/api/project/v1" ) func (c *ControllerV1) Delete(ctx context.Context, req *v1.DeleteReq) (res *v1.DeleteRes, err error) { - return nil, gerror.NewCode(gcode.CodeNotImplemented) + err = service.AdminProject().Delete(ctx, &req.ProjectDeleteInp) + return } diff --git a/internal/controller/project/project_v1_deploy.go b/internal/controller/project/project_v1_deploy.go new file mode 100644 index 0000000..6dbbd6d --- /dev/null +++ b/internal/controller/project/project_v1_deploy.go @@ -0,0 +1,17 @@ +package project + +import ( + "context" + "hotgo/api/project/v1" + "hotgo/internal/service" +) + +func (c *ControllerV1) Deploy(ctx context.Context, req *v1.DeployReq) (res *v1.DeployRes, err error) { + err = service.AdminProject().Deploy(ctx, &req.ProjectDeployInp) + return +} + +func (c *ControllerV1) DeployList(ctx context.Context, req *v1.DeployListReq) (res *v1.DeployListRes, err error) { + err = service.AdminProject().Deploy(ctx, &req.ProjectDeployInp) + return +} diff --git a/internal/logic/admin/project.go b/internal/logic/admin/project.go index fcf159a..ade96fd 100644 --- a/internal/logic/admin/project.go +++ b/internal/logic/admin/project.go @@ -5,18 +5,30 @@ package admin import ( "context" + "encoding/json" "fmt" + "github.com/bndr/gojenkins" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/grand" + "github.com/gogf/gf/v2/util/guid" "hotgo/internal/dao" "hotgo/internal/library/contexts" + "hotgo/internal/model" "hotgo/internal/model/entity" "hotgo/internal/model/input/adminin" "hotgo/internal/service" + "hotgo/internal/utils" + "io" + "net/http" + "os" + "path/filepath" + "strings" ) type sAdminProject struct{} @@ -29,6 +41,21 @@ func init() { service.RegisterAdminProject(NewAdminProject()) } +const ( + DEPLOY_BUILD_KEY = "deploy.build" + DEPLOY_BUILD_URL_KEY = DEPLOY_BUILD_KEY + "." + "url" + DEPLOY_BUILD_JOBNAME_KEY = DEPLOY_BUILD_KEY + "." + "jobName" + DEPLOY_BUILD_JOBDOWN_KEY = DEPLOY_BUILD_KEY + "." + "jobdown" + DEPLOY_BUILD_CALLBACK_KEY = DEPLOY_BUILD_KEY + "." + "callback" + DEPLOY_BUILD_MINIOPATH_KEY = DEPLOY_BUILD_KEY + "." + "ossPathPre" + DEPLOY_BUILD_UPLOAD_KEY = DEPLOY_BUILD_KEY + "." + "upload" + DEPLOY_BUILD_CONFIG_PATH = DEPLOY_BUILD_KEY + "." + "confPath" + DEPLOY_BUILD_FRONT_PATH = DEPLOY_BUILD_KEY + "." + "frontPath" + DEPLOY_BUILD_JOBTOKEN_KEY = DEPLOY_BUILD_KEY + "." + "jobToken" + DEPLOY_BUILD_JOBUSER_KEY = DEPLOY_BUILD_KEY + "." + "jobUser" + DEPLOY_BUILD_JOBPASSWORD_KEY = DEPLOY_BUILD_KEY + "." + "jobPassword" +) + // Model ORM模型 func (s *sAdminProject) Model(ctx context.Context) *gdb.Model { return dao.AdminProject.Ctx(ctx).Handler(func(m *gdb.Model) *gdb.Model { @@ -66,6 +93,12 @@ func (s *sAdminProject) Delete(ctx context.Context, in *adminin.ProjectDeleteInp err = gerror.New("没有找到项目!") return err } + var project *entity.AdminProject + err = s.Model(ctx).WherePri(in.Id).Scan(&project) + if project.Status == 1 { + err = gerror.New("项目已发布,无法删除!") + return err + } _, err = s.Model(ctx).WherePri(in.Id).Delete() return } @@ -165,3 +198,193 @@ func (s *sAdminProject) ExistById(ctx context.Context, id int64) (exists bool, e exists = count > 0 return } + +func (s *sAdminProject) Deploy(ctx context.Context, in *adminin.ProjectDeployInp) (err error) { + var tempName = guid.S() + var basePath = "resource/tmp/" + tempName + var project *entity.AdminProject + var jsonObj *model.JsonObj + err = s.Model(ctx).WherePri(in.Id).Scan(&project) + if project == nil { + err = gerror.New("项目不存在") + return + } + gconv.Struct(project.Content, &jsonObj) + fmt.Println(jsonObj.EditCanvasConfig.BackgroundImage) + if jsonObj.EditCanvasConfig.BackgroundImage != "" { + backimageUrl := gconv.String(jsonObj.EditCanvasConfig.BackgroundImage) + urlarr := strings.Split(backimageUrl, "?") + filename := filepath.Base(urlarr[0]) + DownloadImage(ctx, urlarr[0], basePath+"/data/image/") + jsonObj.EditCanvasConfig.BackgroundImage = "/data/image/" + filename + "?" + urlarr[1] + } + + componentList := jsonObj.ComponentList + for _, component := range componentList { + if component.Key == "Image" && component.Option.Dataset != nil { + fmt.Println("Image") + fmt.Println(component.Option.Dataset) + fileUrl := gconv.String(component.Option.Dataset) + filename := filepath.Base(fileUrl) + DownloadImage(ctx, fileUrl, basePath+"/data/image/") + component.Option.Dataset = "/data/image/" + filename + } + if component.Key == "Video" && component.Option.Dataset != nil { + fmt.Println("Video") + fmt.Println(component.Option.Dataset) + fileUrl := gconv.String(component.Option.Dataset) + filename := filepath.Base(fileUrl) + DownloadImage(ctx, fileUrl, basePath+"/data/video/") + component.Option.Dataset = "/data/video/" + filename + } + if component.Key == "ImageCarousel" && component.Option.Dataset != nil { + fmt.Println("ImageCarousel") + fmt.Println(component.Option.Dataset) + var datalist = gconv.SliceStr(component.Option.Dataset) + for i, fileUrl := range datalist { + filename := filepath.Base(fileUrl) + DownloadImage(ctx, fileUrl, basePath+"/data/image/") + datalist[i] = "/data/image/" + filename + } + component.Option.Dataset = datalist + } + + } + jsonstr, err := json.Marshal(jsonObj) + os.WriteFile(basePath+"/project.json", jsonstr, 0666) + + err = utils.ZipCompress(basePath, basePath+".zip") + if err != nil { + return err + } + g.Log().Debug(ctx, "压缩文件完成") + s.BuildTask(ctx, in.Url, basePath+".zip", in.Id) + //serverFile := basePath + tempName + ".zip" + return nil +} + +func DownloadImage(ctx context.Context, url string, path string) error { + // 发送 GET 请求获取图片 + resp, err := http.Get(url) + if err != nil { + g.Log().Error(ctx, err) + return fmt.Errorf("failed to fetch the image: %w", err) + } + defer resp.Body.Close() + + // 检查响应状态码 + if resp.StatusCode != http.StatusOK { + g.Log().Error(ctx, err) + return fmt.Errorf("request returned status code %d", resp.StatusCode) + } + + // 获取图片扩展名 + filename := filepath.Base(url) + // 确保目录存在 + err = os.MkdirAll(path, os.ModePerm) + if err != nil { + g.Log().Error(ctx, err) + return fmt.Errorf("failed to create directory: %w", err) + } + // 打开或创建文件 + file, err := os.Create(path + filename) + if err != nil { + g.Log().Error(ctx, err) + return fmt.Errorf("failed to create file: %w", err) + } + defer file.Close() + + // 将图片数据写入文件 + _, err = io.Copy(file, resp.Body) + if err != nil { + g.Log().Error(ctx, err) + return fmt.Errorf("failed to write data to file: %w", err) + } + + return nil +} + +func (s *sAdminProject) BuildTask(ctx context.Context, options string, resource string, projectId int64) (err error) { + + serviceurl := s.GetCfgEnv(ctx, DEPLOY_BUILD_URL_KEY) + jobName := s.GetCfgEnv(ctx, DEPLOY_BUILD_JOBNAME_KEY) + callback := s.GetCfgEnv(ctx, DEPLOY_BUILD_CALLBACK_KEY) + uploadPath := s.GetCfgEnv(ctx, DEPLOY_BUILD_UPLOAD_KEY) + + last := strings.LastIndex(resource, "/") + if last > 0 { + filename := resource + + var uploadFile = "" + if gstr.ToLower("windows") == "linux" { + uploadFile = "bin/linux_amd64/hc-base-gf" + } else { + uploadFile = "bin/windows_amd64/hc-base-gf.exe" + } + paras := map[string]string{ + "jobName": jobName, + "remote_path": "obj.ResUrl", + "project_id": gconv.String(projectId), + // "target_path": "target/project", + "target_path": "/projects/1panel/1panel/apps/jenkins/jenkins/data/_data/jenkins_output", + "file_name": filename, + + "callback_url": callback, //"http://192.168.10.241:30621/port/getJobBuildBack", + "upload_path": uploadPath, + "deploy_env": "windows", + "auto_open_enable": "true", + "upload_file": uploadFile, + "front_path": "", + } + + num, err, errMsg := s.buildJenkinsJob(ctx, serviceurl, paras, jobName) + g.Log().Info(ctx, fmt.Sprintf("构建任务:%s, num:%d, err:%s, errMsg:%s", jobName, num, err, errMsg)) + + } + + return err +} + +func (s *sAdminProject) GetCfgEnv(ctx context.Context, key string) string { + v, _ := g.Cfg().GetWithEnv(ctx, key) + + return v.String() +} + +func (s *sAdminProject) buildJenkinsJob(ctx context.Context, baseUrl string, parameters map[string]string, pipelineJobName string) (num int64, err error, errMsg string) { + glog.Info(ctx, fmt.Sprintf("开始构建部署任务:baseUrl:%s, parameters:%s,pipelineJobName:%s", baseUrl, parameters, pipelineJobName)) + // 替换为有效的用户名和 API Token + jobuser := s.GetCfgEnv(ctx, DEPLOY_BUILD_JOBUSER_KEY) + jobpassword := s.GetCfgEnv(ctx, DEPLOY_BUILD_JOBPASSWORD_KEY) + // 创建 HTTP 客户端 + client := &http.Client{} + glog.Info(ctx, fmt.Sprintf("初始化Jenkins客户端:baseUrl:%s,用户:%s", baseUrl, jobuser)) + jenkins, err := gojenkins.CreateJenkins(client, baseUrl, jobuser, jobpassword).Init(ctx) + if err != nil { + errMsg = fmt.Sprintf("Jenkins 客户端初始化失败:baseUrl:%s,用户:%s", baseUrl, jobuser) + glog.Errorf(ctx, errMsg, err) + return 0, err, errMsg + } + jobarr := strings.Split(pipelineJobName, "/") + newjobname := strings.Join(jobarr, "/job/") + glog.Info(ctx, fmt.Sprintf("触发流水线执行,任务名:%s", newjobname)) + queId, err := jenkins.BuildJob(ctx, newjobname, parameters) + glog.Debug(ctx, queId) + if err != nil { + errMsg = fmt.Sprintf("触发流水线执行失败:流水线名:%s, 错误信息:%s", newjobname, err.Error()) + glog.Error(ctx, errMsg, err) + return 0, err, errMsg + } + //通过队列号获取不到构建,只用来刷新构建号获取值 + build, err := jenkins.GetBuildFromQueueID(ctx, queId) + if build != nil { + glog.Debug(ctx, build.GetBuildNumber()) + } else { + glog.Debug(ctx, queId) + } + job, err := jenkins.GetJob(ctx, newjobname) + lastbuild, err := job.GetLastBuild(ctx) + glog.Debug(ctx, "流水线执行已触发,构建号:", lastbuild.GetBuildNumber()) + glog.Info(ctx, "部署任务执行成功,构建号:", lastbuild.GetBuildNumber()) + return lastbuild.GetBuildNumber(), err, "" +} diff --git a/internal/model/input/adminin/project.go b/internal/model/input/adminin/project.go index d154110..4eb459a 100644 --- a/internal/model/input/adminin/project.go +++ b/internal/model/input/adminin/project.go @@ -125,3 +125,15 @@ type ProjectPublishModel struct{} type ProjectUploadInp struct { File *ghttp.UploadFile `json:"object" type:"file" dc:"文件"` } + +type ProjectDeployInp struct { + Id int64 `json:"id"` + Url string `json:"url"` +} + +type ProjectDeployListInp struct { + Name string `json:"name"` +} + +type ProjectDeployListModel struct { +} diff --git a/internal/model/json_obj.go b/internal/model/json_obj.go new file mode 100644 index 0000000..ab47b65 --- /dev/null +++ b/internal/model/json_obj.go @@ -0,0 +1,109 @@ +package model + +type JsonObj struct { + EditCanvasConfig *EditCanvasConfig `json:"editCanvasConfig"` + ComponentList []*Component `json:"componentList"` + RequestGlobalConfig interface{} `json:"requestGlobalConfig"` + StaticFile interface{} `json:"staticFile"` + GlobalVarList []interface{} `json:"globalVarList"` +} + +type Component struct { + Id string `json:"id"` + IsGroup bool `json:"isGroup"` + Attr interface{} `json:"attr"` + Styles interface{} `json:"styles"` + Preview struct { + OverFlowHidden bool `json:"overFlowHidden"` + } `json:"preview"` + Status struct { + Lock bool `json:"lock"` + Hide bool `json:"hide"` + } `json:"status"` + Request interface{} `json:"request"` + Filter interface{} `json:"filter"` + Events interface{} `json:"events"` + Key string `json:"key"` + ChartConfig struct { + Key string `json:"key"` + ChartKey string `json:"chartKey"` + ConKey string `json:"conKey"` + Title string `json:"title"` + Category string `json:"category"` + CategoryName string `json:"categoryName"` + Package string `json:"package"` + ChartFrame string `json:"chartFrame"` + Image string `json:"image"` + } `json:"chartConfig"` + Option struct { + Type string `json:"type"` + Dataset interface{} `json:"dataset"` + Fit string `json:"fit"` + BorderRadius int `json:"borderRadius,omitempty"` + Loop bool `json:"loop,omitempty"` + Muted bool `json:"muted,omitempty"` + Autoplay bool `json:"autoplay,omitempty"` + Interval int `json:"interval,omitempty"` + SlidesPerview int `json:"slidesPerview,omitempty"` + Direction string `json:"direction,omitempty"` + Draggable bool `json:"draggable,omitempty"` + CenteredSlides bool `json:"centeredSlides,omitempty"` + Effect string `json:"effect,omitempty"` + ShowDots bool `json:"showDots,omitempty"` + DotType string `json:"dotType,omitempty"` + DotPlacement string `json:"dotPlacement,omitempty"` + ShowArrow bool `json:"showArrow,omitempty"` + } `json:"option"` +} + +type EditCanvasConfig struct { + ProjectName string `json:"projectName"` + Width int `json:"width"` + Height int `json:"height"` + FilterShow bool `json:"filterShow"` + HueRotate int `json:"hueRotate"` + Saturate int `json:"saturate"` + Contrast int `json:"contrast"` + Brightness int `json:"brightness"` + Opacity int `json:"opacity"` + RotateZ int `json:"rotateZ"` + RotateX int `json:"rotateX"` + RotateY int `json:"rotateY"` + SkewX int `json:"skewX"` + SkewY int `json:"skewY"` + BlendMode string `json:"blendMode"` + Background interface{} `json:"background"` + BackgroundImage string `json:"backgroundImage"` + SelectColor bool `json:"selectColor"` + ChartThemeColor string `json:"chartThemeColor"` + ChartCustomThemeColorInfo interface{} `json:"chartCustomThemeColorInfo"` + ChartThemeSetting interface{} `json:"chartThemeSetting"` + PreviewScaleType string `json:"previewScaleType"` +} + +type RequestObj struct { + RequestDataType int `json:"requestDataType"` + RequestHttpType string `json:"requestHttpType"` + RequestUrl string `json:"requestUrl"` + RequestInterval interface{} `json:"requestInterval"` + RequestIntervalUnit string `json:"requestIntervalUnit"` + RequestContentType int `json:"requestContentType"` + RequestParamsBodyType string `json:"requestParamsBodyType"` + RequestSQLContent struct { + Sql string `json:"sql"` + } `json:"requestSQLContent"` + RequestParams struct { + Body struct { + FormData struct { + } `json:"form-data"` + XWwwFormUrlencoded struct { + } `json:"x-www-form-urlencoded"` + Json string `json:"json"` + Xml string `json:"xml"` + } `json:"Body"` + Header struct { + } `json:"Header"` + Params struct { + } `json:"Params"` + } `json:"requestParams"` +} diff --git a/internal/service/admin.go b/internal/service/admin.go index 8bbd501..c9e6059 100644 --- a/internal/service/admin.go +++ b/internal/service/admin.go @@ -27,6 +27,8 @@ type ( Publish(ctx context.Context, in *adminin.ProjectPublishInp) (err error) // GetData 获取指定信息 GetData(ctx context.Context, in *adminin.ProjectGetDataInp) (res *adminin.ProjectGetDataModel, err error) + //项目部署 + Deploy(ctx context.Context, in *adminin.ProjectDeployInp)(err error) } IAdminSite interface { // Register 账号注册 diff --git a/internal/utils/file.go b/internal/utils/file.go new file mode 100644 index 0000000..4d0b2c1 --- /dev/null +++ b/internal/utils/file.go @@ -0,0 +1,20 @@ +package utils + +import "os" + +func PathExists(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil{ + return true, nil + } + if os.IsNotExist(err) { + // 创建文件夹 + err := os.MkdirAll(path, os.ModePerm) + if err != nil { + return false, err + } else { + return true, nil + } + } + return false, err +} \ No newline at end of file diff --git a/internal/utils/zip.go b/internal/utils/zip.go new file mode 100644 index 0000000..f51e26a --- /dev/null +++ b/internal/utils/zip.go @@ -0,0 +1,104 @@ +/* + * @Descripttion: + * @version: + * @Author: lfzxs@qq.com + * @Date: 2023-09-20 16:06:07 + * @LastEditors: lfzxs@qq.com + * @LastEditTime: 2023-09-21 16:48:23 + */ +package utils + +import ( + "archive/zip" + "bytes" + "io" + "os" + "path/filepath" + + "golang.org/x/exp/slog" +) + +func ZipCompress(dirPath string, zipPath string) error { + + // 创建一个缓冲区来存储压缩后的数据 + buf := new(bytes.Buffer) + + // 创建一个zip.Writer + zipWriter := zip.NewWriter(buf) + + // 遍历目录下的文件和子目录 + err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // 构建在zip中的相对路径 + relPath, err := filepath.Rel(dirPath, path) + if err != nil { + return err + } + + // 判断是否为文件 + if !info.IsDir() { + // 将文件添加到zip中 + zipFile, err := zipWriter.Create(relPath) + if err != nil { + return err + } + + // 打开文件并复制到zip中 + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + _, err = io.Copy(zipFile, file) + if err != nil { + return err + } + } + + return nil + }) + + + + if err != nil { + slog.Error("遍历目录失败:","error",err) + return err + } + + // 关闭zip写入器 + err = zipWriter.Close() + if err != nil { + slog.Error("关闭zip写入器失败:","error",err) + return err + } + + // 将压缩后的数据写入文件 + fpath := filepath.Dir(zipPath) + _,err = PathExists(fpath) + + if err != nil { + slog.Error("创建压缩文件失败:","error",err) + return err + } + + zipFile, err := os.Create(zipPath) + if err != nil { + slog.Error("创建压缩文件失败:","error",err) + return err + } + defer zipFile.Close() + + // 将压缩后的数据复制到文件中 + _, err = io.Copy(zipFile, buf) + if err != nil { + slog.Error("复制压缩数据到文件失败:","error",err) + return err + } + + return nil + +} diff --git a/manifest/config/config.yaml b/manifest/config/config.yaml index ab79c5a..d30502c 100644 --- a/manifest/config/config.yaml +++ b/manifest/config/config.yaml @@ -35,7 +35,7 @@ server: openapiPath: "/api.json" # OpenAPI接口文档地址 swaggerPath: "/swagger" # 内置SwaggerUI展示地址 serverRoot: "resource/public" # 静态文件服务的目录根路径,配置时自动开启静态文件服务。 - DumpRouterMap: false # 是否在Server启动时打印所有的路由列表。 + DumpRouterMap: true # 是否在Server启动时打印所有的路由列表。 logPath: "logs/server" # 服务日志保存路径 ErrorStack: true # 当Server捕获到异常时是否记录堆栈信息到日志中。默认为true ErrorLogEnabled: true # 是否记录异常日志信息到日志中。默认为true @@ -43,7 +43,7 @@ server: accessLogEnabled: true # 是否记录访问日志。默认为false accessLogPattern: "access/{Y-m-d}.log" # 访问日志文件格式。默认为"access-{Ymd}.log" maxHeaderBytes: "100KB" # 请求头大小限制,请求头包括客户端提交的Cookie数据,默认设置为100KB - clientMaxBodySize: "300MB" # 客户端提交的Body大小限制,同时也影响文件上传大小,默认设置为300MB + clientMaxBodySize: "600MB" # 客户端提交的Body大小限制,同时也影响文件上传大小,默认设置为300MB serverAgent: "HG HTTP Server" # 服务端Agent信息。默认为"HG HTTP Server" # PProf配置 pprofEnabled: true # 是否开启PProf性能调试特性。默认为false @@ -73,8 +73,8 @@ token: storager: # 通用配置 drive: "local" # 上传驱动。local:本地存储 - fileSize: "10" # 上传图片大小限制,单位:MB - fileType: "doc,docx,pdf,zip,tar,xls,xlsx,rar,jpg,jpeg,gif,npm,png,svg" # 上传文件类型限制,文件上传后缀类型限制 + fileSize: "1000" # 上传图片大小限制,单位:MB + fileType: "doc,docx,pdf,zip,tar,xls,xlsx,rar,jpg,jpeg,gif,npm,png,svg,mp4" # 上传文件类型限制,文件上传后缀类型限制 imageSize: "your_image_size_value" # 上传图片大小限制,单位:MB imageType: "jpg,jpeg,gif,npm,png,svg" # 上传图片类型限制,图片上传后缀类型限制 localPath: "attachment/" # 本地存储路径,对外访问的相对路径 @@ -101,3 +101,19 @@ database: Prefix: "hg_" +deploy: + build: + #部署中间层服务地址 + url: "http://192.168.1.100:8888" + #Jenkins任务名称 + jobName: "local-deploy" + #Jenkins任务用户 + jobUser: "root" + #Jenkins任务密码 + jobPassword: "Gy,.2024" + #Jenkins任务token + jobToken: "" + #部署结果文件上传地址 + upload: "http://192.168.10.241:30103/trade/deploy/bosavepath" + #部署打包中外部打包资源的回调接口的URL地址 + callBackUrl: "http://192.168.10.241:30103/trade/deploy/packinfocall" \ No newline at end of file