-
Notifications
You must be signed in to change notification settings - Fork 2.9k
feat: Support script library management #8067
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| package v2 | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "path" | ||
| "strconv" | ||
| "strings" | ||
|
|
||
| "github.com/1Panel-dev/1Panel/core/app/api/v2/helper" | ||
| "github.com/1Panel-dev/1Panel/core/app/dto" | ||
| "github.com/1Panel-dev/1Panel/core/app/service" | ||
| "github.com/1Panel-dev/1Panel/core/global" | ||
| "github.com/1Panel-dev/1Panel/core/utils/ssh" | ||
| "github.com/1Panel-dev/1Panel/core/utils/terminal" | ||
| "github.com/1Panel-dev/1Panel/core/utils/xpack" | ||
| "github.com/gin-gonic/gin" | ||
| "github.com/pkg/errors" | ||
| ) | ||
|
|
||
| // @Tags ScriptLibrary | ||
| // @Summary Add script | ||
| // @Accept json | ||
| // @Param request body dto.ScriptOperate true "request" | ||
| // @Success 200 | ||
| // @Security ApiKeyAuth | ||
| // @Security Timestamp | ||
| // @Router /script [post] | ||
| // @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加脚本库脚本 [name]","formatEN":"add script [name]"} | ||
| func (b *BaseApi) CreateScript(c *gin.Context) { | ||
| var req dto.ScriptOperate | ||
| if err := helper.CheckBindAndValidate(&req, c); err != nil { | ||
| return | ||
| } | ||
|
|
||
| if err := scriptService.Create(req); err != nil { | ||
| helper.InternalServer(c, err) | ||
| return | ||
| } | ||
| helper.SuccessWithOutData(c) | ||
| } | ||
|
|
||
| // @Tags ScriptLibrary | ||
| // @Summary Page script | ||
| // @Accept json | ||
| // @Param request body dto.SearchPageWithGroup true "request" | ||
| // @Success 200 {object} dto.PageResult | ||
| // @Security ApiKeyAuth | ||
| // @Security Timestamp | ||
| // @Router /script/search [post] | ||
| func (b *BaseApi) SearchScript(c *gin.Context) { | ||
| var req dto.SearchPageWithGroup | ||
| if err := helper.CheckBindAndValidate(&req, c); err != nil { | ||
| return | ||
| } | ||
|
|
||
| total, list, err := scriptService.Search(req) | ||
| if err != nil { | ||
| helper.InternalServer(c, err) | ||
| return | ||
| } | ||
|
|
||
| helper.SuccessWithData(c, dto.PageResult{ | ||
| Items: list, | ||
| Total: total, | ||
| }) | ||
| } | ||
|
|
||
| // @Tags ScriptLibrary | ||
| // @Summary Delete script | ||
| // @Accept json | ||
| // @Param request body dto.BatchDeleteReq true "request" | ||
| // @Success 200 | ||
| // @Security ApiKeyAuth | ||
| // @Security Timestamp | ||
| // @Router /script/del [post] | ||
| // @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"script_librarys","output_column":"name","output_value":"names"}],"formatZH":"删除脚本库脚本 [names]","formatEN":"delete script [names]"} | ||
| func (b *BaseApi) DeleteScript(c *gin.Context) { | ||
| var req dto.OperateByIDs | ||
| if err := helper.CheckBindAndValidate(&req, c); err != nil { | ||
| return | ||
| } | ||
|
|
||
| if err := scriptService.Delete(req); err != nil { | ||
| helper.InternalServer(c, err) | ||
| return | ||
| } | ||
| helper.SuccessWithOutData(c) | ||
| } | ||
|
|
||
| // @Tags ScriptLibrary | ||
| // @Summary Update script | ||
| // @Accept json | ||
| // @Param request body dto.ScriptOperate true "request" | ||
| // @Success 200 | ||
| // @Security ApiKeyAuth | ||
| // @Security Timestamp | ||
| // @Router /script/update [post] | ||
| // @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"cronjobs","output_column":"name","output_value":"name"}],"formatZH":"更新脚本库脚本 [name]","formatEN":"update script [name]"} | ||
| func (b *BaseApi) UpdateScript(c *gin.Context) { | ||
| var req dto.ScriptOperate | ||
| if err := helper.CheckBindAndValidate(&req, c); err != nil { | ||
| return | ||
| } | ||
|
|
||
| if err := scriptService.Update(req); err != nil { | ||
| helper.InternalServer(c, err) | ||
| return | ||
| } | ||
| helper.SuccessWithOutData(c) | ||
| } | ||
|
|
||
| func (b *BaseApi) RunScript(c *gin.Context) { | ||
| wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil) | ||
| if err != nil { | ||
| global.LOG.Errorf("gin context http handler failed, err: %v", err) | ||
| return | ||
| } | ||
| defer wsConn.Close() | ||
|
|
||
| if global.CONF.Base.IsDemo { | ||
| if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) { | ||
| return | ||
| } | ||
| } | ||
|
|
||
| cols, err := strconv.Atoi(c.DefaultQuery("cols", "80")) | ||
| if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) { | ||
| return | ||
| } | ||
| rows, err := strconv.Atoi(c.DefaultQuery("rows", "40")) | ||
| if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) { | ||
| return | ||
| } | ||
| scriptID := c.Query("script_id") | ||
| currentNode := c.Query("current_node") | ||
| intNum, _ := strconv.Atoi(scriptID) | ||
| if intNum == 0 { | ||
| if wshandleError(wsConn, fmt.Errorf(" no such script %v in library, please check and try again!", scriptID)) { | ||
| return | ||
| } | ||
| } | ||
| scriptItem, err := service.LoadScriptInfo(uint(intNum)) | ||
| if wshandleError(wsConn, err) { | ||
| return | ||
| } | ||
|
|
||
| fileName := strings.ReplaceAll(scriptItem.Name, " ", "_") | ||
| quitChan := make(chan bool, 3) | ||
| if currentNode == "local" { | ||
| tmpFile := path.Join(global.CONF.Base.InstallDir, "1panel/tmp/script") | ||
| initCmd := fmt.Sprintf("d=%s && mkdir -p $d && echo %s > $d/%s && clear && bash $d/%s", tmpFile, scriptItem.Script, fileName, fileName) | ||
| slave, err := terminal.NewCommand(initCmd) | ||
| if wshandleError(wsConn, err) { | ||
| return | ||
| } | ||
| defer slave.Close() | ||
|
|
||
| tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true) | ||
| if wshandleError(wsConn, err) { | ||
| return | ||
| } | ||
|
|
||
| quitChan := make(chan bool, 3) | ||
| tty.Start(quitChan) | ||
| go slave.Wait(quitChan) | ||
| } else { | ||
| connInfo, installDir, err := xpack.LoadNodeInfo(currentNode) | ||
| if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) { | ||
| return | ||
| } | ||
| tmpFile := path.Join(installDir, "1panel/tmp/script") | ||
| initCmd := fmt.Sprintf("d=%s && mkdir -p $d && echo %s > $d/%s && clear && bash $d/%s", tmpFile, scriptItem.Script, fileName, fileName) | ||
| client, err := ssh.NewClient(*connInfo) | ||
| if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) { | ||
| return | ||
| } | ||
| defer client.Close() | ||
|
|
||
| sws, err := terminal.NewLogicSshWsSession(cols, rows, client.Client, wsConn, initCmd) | ||
| if wshandleError(wsConn, err) { | ||
| return | ||
| } | ||
| defer sws.Close() | ||
| sws.Start(quitChan) | ||
| go sws.Wait(quitChan) | ||
| } | ||
|
|
||
| <-quitChan | ||
|
|
||
| global.LOG.Info("websocket finished") | ||
| if wshandleError(wsConn, err) { | ||
| return | ||
| } | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The provided code appears to be from the script library API of a backend application written in Gin framework using Go programming language. Given that it's quite old with an outdated format and design elements, there is nothing inherently irregular about it at this point; however, some potential areas could include:
In particular, here are some points which can guide improvements or optimizations based on modern practices while considering legacy nature of these functions: Function Definitions: Ensure each function follows proper naming conventions without redundant comments. The //@Package v2Input Validation: Validate parameters in more robust ways instead of simple checks, especially when they represent data types beyond basic validation. var req dto.ScriptOperate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}Ensure all requests have validations for required fields before processing them. For example: if len(req.name) < 1 || len(req.description) < 1 { // Adding custom validator function
errors.New("Invalid name or description length.")
}Use more meaningful names for constants rather than hard-coded variable names where possible. Consider replacing magic number To enhance readability, organize the source file into namespaces/slices/tuples wherever appropriate for modularity. Finally, maintain backward compatibility for new functionality added later on, but ensure backwards compatability across existing functionality remains intact. Regarding performance and optimization, consider adding import statements for more recent packages that would improve runtime and memory efficiency. As these examples reflect changes related to best practices in modern engineering development, they are intended solely as guidance towards cleaner coding approaches. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package dto | ||
|
|
||
| import "time" | ||
|
|
||
| type ScriptInfo struct { | ||
| ID uint `json:"id"` | ||
| Name string `json:"name"` | ||
| Lable string `json:"lable"` | ||
| Script string `json:"script"` | ||
| GroupList []uint `json:"groupList"` | ||
| GroupBelong []string `json:"groupBelong"` | ||
| IsSystem bool `json:"isSystem"` | ||
| Description string `json:"description"` | ||
| CreatedAt time.Time `json:"createdAt"` | ||
| } | ||
|
|
||
| type ScriptOperate struct { | ||
| ID uint `json:"id"` | ||
| Name string `json:"name"` | ||
| Script string `json:"script"` | ||
| Groups string `json:"groups"` | ||
| Description string `json:"description"` | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package model | ||
|
|
||
| type ScriptLibrary struct { | ||
| BaseModel | ||
| Name string `json:"name" gorm:"not null;"` | ||
| Lable string `json:"lable"` | ||
| Script string `json:"script" gorm:"not null;"` | ||
| Groups string `json:"groups"` | ||
| IsSystem bool `json:"isSystem"` | ||
| Description string `json:"description"` | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The provided code is from the 1Panel project and contains several parts of its logic related to various APIs in the script library.
Changes Needed Summary
There are multiple changes that can be suggested based on the specific requirements and constraints of your application:
BaseApi package
helper.SuccessWithOutData(c)which doesn't seem to have an action parameter like it should handle data back to clients properly.General Code Modifications
SearchScriptrather than just/script/search).To provide detailed and useful feedback, let’s address points specifically addressed below: