Skip to content

Commit

Permalink
feat: Rsync 插件
Browse files Browse the repository at this point in the history
  • Loading branch information
devhaozi committed Nov 23, 2023
1 parent 596cd68 commit a368117
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 160 deletions.
4 changes: 2 additions & 2 deletions app/console/commands/panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,8 @@ func (receiver *Panel) Handle(ctx console.Context) error {
case "writeSite":
name := arg1
status := cast.ToBool(arg2)
path := ctx.Argument(3)
php := cast.ToInt(ctx.Argument(4))
path := arg3
php := cast.ToInt(arg4)
ssl := cast.ToBool(ctx.Argument(5))
if len(name) == 0 || len(path) == 0 {
color.Redln("参数错误")
Expand Down
79 changes: 48 additions & 31 deletions app/http/controllers/plugins/rsync_controller.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package plugins

import (
"regexp"
"strings"

"github.com/goravel/framework/contracts/http"
Expand Down Expand Up @@ -133,7 +134,7 @@ func (r *RsyncController) List(ctx http.Context) http.Response {
modules = append(modules, *currentModule)
}
moduleName := line[1 : len(line)-1]
secret, err := tools.Exec("grep -E '^" + moduleName + ":.*$' /etc/rsyncd.secrets | awk '{print $2}'")
secret, err := tools.Exec("grep -E '^" + moduleName + ":.*$' /etc/rsyncd.secrets | awk -F ':' '{print $2}'")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, "获取模块"+moduleName+"的密钥失败")
}
Expand Down Expand Up @@ -189,40 +190,47 @@ func (r *RsyncController) List(ctx http.Context) http.Response {
})
}

// Add
// Create
//
// @Summary 添加模块
// @Description 添加 Rsync 模块
// @Tags 插件-Rsync
// @Produce json
// @Security BearerToken
// @Param data body requests.Add true "request"
// @Param data body requests.Create true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/rsync/modules [post]
func (r *RsyncController) Add(ctx http.Context) http.Response {
var addRequest requests.Add
sanitize := controllers.Sanitize(ctx, &addRequest)
func (r *RsyncController) Create(ctx http.Context) http.Response {
var createRequest requests.Create
sanitize := controllers.Sanitize(ctx, &createRequest)
if sanitize != nil {
return sanitize
}

conf := `
# ` + addRequest.Name + `-START
[` + addRequest.Name + `]
path = ` + addRequest.Path + `
comment = ` + addRequest.Comment + `
config, err := tools.Read("/etc/rsyncd.conf")
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if strings.Contains(config, "["+createRequest.Name+"]") {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "模块 "+createRequest.Name+" 已存在")
}

conf := `# ` + createRequest.Name + `-START
[` + createRequest.Name + `]
path = ` + createRequest.Path + `
comment = ` + createRequest.Comment + `
read only = no
auth users = ` + addRequest.AuthUser + `
hosts allow = ` + addRequest.HostsAllow + `
auth users = ` + createRequest.AuthUser + `
hosts allow = ` + createRequest.HostsAllow + `
secrets file = /etc/rsyncd.secrets
# ` + addRequest.Name + `-END
# ` + createRequest.Name + `-END
`

if err := tools.WriteAppend("/etc/rsyncd.conf", conf); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if err := tools.WriteAppend("/etc/rsyncd.secrets", addRequest.Name+":"+addRequest.Secret); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
if out, err := tools.Exec("echo '" + createRequest.AuthUser + ":" + createRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}

if err := tools.ServiceRestart("rsyncd"); err != nil {
Expand All @@ -232,17 +240,17 @@ secrets file = /etc/rsyncd.secrets
return controllers.Success(ctx, nil)
}

// Delete
// Destroy
//
// @Summary 删除模块
// @Description 删除 Rsync 模块
// @Tags 插件-Rsync
// @Produce json
// @Security BearerToken
// @Param name query string true "模块名称"
// @Param name path string true "模块名称"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/rsync/modules [delete]
func (r *RsyncController) Delete(ctx http.Context) http.Response {
// @Router /plugins/rsync/modules/{name} [delete]
func (r *RsyncController) Destroy(ctx http.Context) http.Response {
name := ctx.Request().Input("name")
if len(name) == 0 {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "name 不能为空")
Expand All @@ -259,12 +267,17 @@ func (r *RsyncController) Delete(ctx http.Context) http.Response {
module := tools.Cut(config, "# "+name+"-START", "# "+name+"-END")
config = strings.Replace(config, "\n# "+name+"-START"+module+"# "+name+"-END", "", -1)

match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module)
if len(match) == 2 {
authUser := match[1]
if out, err := tools.Exec("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
}

if err = tools.Write("/etc/rsyncd.conf", config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := tools.Exec("sed -i '/^" + name + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}

if err = tools.ServiceRestart("rsyncd"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
Expand All @@ -280,9 +293,10 @@ func (r *RsyncController) Delete(ctx http.Context) http.Response {
// @Tags 插件-Rsync
// @Produce json
// @Security BearerToken
// @Param name path string true "模块名称"
// @Param data body requests.Update true "request"
// @Success 200 {object} controllers.SuccessResponse
// @Router /plugins/rsync/modules [put]
// @Router /plugins/rsync/modules/{name} [post]
func (r *RsyncController) Update(ctx http.Context) http.Response {
var updateRequest requests.Update
sanitize := controllers.Sanitize(ctx, &updateRequest)
Expand All @@ -294,7 +308,6 @@ func (r *RsyncController) Update(ctx http.Context) http.Response {
if err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}

if !strings.Contains(config, "["+updateRequest.Name+"]") {
return controllers.Error(ctx, http.StatusUnprocessableEntity, "模块 "+updateRequest.Name+" 不存在")
}
Expand All @@ -307,21 +320,25 @@ read only = no
auth users = ` + updateRequest.AuthUser + `
hosts allow = ` + updateRequest.HostsAllow + `
secrets file = /etc/rsyncd.secrets
# ` + updateRequest.Name + `-END
`
# ` + updateRequest.Name + `-END`

module := tools.Cut(config, "# "+updateRequest.Name+"-START", "# "+updateRequest.Name+"-END")
config = strings.Replace(config, "# "+updateRequest.Name+"-START"+module+"# "+updateRequest.Name+"-END", newConf, -1)

match := regexp.MustCompile(`auth users = ([^\n]+)`).FindStringSubmatch(module)
if len(match) == 2 {
authUser := match[1]
if out, err := tools.Exec("sed -i '/^" + authUser + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
}

if err = tools.Write("/etc/rsyncd.conf", config, 0644); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}
if out, err := tools.Exec("sed -i '/^" + updateRequest.Name + ":.*$/d' /etc/rsyncd.secrets"); err != nil {
if out, err := tools.Exec("echo '" + updateRequest.AuthUser + ":" + updateRequest.Secret + "' >> /etc/rsyncd.secrets"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, out)
}
if err := tools.WriteAppend("/etc/rsyncd.secrets", updateRequest.Name+":"+updateRequest.Secret); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
}

if err = tools.ServiceRestart("rsyncd"); err != nil {
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
Expand Down
17 changes: 5 additions & 12 deletions app/http/requests/common/paginate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
)

type Paginate struct {
Page int `form:"page" json:"page"`
Limit int `form:"limit" json:"limit"`
Page int `form:"page" json:"page" filter:"int"`
Limit int `form:"limit" json:"limit" filter:"int"`
}

func (r *Paginate) Authorize(ctx http.Context) error {
Expand All @@ -16,20 +16,13 @@ func (r *Paginate) Authorize(ctx http.Context) error {

func (r *Paginate) Rules(ctx http.Context) map[string]string {
return map[string]string{
"page": "required|uint|min:1",
"limit": "required|uint|min:1",
"page": "required|int|min:1",
"limit": "required|int|min:1",
}
}

func (r *Paginate) Messages(ctx http.Context) map[string]string {
return map[string]string{
"page.required": "分页参数 page 不能为空",
"page.uint": "分页参数 page 必须是一个整数",
"page.min": "分页参数 page 必须大于等于 1",
"limit.required": "分页参数 limit 不能为空",
"limit.uint": "分页参数 limit 必须是一个整数",
"limit.min": "分页参数 limit 必须大于等于 1",
}
return map[string]string{}
}

func (r *Paginate) Attributes(ctx http.Context) map[string]string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"github.com/goravel/framework/contracts/validation"
)

type Add struct {
type Create struct {
Name string `form:"name" json:"name"`
Path string `form:"path" json:"path"`
Comment string `form:"comment" json:"comment"`
Expand All @@ -14,11 +14,11 @@ type Add struct {
HostsAllow string `form:"hosts_allow" json:"hosts_allow"`
}

func (r *Add) Authorize(ctx http.Context) error {
func (r *Create) Authorize(ctx http.Context) error {
return nil
}

func (r *Add) Rules(ctx http.Context) map[string]string {
func (r *Create) Rules(ctx http.Context) map[string]string {
return map[string]string{
"name": "required|regex:^[a-zA-Z0-9-_]+$",
"path": "regex:^/[a-zA-Z0-9_-]+(\\/[a-zA-Z0-9_-]+)*$",
Expand All @@ -29,14 +29,14 @@ func (r *Add) Rules(ctx http.Context) map[string]string {
}
}

func (r *Add) Messages(ctx http.Context) map[string]string {
func (r *Create) Messages(ctx http.Context) map[string]string {
return map[string]string{}
}

func (r *Add) Attributes(ctx http.Context) map[string]string {
func (r *Create) Attributes(ctx http.Context) map[string]string {
return map[string]string{}
}

func (r *Add) PrepareForValidation(ctx http.Context, data validation.Data) error {
func (r *Create) PrepareForValidation(ctx http.Context, data validation.Data) error {
return nil
}
Loading

0 comments on commit a368117

Please sign in to comment.