diff --git a/app/http/controllers/cert_controller.go b/app/http/controllers/cert_controller.go new file mode 100644 index 00000000..cfc8c7f5 --- /dev/null +++ b/app/http/controllers/cert_controller.go @@ -0,0 +1,488 @@ +package controllers + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" + requests "panel/app/http/requests/cert" + + commonrequests "panel/app/http/requests/common" + responses "panel/app/http/responses/cert" + "panel/app/models" + "panel/app/services" + "panel/pkg/acme" +) + +type CertController struct { + cron services.Cron + cert services.Cert +} + +func NewCertController() *CertController { + return &CertController{ + cron: services.NewCronImpl(), + cert: services.NewCertImpl(), + } +} + +// CAProviders +// @Summary 获取 CA 提供商 +// @Description 获取面板证书管理支持的 CA 提供商 +// @Tags 证书 +// @Produce json +// @Security BearerToken +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Router /panel/cert/caProviders [get] +func (r *CertController) CAProviders(ctx http.Context) http.Response { + return Success(ctx, []map[string]string{ + { + "name": "Let's Encrypt", + "ca": acme.CALetEncrypt, + }, + { + "name": "ZeroSSL", + "ca": acme.CAZeroSSL, + }, + { + "name": "SSL.com", + "ca": acme.CASSLcom, + }, + { + "name": "Google", + "ca": acme.CAGoogle, + }, + { + "name": "Buypass", + "ca": acme.CABuypass, + }, + }) +} + +// DNSProviders +// @Summary 获取 DNS 提供商 +// @Description 获取面板证书管理支持的 DNS 提供商 +// @Tags 证书 +// @Produce json +// @Security BearerToken +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Router /panel/cert/dnsProviders [get] +func (r *CertController) DNSProviders(ctx http.Context) http.Response { + return Success(ctx, []map[string]any{ + { + "name": "DNSPod", + "dns": acme.DnsPod, + }, + { + "name": "阿里云", + "dns": acme.AliYun, + }, + { + "name": "CloudFlare", + "dns": acme.CloudFlare, + }, + }) +} + +// Algorithms +// @Summary 获取算法列表 +// @Description 获取面板证书管理支持的算法列表 +// @Tags 证书 +// @Produce json +// @Security BearerToken +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Router /panel/cert/algorithms [get] +func (r *CertController) Algorithms(ctx http.Context) http.Response { + return Success(ctx, []map[string]any{ + { + "name": "EC256", + "key": acme.KeyEC256, + }, + { + "name": "EC384", + "key": acme.KeyEC384, + }, + { + "name": "RSA2048", + "key": acme.KeyRSA2048, + }, + { + "name": "RSA4096", + "key": acme.KeyRSA4096, + }, + }) +} + +// UserList +// @Summary 获取用户列表 +// @Description 获取面板证书管理的 ACME 用户列表 +// @Tags 证书 +// @Produce json +// @Security BearerToken +// @Success 200 {object} SuccessResponse{data=responses.CertList} +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/users [get] +func (r *CertController) UserList(ctx http.Context) http.Response { + var updateProfileRequest commonrequests.Paginate + sanitize := Sanitize(ctx, &updateProfileRequest) + if sanitize != nil { + return sanitize + } + + var users []models.CertUser + var total int64 + err := facades.Orm().Query().Paginate(updateProfileRequest.Page, updateProfileRequest.Limit, &users, &total) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "error": err.Error(), + }).Error("获取ACME用户列表失败") + return ErrorSystem(ctx) + } + + return Success(ctx, &responses.UserList{ + Total: total, + Items: users, + }) +} + +// UserAdd +// @Summary 添加 ACME 用户 +// @Description 添加 ACME 用户到面板证书管理 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param data body requests.UserAdd true "用户信息" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/users [post] +func (r *CertController) UserAdd(ctx http.Context) http.Response { + var addRequest requests.UserAdd + sanitize := Sanitize(ctx, &addRequest) + if sanitize != nil { + return sanitize + } + + err := r.cert.UserAdd(addRequest) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "error": err.Error(), + }).Error("添加ACME用户失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// UserDelete +// @Summary 删除 ACME 用户 +// @Description 删除面板证书管理的 ACME 用户 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param id path int true "用户 ID" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/users/{id} [delete] +func (r *CertController) UserDelete(ctx http.Context) http.Response { + userID := ctx.Request().InputInt("id") + + err := r.cert.UserDelete(uint(userID)) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "userID": userID, + "error": err.Error(), + }).Error("删除ACME用户失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// DNSList +// @Summary 获取 DNS 接口列表 +// @Description 获取面板证书管理的 DNS 接口列表 +// @Tags 证书 +// @Produce json +// @Security BearerToken +// @Success 200 {object} SuccessResponse{data=responses.DNSList} +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/dns [get] +func (r *CertController) DNSList(ctx http.Context) http.Response { + var updateProfileRequest commonrequests.Paginate + sanitize := Sanitize(ctx, &updateProfileRequest) + if sanitize != nil { + return sanitize + } + + var dns []models.CertDNS + var total int64 + err := facades.Orm().Query().Paginate(updateProfileRequest.Page, updateProfileRequest.Limit, &dns, &total) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "error": err.Error(), + }).Error("获取DNS接口列表失败") + return ErrorSystem(ctx) + } + + return Success(ctx, &responses.DNSList{ + Total: total, + Items: dns, + }) +} + +// DNSAdd +// @Summary 添加 DNS 接口 +// @Description 添加 DNS 接口到面板证书管理 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param data body requests.DNSAdd true "DNS 接口信息" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/dns [post] +func (r *CertController) DNSAdd(ctx http.Context) http.Response { + var addRequest requests.DNSAdd + sanitize := Sanitize(ctx, &addRequest) + if sanitize != nil { + return sanitize + } + + err := r.cert.DNSAdd(addRequest) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "error": err.Error(), + }).Error("添加DNS接口失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// DNSDelete +// @Summary 删除 DNS 接口 +// @Description 删除面板证书管理的 DNS 接口 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param id path int true "DNS 接口 ID" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/dns/{id} [delete] +func (r *CertController) DNSDelete(ctx http.Context) http.Response { + dnsID := ctx.Request().InputInt("id") + + err := r.cert.DNSDelete(uint(dnsID)) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "dnsID": dnsID, + "error": err.Error(), + }).Error("删除DNS接口失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// CertList +// @Summary 获取证书列表 +// @Description 获取面板证书管理的证书列表 +// @Tags 证书 +// @Produce json +// @Security BearerToken +// @Success 200 {object} SuccessResponse{data=responses.CertList} +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/certs [get] +func (r *CertController) CertList(ctx http.Context) http.Response { + var updateProfileRequest commonrequests.Paginate + sanitize := Sanitize(ctx, &updateProfileRequest) + if sanitize != nil { + return sanitize + } + + var certs []models.Cert + var total int64 + err := facades.Orm().Query().Paginate(updateProfileRequest.Page, updateProfileRequest.Limit, &certs, &total) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "error": err.Error(), + }).Error("获取证书列表失败") + return ErrorSystem(ctx) + } + + return Success(ctx, &responses.CertList{ + Total: total, + Items: certs, + }) +} + +// CertAdd +// @Summary 添加证书 +// @Description 添加证书到面板证书管理 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param data body requests.CertAdd true "证书信息" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/certs [post] +func (r *CertController) CertAdd(ctx http.Context) http.Response { + var addRequest requests.CertAdd + sanitize := Sanitize(ctx, &addRequest) + if sanitize != nil { + return sanitize + } + + err := r.cert.CertAdd(addRequest) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "error": err.Error(), + }).Error("添加证书失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// CertDelete +// @Summary 删除证书 +// @Description 删除面板证书管理的证书 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param id path int true "证书 ID" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/certs/{id} [delete] +func (r *CertController) CertDelete(ctx http.Context) http.Response { + certID := ctx.Request().InputInt("id") + + err := r.cert.CertDelete(uint(certID)) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "certID": certID, + "error": err.Error(), + }).Error("删除证书失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// Obtain +// @Summary 签发证书 +// @Description 签发面板证书管理的证书 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param data body requests.Obtain true "证书信息" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/obtain [post] +func (r *CertController) Obtain(ctx http.Context) http.Response { + var obtainRequest requests.Obtain + sanitize := Sanitize(ctx, &obtainRequest) + if sanitize != nil { + return sanitize + } + + cert, err := r.cert.GetByID(obtainRequest.ID) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "certID": obtainRequest.ID, + "error": err.Error(), + }).Error("获取证书失败") + return ErrorSystem(ctx) + } + + if cert.DNS != nil || cert.Website != nil { + _, err = r.cert.ObtainAuto(obtainRequest.ID) + } else { + _, err = r.cert.ObtainManual(obtainRequest.ID) + } + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "error": err.Error(), + }).Error("签发证书失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// Renew +// @Summary 续签证书 +// @Description 续签面板证书管理的证书 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param data body requests.Renew true "证书信息" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/renew [post] +func (r *CertController) Renew(ctx http.Context) http.Response { + var renewRequest requests.Renew + sanitize := Sanitize(ctx, &renewRequest) + if sanitize != nil { + return sanitize + } + + _, err := r.cert.Renew(renewRequest.ID) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "error": err.Error(), + }).Error("续签证书失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// ManualDNS +// @Summary 获取手动 DNS 记录 +// @Description 获取签发证书所需的 DNS 记录 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param data body requests.Obtain true "证书信息" +// @Success 200 {object} SuccessResponse{data=map[string]acme.Resolve} +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/manualDNS [post] +func (r *CertController) ManualDNS(ctx http.Context) http.Response { + var obtainRequest requests.Obtain + sanitize := Sanitize(ctx, &obtainRequest) + if sanitize != nil { + return sanitize + } + + resolves, err := r.cert.ManualDNS(obtainRequest.ID) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "error": err.Error(), + }).Error("获取手动DNS记录失败") + return ErrorSystem(ctx) + } + + return Success(ctx, resolves) +} diff --git a/app/http/controllers/helpers.go b/app/http/controllers/controller.go similarity index 54% rename from app/http/controllers/helpers.go rename to app/http/controllers/controller.go index 6532c481..1042c845 100644 --- a/app/http/controllers/helpers.go +++ b/app/http/controllers/controller.go @@ -9,31 +9,55 @@ import ( "panel/app/services" ) -func Success(ctx http.Context, data ...any) http.Response { - var d any - if len(data) > 0 { - d = data[0] +// SuccessResponse 通用成功响应 +type SuccessResponse struct { + Code int `json:"code"` + Message string `json:"message"` + Data any `json:"data"` +} + +// ErrorResponse 通用错误响应 +type ErrorResponse struct { + Code int `json:"code"` + Message string `json:"message"` +} + +// Success 响应成功 +func Success(ctx http.Context, data any) http.Response { + return ctx.Response().Success().Json(&SuccessResponse{ + Code: 0, + Message: "success", + Data: data, + }) +} + +// Error 响应错误 +func Error(ctx http.Context, code int, message string) http.Response { + return ctx.Response().Json(http.StatusOK, &ErrorResponse{ + Code: code, + Message: message, + }) +} + +// ErrorSystem 响应系统错误 +func ErrorSystem(ctx http.Context) http.Response { + return ctx.Response().Json(http.StatusOK, &ErrorResponse{ + Code: http.StatusInternalServerError, + Message: "系统内部错误", + }) +} + +// Sanitize 消毒请求参数 +func Sanitize(ctx http.Context, request http.FormRequest) http.Response { + errors, err := ctx.Request().ValidateRequest(request) + if err != nil { + return Error(ctx, http.StatusUnprocessableEntity, err.Error()) + } + if errors != nil { + return Error(ctx, http.StatusUnprocessableEntity, errors.One()) } - return ctx.Response().Success().Json(http.Json{ - "code": 0, - "message": "success", - "data": d, - }) -} - -func Error(ctx http.Context, code int, message any) http.Response { - return ctx.Response().Json(http.StatusOK, http.Json{ - "code": code, - "message": message, - }) -} - -func SystemError(ctx http.Context) http.Response { - return ctx.Response().Json(http.StatusOK, http.Json{ - "code": http.StatusInternalServerError, - "message": "系统内部错误", - }) + return nil } // Check 检查插件是否可用 diff --git a/app/http/controllers/plugins/certbot/certbot_controller.go b/app/http/controllers/plugins/certbot/certbot_controller.go deleted file mode 100644 index 62739d6d..00000000 --- a/app/http/controllers/plugins/certbot/certbot_controller.go +++ /dev/null @@ -1,274 +0,0 @@ -package redis - -import ( - "github.com/goravel/framework/contracts/http" - "github.com/goravel/framework/support/carbon" - "github.com/goravel/framework/support/json" - - "panel/app/http/controllers" - "panel/app/services" - "panel/pkg/acme" -) - -type CertbotController struct { - setting services.Setting -} - -type User struct { - Email string - CA string // CA 提供商 (letsencrypt, zerossl, sslcom, google, buypass) - Kid string - HmacEncoded string - PrivateKey string -} - -type DNS struct { - Type string // DNS 类型 (dnspod, aliyun, cloudflare) - acme.DNSParam -} - -type Cert struct { - ID int64 `json:"id"` - CronID int64 `json:"cron_id"` - Type string `json:"type"` - Domains []string `json:"domains"` -} - -func NewCertbotController() *CertbotController { - return &CertbotController{ - setting: services.NewSettingImpl(), - } -} - -// CAProviders 获取 CA 提供商 -func (c *CertbotController) CAProviders(ctx http.Context) http.Response { - check := controllers.Check(ctx, "certbot") - if check != nil { - return check - } - - return controllers.Success(ctx, []map[string]string{ - { - "name": "Let's Encrypt", - "ca": acme.CALetEncrypt, - }, - { - "name": "ZeroSSL", - "ca": acme.CAZeroSSL, - }, - { - "name": "SSL.com", - "ca": acme.CASSLcom, - }, - { - "name": "Google", - "ca": acme.CAGoogle, - }, - { - "name": "Buypass", - "ca": acme.CABuypass, - }, - }) -} - -// Algorithms 获取算法列表 -func (c *CertbotController) Algorithms(ctx http.Context) http.Response { - check := controllers.Check(ctx, "certbot") - if check != nil { - return check - } - - return controllers.Success(ctx, []map[string]any{ - { - "name": "EC256", - "key": acme.KeyEC256, - }, - { - "name": "EC384", - "key": acme.KeyEC384, - }, - { - "name": "RSA2048", - "key": acme.KeyRSA2048, - }, - { - "name": "RSA4096", - "key": acme.KeyRSA4096, - }, - }) -} - -// UserList 获取用户列表 -func (c *CertbotController) UserList(ctx http.Context) http.Response { - check := controllers.Check(ctx, "certbot") - if check != nil { - return check - } - - page := ctx.Request().QueryInt("page", 1) - limit := ctx.Request().QueryInt("limit", 10) - - var userList []User - err := json.UnmarshalString(c.setting.Get("certbot_user", "[]"), &userList) - if err != nil { - return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取证书列表失败") - } - - startIndex := (page - 1) * limit - endIndex := page * limit - if startIndex > len(userList) { - return controllers.Success(ctx, http.Json{ - "total": 0, - "items": []User{}, - }) - } - if endIndex > len(userList) { - endIndex = len(userList) - } - pagedCertList := userList[startIndex:endIndex] - if pagedCertList == nil { - pagedCertList = []User{} - } - - return controllers.Success(ctx, http.Json{ - "total": len(userList), - "items": pagedCertList, - }) -} - -// DNSList 获取 DNS 列表 -func (c *CertbotController) DNSList(ctx http.Context) http.Response { - check := controllers.Check(ctx, "certbot") - if check != nil { - return check - } - - page := ctx.Request().QueryInt("page", 1) - limit := ctx.Request().QueryInt("limit", 10) - - var dnsList []DNS - err := json.UnmarshalString(c.setting.Get("certbot_dns", "[]"), &dnsList) - if err != nil { - return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取证书列表失败") - } - - startIndex := (page - 1) * limit - endIndex := page * limit - if startIndex > len(dnsList) { - return controllers.Success(ctx, http.Json{ - "total": 0, - "items": []DNS{}, - }) - } - if endIndex > len(dnsList) { - endIndex = len(dnsList) - } - pagedCertList := dnsList[startIndex:endIndex] - if pagedCertList == nil { - pagedCertList = []DNS{} - } - - return controllers.Success(ctx, http.Json{ - "total": len(dnsList), - "items": pagedCertList, - }) -} - -// CertList 所有证书 -func (c *CertbotController) CertList(ctx http.Context) http.Response { - check := controllers.Check(ctx, "certbot") - if check != nil { - return check - } - - page := ctx.Request().QueryInt("page", 1) - limit := ctx.Request().QueryInt("limit", 10) - - var certList []Cert - err := json.UnmarshalString(c.setting.Get("certbot_cert", "[]"), &certList) - if err != nil { - return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取证书列表失败") - } - - startIndex := (page - 1) * limit - endIndex := page * limit - if startIndex > len(certList) { - return controllers.Success(ctx, http.Json{ - "total": 0, - "items": []Cert{}, - }) - } - if endIndex > len(certList) { - endIndex = len(certList) - } - pagedCertList := certList[startIndex:endIndex] - if pagedCertList == nil { - pagedCertList = []Cert{} - } - - return controllers.Success(ctx, http.Json{ - "total": len(certList), - "items": pagedCertList, - }) -} - -// CertAdd 添加证书 -func (c *CertbotController) CertAdd(ctx http.Context) http.Response { - check := controllers.Check(ctx, "certbot") - if check != nil { - return check - } - - var certList []Cert - err := json.UnmarshalString(c.setting.Get("certbot_cert", "[]"), &certList) - if err != nil { - return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取证书列表失败") - } - - var cert Cert - cert.ID = carbon.Now().TimestampMilli() - certList = append(certList, cert) - encoded, err := json.MarshalString(certList) - if err != nil { - return controllers.Error(ctx, http.StatusUnprocessableEntity, "添加证书失败") - } - err = c.setting.Set("certbot", encoded) - if err != nil { - return controllers.Error(ctx, http.StatusUnprocessableEntity, "添加证书失败") - } - - return controllers.Success(ctx, nil) -} - -// CertDelete 删除证书 -func (c *CertbotController) CertDelete(ctx http.Context) http.Response { - check := controllers.Check(ctx, "certbot") - if check != nil { - return check - } - - var certList []Cert - err := json.UnmarshalString(c.setting.Get("certbot_cert", "[]"), &certList) - if err != nil { - return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取证书列表失败") - } - - var cert Cert - cert.ID = ctx.Request().InputInt64("id") - for i, item := range certList { - if item.ID == cert.ID { - certList = append(certList[:i], certList[i+1:]...) - break - } - } - encoded, err := json.MarshalString(certList) - if err != nil { - return controllers.Error(ctx, http.StatusUnprocessableEntity, "删除证书失败") - } - err = c.setting.Set("certbot", encoded) - if err != nil { - return controllers.Error(ctx, http.StatusUnprocessableEntity, "删除证书失败") - } - - return controllers.Success(ctx, nil) -} diff --git a/app/http/controllers/plugins/constants.go b/app/http/controllers/plugins/constants.go new file mode 100644 index 00000000..0a7a321a --- /dev/null +++ b/app/http/controllers/plugins/constants.go @@ -0,0 +1,29 @@ +package plugins + +type PHPExtension struct { + Name string `json:"name"` + Slug string `json:"slug"` + Description string `json:"description"` + Installed bool `json:"installed"` +} + +type LoadInfo struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type Fail2banJail struct { + Name string `json:"name"` + Enabled bool `json:"enabled"` + LogPath string `json:"log_path"` + MaxRetry int `json:"max_retry"` + FindTime int `json:"find_time"` + BanTime int `json:"ban_time"` +} + +type S3fsMount struct { + ID int64 `json:"id"` + Path string `json:"path"` + Bucket string `json:"bucket"` + Url string `json:"url"` +} diff --git a/app/http/controllers/plugins/fail2ban/fail2ban_controller.go b/app/http/controllers/plugins/fail2ban_controller.go similarity index 91% rename from app/http/controllers/plugins/fail2ban/fail2ban_controller.go rename to app/http/controllers/plugins/fail2ban_controller.go index 67fd6706..f38f90ae 100644 --- a/app/http/controllers/plugins/fail2ban/fail2ban_controller.go +++ b/app/http/controllers/plugins/fail2ban_controller.go @@ -1,4 +1,4 @@ -package fail2ban +package plugins import ( "regexp" @@ -24,17 +24,8 @@ func NewFail2banController() *Fail2banController { } } -type Jail struct { - Name string `json:"name"` - Enabled bool `json:"enabled"` - LogPath string `json:"log_path"` - MaxRetry int `json:"max_retry"` - FindTime int `json:"find_time"` - BanTime int `json:"ban_time"` -} - // Status 获取运行状态 -func (c *Fail2banController) Status(ctx http.Context) http.Response { +func (r *Fail2banController) Status(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -53,7 +44,7 @@ func (c *Fail2banController) Status(ctx http.Context) http.Response { } // Reload 重载配置 -func (c *Fail2banController) Reload(ctx http.Context) http.Response { +func (r *Fail2banController) Reload(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -73,7 +64,7 @@ func (c *Fail2banController) Reload(ctx http.Context) http.Response { } // Restart 重启服务 -func (c *Fail2banController) Restart(ctx http.Context) http.Response { +func (r *Fail2banController) Restart(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -93,7 +84,7 @@ func (c *Fail2banController) Restart(ctx http.Context) http.Response { } // Start 启动服务 -func (c *Fail2banController) Start(ctx http.Context) http.Response { +func (r *Fail2banController) Start(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -113,7 +104,7 @@ func (c *Fail2banController) Start(ctx http.Context) http.Response { } // Stop 停止服务 -func (c *Fail2banController) Stop(ctx http.Context) http.Response { +func (r *Fail2banController) Stop(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -133,7 +124,7 @@ func (c *Fail2banController) Stop(ctx http.Context) http.Response { } // List 所有 Fail2ban 规则 -func (c *Fail2banController) List(ctx http.Context) http.Response { +func (r *Fail2banController) List(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -151,7 +142,7 @@ func (c *Fail2banController) List(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "Fail2ban 规则为空") } - var jails []Jail + var jails []Fail2banJail for i, jail := range jailList { if i == 0 { continue @@ -168,7 +159,7 @@ func (c *Fail2banController) List(ctx http.Context) http.Response { jailFindTime := regexp.MustCompile(`findtime = (.*)`).FindStringSubmatch(jailRaw) jailBanTime := regexp.MustCompile(`bantime = (.*)`).FindStringSubmatch(jailRaw) - jails = append(jails, Jail{ + jails = append(jails, Fail2banJail{ Name: jailName, Enabled: jailEnabled, LogPath: jailLogPath[1], @@ -183,7 +174,7 @@ func (c *Fail2banController) List(ctx http.Context) http.Response { if startIndex > len(jails) { return controllers.Success(ctx, http.Json{ "total": 0, - "items": []Jail{}, + "items": []Fail2banJail{}, }) } if endIndex > len(jails) { @@ -191,7 +182,7 @@ func (c *Fail2banController) List(ctx http.Context) http.Response { } pagedJails := jails[startIndex:endIndex] if pagedJails == nil { - pagedJails = []Jail{} + pagedJails = []Fail2banJail{} } return controllers.Success(ctx, http.Json{ @@ -201,7 +192,7 @@ func (c *Fail2banController) List(ctx http.Context) http.Response { } // Add 添加 Fail2ban 规则 -func (c *Fail2banController) Add(ctx http.Context) http.Response { +func (r *Fail2banController) Add(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -245,7 +236,7 @@ func (c *Fail2banController) Add(ctx http.Context) http.Response { if err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, "网站不存在") } - config, err := c.website.GetConfig(int(website.ID)) + config, err := r.website.GetConfig(int(website.ID)) if err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取网站配置失败") } @@ -340,7 +331,7 @@ logpath = ` + logPath + ` } // Delete 删除规则 -func (c *Fail2banController) Delete(ctx http.Context) http.Response { +func (r *Fail2banController) Delete(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -362,7 +353,7 @@ func (c *Fail2banController) Delete(ctx http.Context) http.Response { } // BanList 获取封禁列表 -func (c *Fail2banController) BanList(ctx http.Context) http.Response { +func (r *Fail2banController) BanList(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -399,7 +390,7 @@ func (c *Fail2banController) BanList(ctx http.Context) http.Response { } // Unban 解封 -func (c *Fail2banController) Unban(ctx http.Context) http.Response { +func (r *Fail2banController) Unban(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -416,7 +407,7 @@ func (c *Fail2banController) Unban(ctx http.Context) http.Response { } // SetWhiteList 设置白名单 -func (c *Fail2banController) SetWhiteList(ctx http.Context) http.Response { +func (r *Fail2banController) SetWhiteList(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check @@ -442,7 +433,7 @@ func (c *Fail2banController) SetWhiteList(ctx http.Context) http.Response { } // GetWhiteList 获取白名单 -func (c *Fail2banController) GetWhiteList(ctx http.Context) http.Response { +func (r *Fail2banController) GetWhiteList(ctx http.Context) http.Response { check := controllers.Check(ctx, "fail2ban") if check != nil { return check diff --git a/app/http/controllers/plugins/mysql57/mysql57_controller.go b/app/http/controllers/plugins/mysql57_controller.go similarity index 88% rename from app/http/controllers/plugins/mysql57/mysql57_controller.go rename to app/http/controllers/plugins/mysql57_controller.go index 3fab9bc3..1b9d1ff4 100644 --- a/app/http/controllers/plugins/mysql57/mysql57_controller.go +++ b/app/http/controllers/plugins/mysql57_controller.go @@ -1,4 +1,4 @@ -package mysql57 +package plugins import ( "database/sql" @@ -29,7 +29,7 @@ func NewMysql57Controller() *Mysql57Controller { } // Status 获取运行状态 -func (c *Mysql57Controller) Status(ctx http.Context) http.Response { +func (r *Mysql57Controller) Status(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -48,7 +48,7 @@ func (c *Mysql57Controller) Status(ctx http.Context) http.Response { } // Reload 重载配置 -func (c *Mysql57Controller) Reload(ctx http.Context) http.Response { +func (r *Mysql57Controller) Reload(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -68,7 +68,7 @@ func (c *Mysql57Controller) Reload(ctx http.Context) http.Response { } // Restart 重启服务 -func (c *Mysql57Controller) Restart(ctx http.Context) http.Response { +func (r *Mysql57Controller) Restart(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -88,7 +88,7 @@ func (c *Mysql57Controller) Restart(ctx http.Context) http.Response { } // Start 启动服务 -func (c *Mysql57Controller) Start(ctx http.Context) http.Response { +func (r *Mysql57Controller) Start(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -108,7 +108,7 @@ func (c *Mysql57Controller) Start(ctx http.Context) http.Response { } // Stop 停止服务 -func (c *Mysql57Controller) Stop(ctx http.Context) http.Response { +func (r *Mysql57Controller) Stop(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -128,7 +128,7 @@ func (c *Mysql57Controller) Stop(ctx http.Context) http.Response { } // GetConfig 获取配置 -func (c *Mysql57Controller) GetConfig(ctx http.Context) http.Response { +func (r *Mysql57Controller) GetConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -144,7 +144,7 @@ func (c *Mysql57Controller) GetConfig(ctx http.Context) http.Response { } // SaveConfig 保存配置 -func (c *Mysql57Controller) SaveConfig(ctx http.Context) http.Response { +func (r *Mysql57Controller) SaveConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -159,17 +159,17 @@ func (c *Mysql57Controller) SaveConfig(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败") } - return c.Restart(ctx) + return r.Restart(ctx) } // Load 获取负载 -func (c *Mysql57Controller) Load(ctx http.Context) http.Response { +func (r *Mysql57Controller) Load(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) if len(rootPassword) == 0 { return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空") } @@ -238,7 +238,7 @@ func (c *Mysql57Controller) Load(ctx http.Context) http.Response { } // ErrorLog 获取错误日志 -func (c *Mysql57Controller) ErrorLog(ctx http.Context) http.Response { +func (r *Mysql57Controller) ErrorLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -249,7 +249,7 @@ func (c *Mysql57Controller) ErrorLog(ctx http.Context) http.Response { } // ClearErrorLog 清空错误日志 -func (c *Mysql57Controller) ClearErrorLog(ctx http.Context) http.Response { +func (r *Mysql57Controller) ClearErrorLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -260,7 +260,7 @@ func (c *Mysql57Controller) ClearErrorLog(ctx http.Context) http.Response { } // SlowLog 获取慢查询日志 -func (c *Mysql57Controller) SlowLog(ctx http.Context) http.Response { +func (r *Mysql57Controller) SlowLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -271,7 +271,7 @@ func (c *Mysql57Controller) SlowLog(ctx http.Context) http.Response { } // ClearSlowLog 清空慢查询日志 -func (c *Mysql57Controller) ClearSlowLog(ctx http.Context) http.Response { +func (r *Mysql57Controller) ClearSlowLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -282,13 +282,13 @@ func (c *Mysql57Controller) ClearSlowLog(ctx http.Context) http.Response { } // GetRootPassword 获取root密码 -func (c *Mysql57Controller) GetRootPassword(ctx http.Context) http.Response { +func (r *Mysql57Controller) GetRootPassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) if len(rootPassword) == 0 { return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空") } @@ -297,7 +297,7 @@ func (c *Mysql57Controller) GetRootPassword(ctx http.Context) http.Response { } // SetRootPassword 设置root密码 -func (c *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response { +func (r *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -316,11 +316,11 @@ func (c *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码不能为空") } - oldRootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + oldRootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) if oldRootPassword != rootPassword { tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + rootPassword + "';\"") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"FLUSH PRIVILEGES;\"") - err := c.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword) + err := r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword) if err != nil { tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + oldRootPassword + "';\"") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"") @@ -332,13 +332,13 @@ func (c *Mysql57Controller) SetRootPassword(ctx http.Context) http.Response { } // DatabaseList 获取数据库列表 -func (c *Mysql57Controller) DatabaseList(ctx http.Context) http.Response { +func (r *Mysql57Controller) DatabaseList(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) type database struct { Name string `json:"name"` } @@ -395,7 +395,7 @@ func (c *Mysql57Controller) DatabaseList(ctx http.Context) http.Response { } // AddDatabase 添加数据库 -func (c *Mysql57Controller) AddDatabase(ctx http.Context) http.Response { +func (r *Mysql57Controller) AddDatabase(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -413,7 +413,7 @@ func (c *Mysql57Controller) AddDatabase(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) database := ctx.Request().Input("database") user := ctx.Request().Input("user") password := ctx.Request().Input("password") @@ -427,7 +427,7 @@ func (c *Mysql57Controller) AddDatabase(ctx http.Context) http.Response { } // DeleteDatabase 删除数据库 -func (c *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response { +func (r *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -443,7 +443,7 @@ func (c *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) database := ctx.Request().Input("database") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\"") @@ -451,13 +451,13 @@ func (c *Mysql57Controller) DeleteDatabase(ctx http.Context) http.Response { } // BackupList 获取备份列表 -func (c *Mysql57Controller) BackupList(ctx http.Context) http.Response { +func (r *Mysql57Controller) BackupList(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check } - backupList, err := c.backup.MysqlList() + backupList, err := r.backup.MysqlList() if err != nil { facades.Log().Error("[MySQL57] 获取备份列表失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败") @@ -488,7 +488,7 @@ func (c *Mysql57Controller) BackupList(ctx http.Context) http.Response { } // UploadBackup 上传备份 -func (c *Mysql57Controller) UploadBackup(ctx http.Context) http.Response { +func (r *Mysql57Controller) UploadBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -499,7 +499,7 @@ func (c *Mysql57Controller) UploadBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败") } - backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql" + backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql" if !tools.Exists(backupPath) { tools.Mkdir(backupPath, 0644) } @@ -514,7 +514,7 @@ func (c *Mysql57Controller) UploadBackup(ctx http.Context) http.Response { } // CreateBackup 创建备份 -func (c *Mysql57Controller) CreateBackup(ctx http.Context) http.Response { +func (r *Mysql57Controller) CreateBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -531,7 +531,7 @@ func (c *Mysql57Controller) CreateBackup(ctx http.Context) http.Response { } database := ctx.Request().Input("database") - err = c.backup.MysqlBackup(database) + err = r.backup.MysqlBackup(database) if err != nil { facades.Log().Error("[MYSQL57] 创建备份失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败") @@ -541,7 +541,7 @@ func (c *Mysql57Controller) CreateBackup(ctx http.Context) http.Response { } // DeleteBackup 删除备份 -func (c *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response { +func (r *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -557,7 +557,7 @@ func (c *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql" + backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql" fileName := ctx.Request().Input("name") tools.Remove(backupPath + "/" + fileName) @@ -565,7 +565,7 @@ func (c *Mysql57Controller) DeleteBackup(ctx http.Context) http.Response { } // RestoreBackup 还原备份 -func (c *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response { +func (r *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -582,7 +582,7 @@ func (c *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - err = c.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")) + err = r.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")) if err != nil { facades.Log().Error("[MYSQL57] 还原失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error()) @@ -592,7 +592,7 @@ func (c *Mysql57Controller) RestoreBackup(ctx http.Context) http.Response { } // UserList 用户列表 -func (c *Mysql57Controller) UserList(ctx http.Context) http.Response { +func (r *Mysql57Controller) UserList(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -604,7 +604,7 @@ func (c *Mysql57Controller) UserList(ctx http.Context) http.Response { Grants []string `json:"grants"` } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/") if err != nil { facades.Log().Error("[MYSQL57] 连接数据库失败:" + err.Error()) @@ -678,7 +678,7 @@ func (c *Mysql57Controller) UserList(ctx http.Context) http.Response { } // AddUser 添加用户 -func (c *Mysql57Controller) AddUser(ctx http.Context) http.Response { +func (r *Mysql57Controller) AddUser(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -696,7 +696,7 @@ func (c *Mysql57Controller) AddUser(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") password := ctx.Request().Input("password") database := ctx.Request().Input("database") @@ -708,7 +708,7 @@ func (c *Mysql57Controller) AddUser(ctx http.Context) http.Response { } // DeleteUser 删除用户 -func (c *Mysql57Controller) DeleteUser(ctx http.Context) http.Response { +func (r *Mysql57Controller) DeleteUser(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -724,7 +724,7 @@ func (c *Mysql57Controller) DeleteUser(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\"") @@ -732,7 +732,7 @@ func (c *Mysql57Controller) DeleteUser(ctx http.Context) http.Response { } // SetUserPassword 设置用户密码 -func (c *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response { +func (r *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -749,7 +749,7 @@ func (c *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") password := ctx.Request().Input("password") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"") @@ -759,7 +759,7 @@ func (c *Mysql57Controller) SetUserPassword(ctx http.Context) http.Response { } // SetUserPrivileges 设置用户权限 -func (c *Mysql57Controller) SetUserPrivileges(ctx http.Context) http.Response { +func (r *Mysql57Controller) SetUserPrivileges(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql57") if check != nil { return check @@ -776,7 +776,7 @@ func (c *Mysql57Controller) SetUserPrivileges(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") database := ctx.Request().Input("database") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\"") diff --git a/app/http/controllers/plugins/mysql80/mysql80_controller.go b/app/http/controllers/plugins/mysql80_controller.go similarity index 88% rename from app/http/controllers/plugins/mysql80/mysql80_controller.go rename to app/http/controllers/plugins/mysql80_controller.go index aea15e2f..9a12ff10 100644 --- a/app/http/controllers/plugins/mysql80/mysql80_controller.go +++ b/app/http/controllers/plugins/mysql80_controller.go @@ -1,4 +1,4 @@ -package mysql80 +package plugins import ( "database/sql" @@ -29,7 +29,7 @@ func NewMysql80Controller() *Mysql80Controller { } // Status 获取运行状态 -func (c *Mysql80Controller) Status(ctx http.Context) http.Response { +func (r *Mysql80Controller) Status(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -48,7 +48,7 @@ func (c *Mysql80Controller) Status(ctx http.Context) http.Response { } // Reload 重载配置 -func (c *Mysql80Controller) Reload(ctx http.Context) http.Response { +func (r *Mysql80Controller) Reload(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -68,7 +68,7 @@ func (c *Mysql80Controller) Reload(ctx http.Context) http.Response { } // Restart 重启服务 -func (c *Mysql80Controller) Restart(ctx http.Context) http.Response { +func (r *Mysql80Controller) Restart(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -88,7 +88,7 @@ func (c *Mysql80Controller) Restart(ctx http.Context) http.Response { } // Start 启动服务 -func (c *Mysql80Controller) Start(ctx http.Context) http.Response { +func (r *Mysql80Controller) Start(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -108,7 +108,7 @@ func (c *Mysql80Controller) Start(ctx http.Context) http.Response { } // Stop 停止服务 -func (c *Mysql80Controller) Stop(ctx http.Context) http.Response { +func (r *Mysql80Controller) Stop(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -128,7 +128,7 @@ func (c *Mysql80Controller) Stop(ctx http.Context) http.Response { } // GetConfig 获取配置 -func (c *Mysql80Controller) GetConfig(ctx http.Context) http.Response { +func (r *Mysql80Controller) GetConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -144,7 +144,7 @@ func (c *Mysql80Controller) GetConfig(ctx http.Context) http.Response { } // SaveConfig 保存配置 -func (c *Mysql80Controller) SaveConfig(ctx http.Context) http.Response { +func (r *Mysql80Controller) SaveConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -159,17 +159,17 @@ func (c *Mysql80Controller) SaveConfig(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败") } - return c.Restart(ctx) + return r.Restart(ctx) } // Load 获取负载 -func (c *Mysql80Controller) Load(ctx http.Context) http.Response { +func (r *Mysql80Controller) Load(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) if len(rootPassword) == 0 { return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空") } @@ -238,7 +238,7 @@ func (c *Mysql80Controller) Load(ctx http.Context) http.Response { } // ErrorLog 获取错误日志 -func (c *Mysql80Controller) ErrorLog(ctx http.Context) http.Response { +func (r *Mysql80Controller) ErrorLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -249,7 +249,7 @@ func (c *Mysql80Controller) ErrorLog(ctx http.Context) http.Response { } // ClearErrorLog 清空错误日志 -func (c *Mysql80Controller) ClearErrorLog(ctx http.Context) http.Response { +func (r *Mysql80Controller) ClearErrorLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -260,7 +260,7 @@ func (c *Mysql80Controller) ClearErrorLog(ctx http.Context) http.Response { } // SlowLog 获取慢查询日志 -func (c *Mysql80Controller) SlowLog(ctx http.Context) http.Response { +func (r *Mysql80Controller) SlowLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -271,7 +271,7 @@ func (c *Mysql80Controller) SlowLog(ctx http.Context) http.Response { } // ClearSlowLog 清空慢查询日志 -func (c *Mysql80Controller) ClearSlowLog(ctx http.Context) http.Response { +func (r *Mysql80Controller) ClearSlowLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -282,13 +282,13 @@ func (c *Mysql80Controller) ClearSlowLog(ctx http.Context) http.Response { } // GetRootPassword 获取root密码 -func (c *Mysql80Controller) GetRootPassword(ctx http.Context) http.Response { +func (r *Mysql80Controller) GetRootPassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) if len(rootPassword) == 0 { return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码为空") } @@ -297,7 +297,7 @@ func (c *Mysql80Controller) GetRootPassword(ctx http.Context) http.Response { } // SetRootPassword 设置root密码 -func (c *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response { +func (r *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -316,11 +316,11 @@ func (c *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码不能为空") } - oldRootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + oldRootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) if oldRootPassword != rootPassword { tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + rootPassword + "';\"") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + oldRootPassword + " -e \"FLUSH PRIVILEGES;\"") - err := c.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword) + err := r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword) if err != nil { tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '" + oldRootPassword + "';\"") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\"") @@ -332,13 +332,13 @@ func (c *Mysql80Controller) SetRootPassword(ctx http.Context) http.Response { } // DatabaseList 获取数据库列表 -func (c *Mysql80Controller) DatabaseList(ctx http.Context) http.Response { +func (r *Mysql80Controller) DatabaseList(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) type database struct { Name string `json:"name"` } @@ -395,7 +395,7 @@ func (c *Mysql80Controller) DatabaseList(ctx http.Context) http.Response { } // AddDatabase 添加数据库 -func (c *Mysql80Controller) AddDatabase(ctx http.Context) http.Response { +func (r *Mysql80Controller) AddDatabase(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -413,7 +413,7 @@ func (c *Mysql80Controller) AddDatabase(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) database := ctx.Request().Input("database") user := ctx.Request().Input("user") password := ctx.Request().Input("password") @@ -427,7 +427,7 @@ func (c *Mysql80Controller) AddDatabase(ctx http.Context) http.Response { } // DeleteDatabase 删除数据库 -func (c *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response { +func (r *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -443,7 +443,7 @@ func (c *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) database := ctx.Request().Input("database") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\"") @@ -451,13 +451,13 @@ func (c *Mysql80Controller) DeleteDatabase(ctx http.Context) http.Response { } // BackupList 获取备份列表 -func (c *Mysql80Controller) BackupList(ctx http.Context) http.Response { +func (r *Mysql80Controller) BackupList(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check } - backupList, err := c.backup.MysqlList() + backupList, err := r.backup.MysqlList() if err != nil { facades.Log().Error("[MySQL80] 获取备份列表失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败") @@ -488,7 +488,7 @@ func (c *Mysql80Controller) BackupList(ctx http.Context) http.Response { } // UploadBackup 上传备份 -func (c *Mysql80Controller) UploadBackup(ctx http.Context) http.Response { +func (r *Mysql80Controller) UploadBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -499,7 +499,7 @@ func (c *Mysql80Controller) UploadBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败") } - backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql" + backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql" if !tools.Exists(backupPath) { tools.Mkdir(backupPath, 0644) } @@ -514,7 +514,7 @@ func (c *Mysql80Controller) UploadBackup(ctx http.Context) http.Response { } // CreateBackup 创建备份 -func (c *Mysql80Controller) CreateBackup(ctx http.Context) http.Response { +func (r *Mysql80Controller) CreateBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -531,7 +531,7 @@ func (c *Mysql80Controller) CreateBackup(ctx http.Context) http.Response { } database := ctx.Request().Input("database") - err = c.backup.MysqlBackup(database) + err = r.backup.MysqlBackup(database) if err != nil { facades.Log().Error("[MYSQL80] 创建备份失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败") @@ -541,7 +541,7 @@ func (c *Mysql80Controller) CreateBackup(ctx http.Context) http.Response { } // DeleteBackup 删除备份 -func (c *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response { +func (r *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -557,7 +557,7 @@ func (c *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql" + backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/mysql" fileName := ctx.Request().Input("name") tools.Remove(backupPath + "/" + fileName) @@ -565,7 +565,7 @@ func (c *Mysql80Controller) DeleteBackup(ctx http.Context) http.Response { } // RestoreBackup 还原备份 -func (c *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response { +func (r *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -582,7 +582,7 @@ func (c *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - err = c.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")) + err = r.backup.MysqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")) if err != nil { facades.Log().Error("[MYSQL80] 还原失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error()) @@ -592,7 +592,7 @@ func (c *Mysql80Controller) RestoreBackup(ctx http.Context) http.Response { } // UserList 用户列表 -func (c *Mysql80Controller) UserList(ctx http.Context) http.Response { +func (r *Mysql80Controller) UserList(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -604,7 +604,7 @@ func (c *Mysql80Controller) UserList(ctx http.Context) http.Response { Grants []string `json:"grants"` } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/") if err != nil { facades.Log().Error("[MYSQL80] 连接数据库失败:" + err.Error()) @@ -678,7 +678,7 @@ func (c *Mysql80Controller) UserList(ctx http.Context) http.Response { } // AddUser 添加用户 -func (c *Mysql80Controller) AddUser(ctx http.Context) http.Response { +func (r *Mysql80Controller) AddUser(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -696,7 +696,7 @@ func (c *Mysql80Controller) AddUser(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") password := ctx.Request().Input("password") database := ctx.Request().Input("database") @@ -708,7 +708,7 @@ func (c *Mysql80Controller) AddUser(ctx http.Context) http.Response { } // DeleteUser 删除用户 -func (c *Mysql80Controller) DeleteUser(ctx http.Context) http.Response { +func (r *Mysql80Controller) DeleteUser(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -724,7 +724,7 @@ func (c *Mysql80Controller) DeleteUser(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\"") @@ -732,7 +732,7 @@ func (c *Mysql80Controller) DeleteUser(ctx http.Context) http.Response { } // SetUserPassword 设置用户密码 -func (c *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response { +func (r *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -749,7 +749,7 @@ func (c *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") password := ctx.Request().Input("password") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\"") @@ -759,7 +759,7 @@ func (c *Mysql80Controller) SetUserPassword(ctx http.Context) http.Response { } // SetUserPrivileges 设置用户权限 -func (c *Mysql80Controller) SetUserPrivileges(ctx http.Context) http.Response { +func (r *Mysql80Controller) SetUserPrivileges(ctx http.Context) http.Response { check := controllers.Check(ctx, "mysql80") if check != nil { return check @@ -776,7 +776,7 @@ func (c *Mysql80Controller) SetUserPrivileges(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword) + rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword) user := ctx.Request().Input("user") database := ctx.Request().Input("database") tools.Exec("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\"") diff --git a/app/http/controllers/plugins/openresty/openresty_controller.go b/app/http/controllers/plugins/openresty_controller.go similarity index 90% rename from app/http/controllers/plugins/openresty/openresty_controller.go rename to app/http/controllers/plugins/openresty_controller.go index 0c5e1ad8..7c97ffbf 100644 --- a/app/http/controllers/plugins/openresty/openresty_controller.go +++ b/app/http/controllers/plugins/openresty_controller.go @@ -1,4 +1,4 @@ -package openresty +package plugins import ( "regexp" @@ -24,7 +24,7 @@ func NewOpenrestyController() *OpenRestyController { } // Status 获取运行状态 -func (c *OpenRestyController) Status(ctx http.Context) http.Response { +func (r *OpenRestyController) Status(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check @@ -43,7 +43,7 @@ func (c *OpenRestyController) Status(ctx http.Context) http.Response { } // Reload 重载配置 -func (c *OpenRestyController) Reload(ctx http.Context) http.Response { +func (r *OpenRestyController) Reload(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check @@ -63,7 +63,7 @@ func (c *OpenRestyController) Reload(ctx http.Context) http.Response { } // Start 启动OpenResty -func (c *OpenRestyController) Start(ctx http.Context) http.Response { +func (r *OpenRestyController) Start(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check @@ -83,7 +83,7 @@ func (c *OpenRestyController) Start(ctx http.Context) http.Response { } // Stop 停止OpenResty -func (c *OpenRestyController) Stop(ctx http.Context) http.Response { +func (r *OpenRestyController) Stop(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check @@ -103,7 +103,7 @@ func (c *OpenRestyController) Stop(ctx http.Context) http.Response { } // Restart 重启OpenResty -func (c *OpenRestyController) Restart(ctx http.Context) http.Response { +func (r *OpenRestyController) Restart(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check @@ -123,7 +123,7 @@ func (c *OpenRestyController) Restart(ctx http.Context) http.Response { } // GetConfig 获取配置 -func (c *OpenRestyController) GetConfig(ctx http.Context) http.Response { +func (r *OpenRestyController) GetConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check @@ -138,7 +138,7 @@ func (c *OpenRestyController) GetConfig(ctx http.Context) http.Response { } // SaveConfig 保存配置 -func (c *OpenRestyController) SaveConfig(ctx http.Context) http.Response { +func (r *OpenRestyController) SaveConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check @@ -153,11 +153,11 @@ func (c *OpenRestyController) SaveConfig(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "保存OpenResty配置失败") } - return c.Reload(ctx) + return r.Reload(ctx) } // ErrorLog 获取错误日志 -func (c *OpenRestyController) ErrorLog(ctx http.Context) http.Response { +func (r *OpenRestyController) ErrorLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check @@ -172,7 +172,7 @@ func (c *OpenRestyController) ErrorLog(ctx http.Context) http.Response { } // ClearErrorLog 清空错误日志 -func (c *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response { +func (r *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check @@ -183,7 +183,7 @@ func (c *OpenRestyController) ClearErrorLog(ctx http.Context) http.Response { } // Load 获取负载 -func (c *OpenRestyController) Load(ctx http.Context) http.Response { +func (r *OpenRestyController) Load(ctx http.Context) http.Response { check := controllers.Check(ctx, "openresty") if check != nil { return check diff --git a/app/http/controllers/plugins/php74/php74_controller.go b/app/http/controllers/plugins/php74_controller.go similarity index 64% rename from app/http/controllers/plugins/php74/php74_controller.go rename to app/http/controllers/plugins/php74_controller.go index b1c36efb..82b94f75 100644 --- a/app/http/controllers/plugins/php74/php74_controller.go +++ b/app/http/controllers/plugins/php74_controller.go @@ -1,4 +1,4 @@ -package php74 +package plugins import ( "fmt" @@ -30,15 +30,15 @@ func NewPhp74Controller() *Php74Controller { } } -func (c *Php74Controller) Status(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) Status(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + status := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -48,17 +48,17 @@ func (c *Php74Controller) Status(ctx http.Context) http.Response { } } -func (c *Php74Controller) Reload(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) Reload(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl reload php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl reload php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -68,17 +68,17 @@ func (c *Php74Controller) Reload(ctx http.Context) http.Response { } } -func (c *Php74Controller) Start(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) Start(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl start php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl start php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -88,17 +88,17 @@ func (c *Php74Controller) Start(ctx http.Context) http.Response { } } -func (c *Php74Controller) Stop(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) Stop(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl stop php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl stop php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status != "active" { @@ -108,17 +108,17 @@ func (c *Php74Controller) Stop(ctx http.Context) http.Response { } } -func (c *Php74Controller) Restart(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) Restart(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl restart php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl restart php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -128,38 +128,38 @@ func (c *Php74Controller) Restart(ctx http.Context) http.Response { } } -func (c *Php74Controller) GetConfig(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) GetConfig(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini") + config := tools.Read("/www/server/php/" + r.version + "/etc/php.ini") return controllers.Success(ctx, config) } -func (c *Php74Controller) SaveConfig(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) SaveConfig(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } config := ctx.Request().Input("config") - tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644) - return c.Reload(ctx) + tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644) + return r.Reload(ctx) } -func (c *Php74Controller) Load(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) Load(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } client := req.C().SetTimeout(10 * time.Second) - resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version) + resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version) if err != nil || !resp.IsSuccessState() { - facades.Log().Error("获取PHP-" + c.version + "运行状态失败") - return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败") + facades.Log().Error("获取PHP-" + r.version + "运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败") } raw := resp.String() @@ -185,65 +185,58 @@ func (c *Php74Controller) Load(ctx http.Context) http.Response { return controllers.Success(ctx, data) } -func (c *Php74Controller) ErrorLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) ErrorLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log")) + log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log")) return controllers.Success(ctx, log) } -func (c *Php74Controller) SlowLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) SlowLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log")) + log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log")) return controllers.Success(ctx, log) } -func (c *Php74Controller) ClearErrorLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) ClearErrorLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log") + tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log") return controllers.Success(ctx, true) } -func (c *Php74Controller) ClearSlowLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) ClearSlowLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log") + tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log") return controllers.Success(ctx, true) } -type Extension struct { - Name string `json:"name"` - Slug string `json:"slug"` - Description string `json:"description"` - Installed bool `json:"installed"` -} - -func (c *Php74Controller) GetExtensionList(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) GetExtensionList(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - extensions := c.GetExtensions() + extensions := r.GetExtensions() return controllers.Success(ctx, extensions) } -func (c *Php74Controller) InstallExtension(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) InstallExtension(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } @@ -253,7 +246,7 @@ func (c *Php74Controller) InstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") } - extensions := c.GetExtensions() + extensions := r.GetExtensions() for _, item := range extensions { if item.Slug == slug { if item.Installed { @@ -261,16 +254,16 @@ func (c *Php74Controller) InstallExtension(ctx http.Context) http.Response { } var task models.Task - task.Name = "安装PHP-" + c.version + "扩展-" + item.Name + task.Name = "安装PHP-" + r.version + "扩展-" + item.Name task.Status = models.TaskStatusWaiting - task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` task.Log = "/tmp/" + item.Slug + ".log" if err := facades.Orm().Query().Create(&task); err != nil { - facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error()) + facades.Log().Error("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误") } - c.task.Process(task.ID) + r.task.Process(task.ID) return controllers.Success(ctx, true) } @@ -279,8 +272,8 @@ func (c *Php74Controller) InstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") } -func (c *Php74Controller) UninstallExtension(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php74Controller) UninstallExtension(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } @@ -290,7 +283,7 @@ func (c *Php74Controller) UninstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") } - extensions := c.GetExtensions() + extensions := r.GetExtensions() for _, item := range extensions { if item.Slug == slug { if !item.Installed { @@ -298,16 +291,16 @@ func (c *Php74Controller) UninstallExtension(ctx http.Context) http.Response { } var task models.Task - task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name + task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name task.Status = models.TaskStatusWaiting - task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` task.Log = "/tmp/" + item.Slug + ".log" if err := facades.Orm().Query().Create(&task); err != nil { - facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error()) + facades.Log().Error("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误") } - c.task.Process(task.ID) + r.task.Process(task.ID) return controllers.Success(ctx, true) } @@ -316,46 +309,46 @@ func (c *Php74Controller) UninstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") } -func (c *Php74Controller) GetExtensions() []Extension { - var extensions []Extension - extensions = append(extensions, Extension{ +func (r *Php74Controller) GetExtensions() []PHPExtension { + var extensions []PHPExtension + extensions = append(extensions, PHPExtension{ Name: "OPcache", Slug: "Zend OPcache", Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "PhpRedis", Slug: "redis", Description: "PhpRedis 是一个用C语言编写的PHP模块,用来连接并操作 Redis 数据库上的数据。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "ImageMagick", Slug: "imagick", Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "Exif", Slug: "exif", Description: "通过 exif 扩展,你可以操作图像元数据。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "pdo_pgsql", Slug: "pdo_pgsql", Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "ionCube", Slug: "ionCube Loader", Description: "ionCube 是一个专业级的PHP加密解密工具。", Installed: false, }) - raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m") + raw := tools.Exec("/www/server/php/" + r.version + "/bin/php -m") rawExtensionList := strings.Split(raw, "\n") for _, item := range rawExtensionList { diff --git a/app/http/controllers/plugins/php80/php80_controller.go b/app/http/controllers/plugins/php80_controller.go similarity index 64% rename from app/http/controllers/plugins/php80/php80_controller.go rename to app/http/controllers/plugins/php80_controller.go index dc80ecad..7d300a28 100644 --- a/app/http/controllers/plugins/php80/php80_controller.go +++ b/app/http/controllers/plugins/php80_controller.go @@ -1,4 +1,4 @@ -package php80 +package plugins import ( "fmt" @@ -30,15 +30,15 @@ func NewPhp80Controller() *Php80Controller { } } -func (c *Php80Controller) Status(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) Status(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + status := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -48,17 +48,17 @@ func (c *Php80Controller) Status(ctx http.Context) http.Response { } } -func (c *Php80Controller) Reload(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) Reload(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl reload php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl reload php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -68,17 +68,17 @@ func (c *Php80Controller) Reload(ctx http.Context) http.Response { } } -func (c *Php80Controller) Start(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) Start(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl start php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl start php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -88,17 +88,17 @@ func (c *Php80Controller) Start(ctx http.Context) http.Response { } } -func (c *Php80Controller) Stop(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) Stop(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl stop php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl stop php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status != "active" { @@ -108,17 +108,17 @@ func (c *Php80Controller) Stop(ctx http.Context) http.Response { } } -func (c *Php80Controller) Restart(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) Restart(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl restart php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl restart php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -128,38 +128,38 @@ func (c *Php80Controller) Restart(ctx http.Context) http.Response { } } -func (c *Php80Controller) GetConfig(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) GetConfig(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini") + config := tools.Read("/www/server/php/" + r.version + "/etc/php.ini") return controllers.Success(ctx, config) } -func (c *Php80Controller) SaveConfig(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) SaveConfig(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } config := ctx.Request().Input("config") - tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644) - return c.Reload(ctx) + tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644) + return r.Reload(ctx) } -func (c *Php80Controller) Load(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) Load(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } client := req.C().SetTimeout(10 * time.Second) - resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version) + resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version) if err != nil || !resp.IsSuccessState() { - facades.Log().Error("获取PHP-" + c.version + "运行状态失败") - return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败") + facades.Log().Error("获取PHP-" + r.version + "运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败") } raw := resp.String() @@ -185,65 +185,58 @@ func (c *Php80Controller) Load(ctx http.Context) http.Response { return controllers.Success(ctx, data) } -func (c *Php80Controller) ErrorLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) ErrorLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log")) + log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log")) return controllers.Success(ctx, log) } -func (c *Php80Controller) SlowLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) SlowLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log")) + log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log")) return controllers.Success(ctx, log) } -func (c *Php80Controller) ClearErrorLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) ClearErrorLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log") + tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log") return controllers.Success(ctx, true) } -func (c *Php80Controller) ClearSlowLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) ClearSlowLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log") + tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log") return controllers.Success(ctx, true) } -type Extension struct { - Name string `json:"name"` - Slug string `json:"slug"` - Description string `json:"description"` - Installed bool `json:"installed"` -} - -func (c *Php80Controller) GetExtensionList(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) GetExtensionList(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - extensions := c.GetExtensions() + extensions := r.GetExtensions() return controllers.Success(ctx, extensions) } -func (c *Php80Controller) InstallExtension(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) InstallExtension(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } @@ -253,7 +246,7 @@ func (c *Php80Controller) InstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") } - extensions := c.GetExtensions() + extensions := r.GetExtensions() for _, item := range extensions { if item.Slug == slug { if item.Installed { @@ -261,16 +254,16 @@ func (c *Php80Controller) InstallExtension(ctx http.Context) http.Response { } var task models.Task - task.Name = "安装PHP-" + c.version + "扩展-" + item.Name + task.Name = "安装PHP-" + r.version + "扩展-" + item.Name task.Status = models.TaskStatusWaiting - task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` task.Log = "/tmp/" + item.Slug + ".log" if err := facades.Orm().Query().Create(&task); err != nil { - facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error()) + facades.Log().Error("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误") } - c.task.Process(task.ID) + r.task.Process(task.ID) return controllers.Success(ctx, true) } @@ -279,8 +272,8 @@ func (c *Php80Controller) InstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") } -func (c *Php80Controller) UninstallExtension(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php80Controller) UninstallExtension(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } @@ -290,7 +283,7 @@ func (c *Php80Controller) UninstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") } - extensions := c.GetExtensions() + extensions := r.GetExtensions() for _, item := range extensions { if item.Slug == slug { if !item.Installed { @@ -298,16 +291,16 @@ func (c *Php80Controller) UninstallExtension(ctx http.Context) http.Response { } var task models.Task - task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name + task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name task.Status = models.TaskStatusWaiting - task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` task.Log = "/tmp/" + item.Slug + ".log" if err := facades.Orm().Query().Create(&task); err != nil { - facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error()) + facades.Log().Error("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误") } - c.task.Process(task.ID) + r.task.Process(task.ID) return controllers.Success(ctx, true) } @@ -316,46 +309,46 @@ func (c *Php80Controller) UninstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") } -func (c *Php80Controller) GetExtensions() []Extension { - var extensions []Extension - extensions = append(extensions, Extension{ +func (r *Php80Controller) GetExtensions() []PHPExtension { + var extensions []PHPExtension + extensions = append(extensions, PHPExtension{ Name: "OPcache", Slug: "Zend OPcache", Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "PhpRedis", Slug: "redis", Description: "PhpRedis 是一个用C语言编写的PHP模块,用来连接并操作 Redis 数据库上的数据。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "ImageMagick", Slug: "imagick", Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "Exif", Slug: "exif", Description: "通过 exif 扩展,你可以操作图像元数据。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "pdo_pgsql", Slug: "pdo_pgsql", Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "ionCube", Slug: "ionCube Loader", Description: "ionCube 是一个专业级的PHP加密解密工具。", Installed: false, }) - raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m") + raw := tools.Exec("/www/server/php/" + r.version + "/bin/php -m") rawExtensionList := strings.Split(raw, "\n") for _, item := range rawExtensionList { diff --git a/app/http/controllers/plugins/php81/php81_controller.go b/app/http/controllers/plugins/php81_controller.go similarity index 64% rename from app/http/controllers/plugins/php81/php81_controller.go rename to app/http/controllers/plugins/php81_controller.go index 20e11f20..bcfca570 100644 --- a/app/http/controllers/plugins/php81/php81_controller.go +++ b/app/http/controllers/plugins/php81_controller.go @@ -1,4 +1,4 @@ -package php81 +package plugins import ( "fmt" @@ -30,15 +30,15 @@ func NewPhp81Controller() *Php81Controller { } } -func (c *Php81Controller) Status(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) Status(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + status := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -48,17 +48,17 @@ func (c *Php81Controller) Status(ctx http.Context) http.Response { } } -func (c *Php81Controller) Reload(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) Reload(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl reload php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl reload php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -68,17 +68,17 @@ func (c *Php81Controller) Reload(ctx http.Context) http.Response { } } -func (c *Php81Controller) Start(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) Start(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl start php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl start php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -88,17 +88,17 @@ func (c *Php81Controller) Start(ctx http.Context) http.Response { } } -func (c *Php81Controller) Stop(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) Stop(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl stop php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl stop php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status != "active" { @@ -108,17 +108,17 @@ func (c *Php81Controller) Stop(ctx http.Context) http.Response { } } -func (c *Php81Controller) Restart(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) Restart(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl restart php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl restart php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -128,38 +128,38 @@ func (c *Php81Controller) Restart(ctx http.Context) http.Response { } } -func (c *Php81Controller) GetConfig(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) GetConfig(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini") + config := tools.Read("/www/server/php/" + r.version + "/etc/php.ini") return controllers.Success(ctx, config) } -func (c *Php81Controller) SaveConfig(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) SaveConfig(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } config := ctx.Request().Input("config") - tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644) - return c.Reload(ctx) + tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644) + return r.Reload(ctx) } -func (c *Php81Controller) Load(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) Load(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } client := req.C().SetTimeout(10 * time.Second) - resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version) + resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version) if err != nil || !resp.IsSuccessState() { - facades.Log().Error("获取PHP-" + c.version + "运行状态失败") - return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败") + facades.Log().Error("获取PHP-" + r.version + "运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败") } raw := resp.String() @@ -185,65 +185,58 @@ func (c *Php81Controller) Load(ctx http.Context) http.Response { return controllers.Success(ctx, data) } -func (c *Php81Controller) ErrorLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) ErrorLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log")) + log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log")) return controllers.Success(ctx, log) } -func (c *Php81Controller) SlowLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) SlowLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log")) + log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log")) return controllers.Success(ctx, log) } -func (c *Php81Controller) ClearErrorLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) ClearErrorLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log") + tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log") return controllers.Success(ctx, true) } -func (c *Php81Controller) ClearSlowLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) ClearSlowLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log") + tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log") return controllers.Success(ctx, true) } -type Extension struct { - Name string `json:"name"` - Slug string `json:"slug"` - Description string `json:"description"` - Installed bool `json:"installed"` -} - -func (c *Php81Controller) GetExtensionList(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) GetExtensionList(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - extensions := c.GetExtensions() + extensions := r.GetExtensions() return controllers.Success(ctx, extensions) } -func (c *Php81Controller) InstallExtension(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) InstallExtension(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } @@ -253,7 +246,7 @@ func (c *Php81Controller) InstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") } - extensions := c.GetExtensions() + extensions := r.GetExtensions() for _, item := range extensions { if item.Slug == slug { if item.Installed { @@ -261,16 +254,16 @@ func (c *Php81Controller) InstallExtension(ctx http.Context) http.Response { } var task models.Task - task.Name = "安装PHP-" + c.version + "扩展-" + item.Name + task.Name = "安装PHP-" + r.version + "扩展-" + item.Name task.Status = models.TaskStatusWaiting - task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` task.Log = "/tmp/" + item.Slug + ".log" if err := facades.Orm().Query().Create(&task); err != nil { - facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error()) + facades.Log().Error("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误") } - c.task.Process(task.ID) + r.task.Process(task.ID) return controllers.Success(ctx, true) } @@ -279,8 +272,8 @@ func (c *Php81Controller) InstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") } -func (c *Php81Controller) UninstallExtension(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php81Controller) UninstallExtension(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } @@ -290,7 +283,7 @@ func (c *Php81Controller) UninstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") } - extensions := c.GetExtensions() + extensions := r.GetExtensions() for _, item := range extensions { if item.Slug == slug { if !item.Installed { @@ -298,16 +291,16 @@ func (c *Php81Controller) UninstallExtension(ctx http.Context) http.Response { } var task models.Task - task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name + task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name task.Status = models.TaskStatusWaiting - task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` task.Log = "/tmp/" + item.Slug + ".log" if err := facades.Orm().Query().Create(&task); err != nil { - facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error()) + facades.Log().Error("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误") } - c.task.Process(task.ID) + r.task.Process(task.ID) return controllers.Success(ctx, true) } @@ -316,46 +309,46 @@ func (c *Php81Controller) UninstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") } -func (c *Php81Controller) GetExtensions() []Extension { - var extensions []Extension - extensions = append(extensions, Extension{ +func (r *Php81Controller) GetExtensions() []PHPExtension { + var extensions []PHPExtension + extensions = append(extensions, PHPExtension{ Name: "OPcache", Slug: "Zend OPcache", Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "PhpRedis", Slug: "redis", Description: "PhpRedis 是一个用C语言编写的PHP模块,用来连接并操作 Redis 数据库上的数据。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "ImageMagick", Slug: "imagick", Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "Exif", Slug: "exif", Description: "通过 exif 扩展,你可以操作图像元数据。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "pdo_pgsql", Slug: "pdo_pgsql", Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "ionCube", Slug: "ionCube Loader", Description: "ionCube 是一个专业级的PHP加密解密工具。", Installed: false, }) - raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m") + raw := tools.Exec("/www/server/php/" + r.version + "/bin/php -m") rawExtensionList := strings.Split(raw, "\n") for _, item := range rawExtensionList { diff --git a/app/http/controllers/plugins/php82/php82_controller.go b/app/http/controllers/plugins/php82_controller.go similarity index 64% rename from app/http/controllers/plugins/php82/php82_controller.go rename to app/http/controllers/plugins/php82_controller.go index bb04f5b1..f1e6dfd6 100644 --- a/app/http/controllers/plugins/php82/php82_controller.go +++ b/app/http/controllers/plugins/php82_controller.go @@ -1,4 +1,4 @@ -package php82 +package plugins import ( "fmt" @@ -30,15 +30,15 @@ func NewPhp82Controller() *Php82Controller { } } -func (c *Php82Controller) Status(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) Status(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - status := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + status := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -48,17 +48,17 @@ func (c *Php82Controller) Status(ctx http.Context) http.Response { } } -func (c *Php82Controller) Reload(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) Reload(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl reload php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl reload php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -68,17 +68,17 @@ func (c *Php82Controller) Reload(ctx http.Context) http.Response { } } -func (c *Php82Controller) Start(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) Start(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl start php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl start php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -88,17 +88,17 @@ func (c *Php82Controller) Start(ctx http.Context) http.Response { } } -func (c *Php82Controller) Stop(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) Stop(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl stop php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl stop php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status != "active" { @@ -108,17 +108,17 @@ func (c *Php82Controller) Stop(ctx http.Context) http.Response { } } -func (c *Php82Controller) Restart(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) Restart(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("systemctl restart php-fpm-" + c.version) - out := tools.Exec("systemctl status php-fpm-" + c.version + " | grep Active | grep -v grep | awk '{print $2}'") + tools.Exec("systemctl restart php-fpm-" + r.version) + out := tools.Exec("systemctl status php-fpm-" + r.version + " | grep Active | grep -v grep | awk '{print $2}'") status := strings.TrimSpace(out) if len(status) == 0 { - return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+c.version+"运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") } if status == "active" { @@ -128,38 +128,38 @@ func (c *Php82Controller) Restart(ctx http.Context) http.Response { } } -func (c *Php82Controller) GetConfig(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) GetConfig(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - config := tools.Read("/www/server/php/" + c.version + "/etc/php.ini") + config := tools.Read("/www/server/php/" + r.version + "/etc/php.ini") return controllers.Success(ctx, config) } -func (c *Php82Controller) SaveConfig(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) SaveConfig(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } config := ctx.Request().Input("config") - tools.Write("/www/server/php/"+c.version+"/etc/php.ini", config, 0644) - return c.Reload(ctx) + tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644) + return r.Reload(ctx) } -func (c *Php82Controller) Load(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) Load(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } client := req.C().SetTimeout(10 * time.Second) - resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version) + resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version) if err != nil || !resp.IsSuccessState() { - facades.Log().Error("获取PHP-" + c.version + "运行状态失败") - return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败") + facades.Log().Error("获取PHP-" + r.version + "运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败") } raw := resp.String() @@ -185,65 +185,58 @@ func (c *Php82Controller) Load(ctx http.Context) http.Response { return controllers.Success(ctx, data) } -func (c *Php82Controller) ErrorLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) ErrorLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/php-fpm.log")) + log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log")) return controllers.Success(ctx, log) } -func (c *Php82Controller) SlowLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) SlowLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + c.version + "/var/log/slow.log")) + log := tools.Escape(tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log")) return controllers.Success(ctx, log) } -func (c *Php82Controller) ClearErrorLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) ClearErrorLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/php-fpm.log") + tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log") return controllers.Success(ctx, true) } -func (c *Php82Controller) ClearSlowLog(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) ClearSlowLog(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - tools.Exec("echo '' > /www/server/php/" + c.version + "/var/log/slow.log") + tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log") return controllers.Success(ctx, true) } -type Extension struct { - Name string `json:"name"` - Slug string `json:"slug"` - Description string `json:"description"` - Installed bool `json:"installed"` -} - -func (c *Php82Controller) GetExtensionList(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) GetExtensionList(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } - extensions := c.GetExtensions() + extensions := r.GetExtensions() return controllers.Success(ctx, extensions) } -func (c *Php82Controller) InstallExtension(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) InstallExtension(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } @@ -253,7 +246,7 @@ func (c *Php82Controller) InstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") } - extensions := c.GetExtensions() + extensions := r.GetExtensions() for _, item := range extensions { if item.Slug == slug { if item.Installed { @@ -261,16 +254,16 @@ func (c *Php82Controller) InstallExtension(ctx http.Context) http.Response { } var task models.Task - task.Name = "安装PHP-" + c.version + "扩展-" + item.Name + task.Name = "安装PHP-" + r.version + "扩展-" + item.Name task.Status = models.TaskStatusWaiting - task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` task.Log = "/tmp/" + item.Slug + ".log" if err := facades.Orm().Query().Create(&task); err != nil { - facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error()) + facades.Log().Error("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误") } - c.task.Process(task.ID) + r.task.Process(task.ID) return controllers.Success(ctx, true) } @@ -279,8 +272,8 @@ func (c *Php82Controller) InstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") } -func (c *Php82Controller) UninstallExtension(ctx http.Context) http.Response { - check := controllers.Check(ctx, "php"+c.version) +func (r *Php82Controller) UninstallExtension(ctx http.Context) http.Response { + check := controllers.Check(ctx, "php"+r.version) if check != nil { return check } @@ -290,7 +283,7 @@ func (c *Php82Controller) UninstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") } - extensions := c.GetExtensions() + extensions := r.GetExtensions() for _, item := range extensions { if item.Slug == slug { if !item.Installed { @@ -298,16 +291,16 @@ func (c *Php82Controller) UninstallExtension(ctx http.Context) http.Response { } var task models.Task - task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name + task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name task.Status = models.TaskStatusWaiting - task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` task.Log = "/tmp/" + item.Slug + ".log" if err := facades.Orm().Query().Create(&task); err != nil { - facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error()) + facades.Log().Error("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "系统内部错误") } - c.task.Process(task.ID) + r.task.Process(task.ID) return controllers.Success(ctx, true) } @@ -316,40 +309,40 @@ func (c *Php82Controller) UninstallExtension(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") } -func (c *Php82Controller) GetExtensions() []Extension { - var extensions []Extension - extensions = append(extensions, Extension{ +func (r *Php82Controller) GetExtensions() []PHPExtension { + var extensions []PHPExtension + extensions = append(extensions, PHPExtension{ Name: "OPcache", Slug: "Zend OPcache", Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "PhpRedis", Slug: "redis", Description: "PhpRedis 是一个用C语言编写的PHP模块,用来连接并操作 Redis 数据库上的数据。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "ImageMagick", Slug: "imagick", Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "Exif", Slug: "exif", Description: "通过 exif 扩展,你可以操作图像元数据。", Installed: false, }) - extensions = append(extensions, Extension{ + extensions = append(extensions, PHPExtension{ Name: "pdo_pgsql", Slug: "pdo_pgsql", Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。", Installed: false, }) - raw := tools.Exec("/www/server/php/" + c.version + "/bin/php -m") + raw := tools.Exec("/www/server/php/" + r.version + "/bin/php -m") rawExtensionList := strings.Split(raw, "\n") for _, item := range rawExtensionList { diff --git a/app/http/controllers/plugins/phpmyadmin/phpmyadmin_controller.go b/app/http/controllers/plugins/phpmyadmin_controller.go similarity index 92% rename from app/http/controllers/plugins/phpmyadmin/phpmyadmin_controller.go rename to app/http/controllers/plugins/phpmyadmin_controller.go index a4a0a6dd..524e3f1d 100644 --- a/app/http/controllers/plugins/phpmyadmin/phpmyadmin_controller.go +++ b/app/http/controllers/plugins/phpmyadmin_controller.go @@ -1,4 +1,4 @@ -package phpmyadmin +package plugins import ( "os" @@ -19,7 +19,7 @@ func NewPhpMyAdminController() *PhpMyAdminController { return &PhpMyAdminController{} } -func (c *PhpMyAdminController) Info(ctx http.Context) http.Response { +func (r *PhpMyAdminController) Info(ctx http.Context) http.Response { check := controllers.Check(ctx, "phpmyadmin") if check != nil { return check @@ -52,7 +52,7 @@ func (c *PhpMyAdminController) Info(ctx http.Context) http.Response { }) } -func (c *PhpMyAdminController) SetPort(ctx http.Context) http.Response { +func (r *PhpMyAdminController) SetPort(ctx http.Context) http.Response { check := controllers.Check(ctx, "phpmyadmin") if check != nil { return check diff --git a/app/http/controllers/plugins/postgresql15/postgresql15_controller.go b/app/http/controllers/plugins/postgresql15_controller.go similarity index 89% rename from app/http/controllers/plugins/postgresql15/postgresql15_controller.go rename to app/http/controllers/plugins/postgresql15_controller.go index 1510ffbe..6f8a8c83 100644 --- a/app/http/controllers/plugins/postgresql15/postgresql15_controller.go +++ b/app/http/controllers/plugins/postgresql15_controller.go @@ -1,4 +1,4 @@ -package postgresql15 +package plugins import ( "strings" @@ -18,11 +18,6 @@ type Postgresql15Controller struct { backup services.Backup } -type Info struct { - Name string `json:"name"` - Value string `json:"value"` -} - func NewPostgresql15Controller() *Postgresql15Controller { return &Postgresql15Controller{ setting: services.NewSettingImpl(), @@ -31,7 +26,7 @@ func NewPostgresql15Controller() *Postgresql15Controller { } // Status 获取运行状态 -func (c *Postgresql15Controller) Status(ctx http.Context) http.Response { +func (r *Postgresql15Controller) Status(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -50,7 +45,7 @@ func (c *Postgresql15Controller) Status(ctx http.Context) http.Response { } // Reload 重载配置 -func (c *Postgresql15Controller) Reload(ctx http.Context) http.Response { +func (r *Postgresql15Controller) Reload(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -70,7 +65,7 @@ func (c *Postgresql15Controller) Reload(ctx http.Context) http.Response { } // Restart 重启服务 -func (c *Postgresql15Controller) Restart(ctx http.Context) http.Response { +func (r *Postgresql15Controller) Restart(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -90,7 +85,7 @@ func (c *Postgresql15Controller) Restart(ctx http.Context) http.Response { } // Start 启动服务 -func (c *Postgresql15Controller) Start(ctx http.Context) http.Response { +func (r *Postgresql15Controller) Start(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -110,7 +105,7 @@ func (c *Postgresql15Controller) Start(ctx http.Context) http.Response { } // Stop 停止服务 -func (c *Postgresql15Controller) Stop(ctx http.Context) http.Response { +func (r *Postgresql15Controller) Stop(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -130,7 +125,7 @@ func (c *Postgresql15Controller) Stop(ctx http.Context) http.Response { } // GetConfig 获取配置 -func (c *Postgresql15Controller) GetConfig(ctx http.Context) http.Response { +func (r *Postgresql15Controller) GetConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -146,7 +141,7 @@ func (c *Postgresql15Controller) GetConfig(ctx http.Context) http.Response { } // GetUserConfig 获取用户配置 -func (c *Postgresql15Controller) GetUserConfig(ctx http.Context) http.Response { +func (r *Postgresql15Controller) GetUserConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -162,7 +157,7 @@ func (c *Postgresql15Controller) GetUserConfig(ctx http.Context) http.Response { } // SaveConfig 保存配置 -func (c *Postgresql15Controller) SaveConfig(ctx http.Context) http.Response { +func (r *Postgresql15Controller) SaveConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -177,11 +172,11 @@ func (c *Postgresql15Controller) SaveConfig(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败") } - return c.Restart(ctx) + return r.Restart(ctx) } // SaveUserConfig 保存用户配置 -func (c *Postgresql15Controller) SaveUserConfig(ctx http.Context) http.Response { +func (r *Postgresql15Controller) SaveUserConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -196,11 +191,11 @@ func (c *Postgresql15Controller) SaveUserConfig(ctx http.Context) http.Response return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败") } - return c.Restart(ctx) + return r.Restart(ctx) } // Load 获取负载 -func (c *Postgresql15Controller) Load(ctx http.Context) http.Response { +func (r *Postgresql15Controller) Load(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -211,7 +206,7 @@ func (c *Postgresql15Controller) Load(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行") } - data := []Info{ + data := []LoadInfo{ {"启动时间", carbon.Parse(tools.Exec(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)).ToDateTimeString()}, {"进程 PID", tools.Exec(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)}, {"进程数", tools.Exec(`ps aux | grep postgres | grep -v grep | wc -l`)}, @@ -223,7 +218,7 @@ func (c *Postgresql15Controller) Load(ctx http.Context) http.Response { } // Log 获取日志 -func (c *Postgresql15Controller) Log(ctx http.Context) http.Response { +func (r *Postgresql15Controller) Log(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -234,7 +229,7 @@ func (c *Postgresql15Controller) Log(ctx http.Context) http.Response { } // ClearLog 清空日志 -func (c *Postgresql15Controller) ClearLog(ctx http.Context) http.Response { +func (r *Postgresql15Controller) ClearLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -245,7 +240,7 @@ func (c *Postgresql15Controller) ClearLog(ctx http.Context) http.Response { } // DatabaseList 获取数据库列表 -func (c *Postgresql15Controller) DatabaseList(ctx http.Context) http.Response { +func (r *Postgresql15Controller) DatabaseList(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -309,7 +304,7 @@ func (c *Postgresql15Controller) DatabaseList(ctx http.Context) http.Response { } // AddDatabase 添加数据库 -func (c *Postgresql15Controller) AddDatabase(ctx http.Context) http.Response { +func (r *Postgresql15Controller) AddDatabase(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -339,11 +334,11 @@ func (c *Postgresql15Controller) AddDatabase(ctx http.Context) http.Response { userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256" tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`) - return c.Reload(ctx) + return r.Reload(ctx) } // DeleteDatabase 删除数据库 -func (c *Postgresql15Controller) DeleteDatabase(ctx http.Context) http.Response { +func (r *Postgresql15Controller) DeleteDatabase(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -366,13 +361,13 @@ func (c *Postgresql15Controller) DeleteDatabase(ctx http.Context) http.Response } // BackupList 获取备份列表 -func (c *Postgresql15Controller) BackupList(ctx http.Context) http.Response { +func (r *Postgresql15Controller) BackupList(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check } - backupList, err := c.backup.PostgresqlList() + backupList, err := r.backup.PostgresqlList() if err != nil { facades.Log().Error("[PostgreSQL] 获取备份列表失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败") @@ -403,7 +398,7 @@ func (c *Postgresql15Controller) BackupList(ctx http.Context) http.Response { } // UploadBackup 上传备份 -func (c *Postgresql15Controller) UploadBackup(ctx http.Context) http.Response { +func (r *Postgresql15Controller) UploadBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -414,7 +409,7 @@ func (c *Postgresql15Controller) UploadBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败") } - backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql" + backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql" if !tools.Exists(backupPath) { tools.Mkdir(backupPath, 0644) } @@ -429,7 +424,7 @@ func (c *Postgresql15Controller) UploadBackup(ctx http.Context) http.Response { } // CreateBackup 创建备份 -func (c *Postgresql15Controller) CreateBackup(ctx http.Context) http.Response { +func (r *Postgresql15Controller) CreateBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -446,7 +441,7 @@ func (c *Postgresql15Controller) CreateBackup(ctx http.Context) http.Response { } database := ctx.Request().Input("database") - err = c.backup.PostgresqlBackup(database) + err = r.backup.PostgresqlBackup(database) if err != nil { facades.Log().Error("[PostgreSQL] 创建备份失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败") @@ -456,7 +451,7 @@ func (c *Postgresql15Controller) CreateBackup(ctx http.Context) http.Response { } // DeleteBackup 删除备份 -func (c *Postgresql15Controller) DeleteBackup(ctx http.Context) http.Response { +func (r *Postgresql15Controller) DeleteBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -472,7 +467,7 @@ func (c *Postgresql15Controller) DeleteBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql" + backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql" fileName := ctx.Request().Input("name") tools.Remove(backupPath + "/" + fileName) @@ -480,7 +475,7 @@ func (c *Postgresql15Controller) DeleteBackup(ctx http.Context) http.Response { } // RestoreBackup 还原备份 -func (c *Postgresql15Controller) RestoreBackup(ctx http.Context) http.Response { +func (r *Postgresql15Controller) RestoreBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -497,7 +492,7 @@ func (c *Postgresql15Controller) RestoreBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - err = c.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")) + err = r.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")) if err != nil { facades.Log().Error("[PostgreSQL] 还原失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error()) @@ -507,7 +502,7 @@ func (c *Postgresql15Controller) RestoreBackup(ctx http.Context) http.Response { } // UserList 用户列表 -func (c *Postgresql15Controller) UserList(ctx http.Context) http.Response { +func (r *Postgresql15Controller) UserList(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -560,7 +555,7 @@ func (c *Postgresql15Controller) UserList(ctx http.Context) http.Response { } // AddUser 添加用户 -func (c *Postgresql15Controller) AddUser(ctx http.Context) http.Response { +func (r *Postgresql15Controller) AddUser(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -587,11 +582,11 @@ func (c *Postgresql15Controller) AddUser(ctx http.Context) http.Response { userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256" tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`) - return c.Reload(ctx) + return r.Reload(ctx) } // DeleteUser 删除用户 -func (c *Postgresql15Controller) DeleteUser(ctx http.Context) http.Response { +func (r *Postgresql15Controller) DeleteUser(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check @@ -611,11 +606,11 @@ func (c *Postgresql15Controller) DeleteUser(ctx http.Context) http.Response { tools.Exec(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`) tools.Exec(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`) - return c.Reload(ctx) + return r.Reload(ctx) } // SetUserPassword 设置用户密码 -func (c *Postgresql15Controller) SetUserPassword(ctx http.Context) http.Response { +func (r *Postgresql15Controller) SetUserPassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql15") if check != nil { return check diff --git a/app/http/controllers/plugins/postgresql16/postgresql16_controller.go b/app/http/controllers/plugins/postgresql16_controller.go similarity index 89% rename from app/http/controllers/plugins/postgresql16/postgresql16_controller.go rename to app/http/controllers/plugins/postgresql16_controller.go index 46f72386..4611c246 100644 --- a/app/http/controllers/plugins/postgresql16/postgresql16_controller.go +++ b/app/http/controllers/plugins/postgresql16_controller.go @@ -1,4 +1,4 @@ -package postgresql16 +package plugins import ( "strings" @@ -18,11 +18,6 @@ type Postgresql16Controller struct { backup services.Backup } -type Info struct { - Name string `json:"name"` - Value string `json:"value"` -} - func NewPostgresql16Controller() *Postgresql16Controller { return &Postgresql16Controller{ setting: services.NewSettingImpl(), @@ -31,7 +26,7 @@ func NewPostgresql16Controller() *Postgresql16Controller { } // Status 获取运行状态 -func (c *Postgresql16Controller) Status(ctx http.Context) http.Response { +func (r *Postgresql16Controller) Status(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -50,7 +45,7 @@ func (c *Postgresql16Controller) Status(ctx http.Context) http.Response { } // Reload 重载配置 -func (c *Postgresql16Controller) Reload(ctx http.Context) http.Response { +func (r *Postgresql16Controller) Reload(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -70,7 +65,7 @@ func (c *Postgresql16Controller) Reload(ctx http.Context) http.Response { } // Restart 重启服务 -func (c *Postgresql16Controller) Restart(ctx http.Context) http.Response { +func (r *Postgresql16Controller) Restart(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -90,7 +85,7 @@ func (c *Postgresql16Controller) Restart(ctx http.Context) http.Response { } // Start 启动服务 -func (c *Postgresql16Controller) Start(ctx http.Context) http.Response { +func (r *Postgresql16Controller) Start(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -110,7 +105,7 @@ func (c *Postgresql16Controller) Start(ctx http.Context) http.Response { } // Stop 停止服务 -func (c *Postgresql16Controller) Stop(ctx http.Context) http.Response { +func (r *Postgresql16Controller) Stop(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -130,7 +125,7 @@ func (c *Postgresql16Controller) Stop(ctx http.Context) http.Response { } // GetConfig 获取配置 -func (c *Postgresql16Controller) GetConfig(ctx http.Context) http.Response { +func (r *Postgresql16Controller) GetConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -146,7 +141,7 @@ func (c *Postgresql16Controller) GetConfig(ctx http.Context) http.Response { } // GetUserConfig 获取用户配置 -func (c *Postgresql16Controller) GetUserConfig(ctx http.Context) http.Response { +func (r *Postgresql16Controller) GetUserConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -162,7 +157,7 @@ func (c *Postgresql16Controller) GetUserConfig(ctx http.Context) http.Response { } // SaveConfig 保存配置 -func (c *Postgresql16Controller) SaveConfig(ctx http.Context) http.Response { +func (r *Postgresql16Controller) SaveConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -177,11 +172,11 @@ func (c *Postgresql16Controller) SaveConfig(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败") } - return c.Restart(ctx) + return r.Restart(ctx) } // SaveUserConfig 保存用户配置 -func (c *Postgresql16Controller) SaveUserConfig(ctx http.Context) http.Response { +func (r *Postgresql16Controller) SaveUserConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -196,11 +191,11 @@ func (c *Postgresql16Controller) SaveUserConfig(ctx http.Context) http.Response return controllers.Error(ctx, http.StatusInternalServerError, "写入PostgreSQL配置失败") } - return c.Restart(ctx) + return r.Restart(ctx) } // Load 获取负载 -func (c *Postgresql16Controller) Load(ctx http.Context) http.Response { +func (r *Postgresql16Controller) Load(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -211,7 +206,7 @@ func (c *Postgresql16Controller) Load(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "PostgreSQL 已停止运行") } - data := []Info{ + data := []LoadInfo{ {"启动时间", carbon.Parse(tools.Exec(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)).ToDateTimeString()}, {"进程 PID", tools.Exec(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)}, {"进程数", tools.Exec(`ps aux | grep postgres | grep -v grep | wc -l`)}, @@ -223,7 +218,7 @@ func (c *Postgresql16Controller) Load(ctx http.Context) http.Response { } // Log 获取日志 -func (c *Postgresql16Controller) Log(ctx http.Context) http.Response { +func (r *Postgresql16Controller) Log(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -234,7 +229,7 @@ func (c *Postgresql16Controller) Log(ctx http.Context) http.Response { } // ClearLog 清空日志 -func (c *Postgresql16Controller) ClearLog(ctx http.Context) http.Response { +func (r *Postgresql16Controller) ClearLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -245,7 +240,7 @@ func (c *Postgresql16Controller) ClearLog(ctx http.Context) http.Response { } // DatabaseList 获取数据库列表 -func (c *Postgresql16Controller) DatabaseList(ctx http.Context) http.Response { +func (r *Postgresql16Controller) DatabaseList(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -309,7 +304,7 @@ func (c *Postgresql16Controller) DatabaseList(ctx http.Context) http.Response { } // AddDatabase 添加数据库 -func (c *Postgresql16Controller) AddDatabase(ctx http.Context) http.Response { +func (r *Postgresql16Controller) AddDatabase(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -339,11 +334,11 @@ func (c *Postgresql16Controller) AddDatabase(ctx http.Context) http.Response { userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256" tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`) - return c.Reload(ctx) + return r.Reload(ctx) } // DeleteDatabase 删除数据库 -func (c *Postgresql16Controller) DeleteDatabase(ctx http.Context) http.Response { +func (r *Postgresql16Controller) DeleteDatabase(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -366,13 +361,13 @@ func (c *Postgresql16Controller) DeleteDatabase(ctx http.Context) http.Response } // BackupList 获取备份列表 -func (c *Postgresql16Controller) BackupList(ctx http.Context) http.Response { +func (r *Postgresql16Controller) BackupList(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check } - backupList, err := c.backup.PostgresqlList() + backupList, err := r.backup.PostgresqlList() if err != nil { facades.Log().Error("[PostgreSQL] 获取备份列表失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "获取备份列表失败") @@ -403,7 +398,7 @@ func (c *Postgresql16Controller) BackupList(ctx http.Context) http.Response { } // UploadBackup 上传备份 -func (c *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response { +func (r *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -414,7 +409,7 @@ func (c *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "上传文件失败") } - backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql" + backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql" if !tools.Exists(backupPath) { tools.Mkdir(backupPath, 0644) } @@ -429,7 +424,7 @@ func (c *Postgresql16Controller) UploadBackup(ctx http.Context) http.Response { } // CreateBackup 创建备份 -func (c *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response { +func (r *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -446,7 +441,7 @@ func (c *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response { } database := ctx.Request().Input("database") - err = c.backup.PostgresqlBackup(database) + err = r.backup.PostgresqlBackup(database) if err != nil { facades.Log().Error("[PostgreSQL] 创建备份失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "创建备份失败") @@ -456,7 +451,7 @@ func (c *Postgresql16Controller) CreateBackup(ctx http.Context) http.Response { } // DeleteBackup 删除备份 -func (c *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response { +func (r *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -472,7 +467,7 @@ func (c *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/postgresql" + backupPath := r.setting.Get(models.SettingKeyBackupPath) + "/postgresql" fileName := ctx.Request().Input("name") tools.Remove(backupPath + "/" + fileName) @@ -480,7 +475,7 @@ func (c *Postgresql16Controller) DeleteBackup(ctx http.Context) http.Response { } // RestoreBackup 还原备份 -func (c *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response { +func (r *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -497,7 +492,7 @@ func (c *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One()) } - err = c.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")) + err = r.backup.PostgresqlRestore(ctx.Request().Input("database"), ctx.Request().Input("backup")) if err != nil { facades.Log().Error("[PostgreSQL] 还原失败:" + err.Error()) return controllers.Error(ctx, http.StatusInternalServerError, "还原失败: "+err.Error()) @@ -507,7 +502,7 @@ func (c *Postgresql16Controller) RestoreBackup(ctx http.Context) http.Response { } // UserList 用户列表 -func (c *Postgresql16Controller) UserList(ctx http.Context) http.Response { +func (r *Postgresql16Controller) UserList(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -560,7 +555,7 @@ func (c *Postgresql16Controller) UserList(ctx http.Context) http.Response { } // AddUser 添加用户 -func (c *Postgresql16Controller) AddUser(ctx http.Context) http.Response { +func (r *Postgresql16Controller) AddUser(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -587,11 +582,11 @@ func (c *Postgresql16Controller) AddUser(ctx http.Context) http.Response { userConfig := "host " + database + " " + user + " 127.0.0.1/32 scram-sha-256" tools.Exec(`echo "` + userConfig + `" >> /www/server/postgresql/data/pg_hba.conf`) - return c.Reload(ctx) + return r.Reload(ctx) } // DeleteUser 删除用户 -func (c *Postgresql16Controller) DeleteUser(ctx http.Context) http.Response { +func (r *Postgresql16Controller) DeleteUser(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check @@ -611,11 +606,11 @@ func (c *Postgresql16Controller) DeleteUser(ctx http.Context) http.Response { tools.Exec(`echo "DROP USER ` + user + `;" | su - postgres -c "psql"`) tools.Exec(`sed -i '/` + user + `/d' /www/server/postgresql/data/pg_hba.conf`) - return c.Reload(ctx) + return r.Reload(ctx) } // SetUserPassword 设置用户密码 -func (c *Postgresql16Controller) SetUserPassword(ctx http.Context) http.Response { +func (r *Postgresql16Controller) SetUserPassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "postgresql16") if check != nil { return check diff --git a/app/http/controllers/plugins/pureftpd/pureftpd_controller.go b/app/http/controllers/plugins/pureftpd_controller.go similarity index 91% rename from app/http/controllers/plugins/pureftpd/pureftpd_controller.go rename to app/http/controllers/plugins/pureftpd_controller.go index 905e6f2a..64131ec0 100644 --- a/app/http/controllers/plugins/pureftpd/pureftpd_controller.go +++ b/app/http/controllers/plugins/pureftpd_controller.go @@ -1,4 +1,4 @@ -package pureftpd +package plugins import ( "regexp" @@ -24,7 +24,7 @@ func NewPureFtpdController() *PureFtpdController { } // Status 获取运行状态 -func (c *PureFtpdController) Status(ctx http.Context) http.Response { +func (r *PureFtpdController) Status(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check @@ -43,7 +43,7 @@ func (c *PureFtpdController) Status(ctx http.Context) http.Response { } // Restart 重启服务 -func (c *PureFtpdController) Restart(ctx http.Context) http.Response { +func (r *PureFtpdController) Restart(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check @@ -63,7 +63,7 @@ func (c *PureFtpdController) Restart(ctx http.Context) http.Response { } // Start 启动服务 -func (c *PureFtpdController) Start(ctx http.Context) http.Response { +func (r *PureFtpdController) Start(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check @@ -83,7 +83,7 @@ func (c *PureFtpdController) Start(ctx http.Context) http.Response { } // Stop 停止服务 -func (c *PureFtpdController) Stop(ctx http.Context) http.Response { +func (r *PureFtpdController) Stop(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check @@ -103,7 +103,7 @@ func (c *PureFtpdController) Stop(ctx http.Context) http.Response { } // List 获取用户列表 -func (c *PureFtpdController) List(ctx http.Context) http.Response { +func (r *PureFtpdController) List(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check @@ -153,7 +153,7 @@ func (c *PureFtpdController) List(ctx http.Context) http.Response { } // Add 添加用户 -func (c *PureFtpdController) Add(ctx http.Context) http.Response { +func (r *PureFtpdController) Add(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check @@ -191,7 +191,7 @@ func (c *PureFtpdController) Add(ctx http.Context) http.Response { } // Delete 删除用户 -func (c *PureFtpdController) Delete(ctx http.Context) http.Response { +func (r *PureFtpdController) Delete(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check @@ -216,7 +216,7 @@ func (c *PureFtpdController) Delete(ctx http.Context) http.Response { } // ChangePassword 修改密码 -func (c *PureFtpdController) ChangePassword(ctx http.Context) http.Response { +func (r *PureFtpdController) ChangePassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check @@ -243,7 +243,7 @@ func (c *PureFtpdController) ChangePassword(ctx http.Context) http.Response { } // GetPort 获取端口 -func (c *PureFtpdController) GetPort(ctx http.Context) http.Response { +func (r *PureFtpdController) GetPort(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check @@ -258,7 +258,7 @@ func (c *PureFtpdController) GetPort(ctx http.Context) http.Response { } // SetPort 设置端口 -func (c *PureFtpdController) SetPort(ctx http.Context) http.Response { +func (r *PureFtpdController) SetPort(ctx http.Context) http.Response { check := controllers.Check(ctx, "pureftpd") if check != nil { return check diff --git a/app/http/controllers/plugins/redis/redis_controller.go b/app/http/controllers/plugins/redis_controller.go similarity index 88% rename from app/http/controllers/plugins/redis/redis_controller.go rename to app/http/controllers/plugins/redis_controller.go index 6fb3e082..5aa75e47 100644 --- a/app/http/controllers/plugins/redis/redis_controller.go +++ b/app/http/controllers/plugins/redis_controller.go @@ -1,4 +1,4 @@ -package redis +package plugins import ( "strings" @@ -12,17 +12,12 @@ import ( type RedisController struct { } -type Info struct { - Name string `json:"name"` - Value string `json:"value"` -} - func NewRedisController() *RedisController { return &RedisController{} } // Status 获取运行状态 -func (c *RedisController) Status(ctx http.Context) http.Response { +func (r *RedisController) Status(ctx http.Context) http.Response { check := controllers.Check(ctx, "redis") if check != nil { return check @@ -41,7 +36,7 @@ func (c *RedisController) Status(ctx http.Context) http.Response { } // Restart 重启服务 -func (c *RedisController) Restart(ctx http.Context) http.Response { +func (r *RedisController) Restart(ctx http.Context) http.Response { check := controllers.Check(ctx, "redis") if check != nil { return check @@ -61,7 +56,7 @@ func (c *RedisController) Restart(ctx http.Context) http.Response { } // Start 启动服务 -func (c *RedisController) Start(ctx http.Context) http.Response { +func (r *RedisController) Start(ctx http.Context) http.Response { check := controllers.Check(ctx, "redis") if check != nil { return check @@ -81,7 +76,7 @@ func (c *RedisController) Start(ctx http.Context) http.Response { } // Stop 停止服务 -func (c *RedisController) Stop(ctx http.Context) http.Response { +func (r *RedisController) Stop(ctx http.Context) http.Response { check := controllers.Check(ctx, "redis") if check != nil { return check @@ -101,7 +96,7 @@ func (c *RedisController) Stop(ctx http.Context) http.Response { } // GetConfig 获取配置 -func (c *RedisController) GetConfig(ctx http.Context) http.Response { +func (r *RedisController) GetConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "redis") if check != nil { return check @@ -117,7 +112,7 @@ func (c *RedisController) GetConfig(ctx http.Context) http.Response { } // SaveConfig 保存配置 -func (c *RedisController) SaveConfig(ctx http.Context) http.Response { +func (r *RedisController) SaveConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "redis") if check != nil { return check @@ -132,11 +127,11 @@ func (c *RedisController) SaveConfig(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "写入Redis配置失败") } - return c.Restart(ctx) + return r.Restart(ctx) } // Load 获取负载 -func (c *RedisController) Load(ctx http.Context) http.Response { +func (r *RedisController) Load(ctx http.Context) http.Response { check := controllers.Check(ctx, "redis") if check != nil { return check @@ -162,7 +157,7 @@ func (c *RedisController) Load(ctx http.Context) http.Response { } } - data := []Info{ + data := []LoadInfo{ {"TCP 端口", dataRaw["tcp_port"]}, {"已运行天数", dataRaw["uptime_in_days"]}, {"连接的客户端数", dataRaw["connected_clients"]}, diff --git a/app/http/controllers/plugins/s3fs/s3fs_controller.go b/app/http/controllers/plugins/s3fs_controller.go similarity index 86% rename from app/http/controllers/plugins/s3fs/s3fs_controller.go rename to app/http/controllers/plugins/s3fs_controller.go index 032e4cb3..74340a17 100644 --- a/app/http/controllers/plugins/s3fs/s3fs_controller.go +++ b/app/http/controllers/plugins/s3fs_controller.go @@ -1,4 +1,4 @@ -package s3fs +package plugins import ( "strings" @@ -17,13 +17,6 @@ type S3fsController struct { setting services.Setting } -type s3fs struct { - ID int64 `json:"id"` - Path string `json:"path"` - Bucket string `json:"bucket"` - Url string `json:"url"` -} - func NewS3fsController() *S3fsController { return &S3fsController{ setting: services.NewSettingImpl(), @@ -31,7 +24,7 @@ func NewS3fsController() *S3fsController { } // List 所有 S3fs 挂载 -func (c *S3fsController) List(ctx http.Context) http.Response { +func (r *S3fsController) List(ctx http.Context) http.Response { check := controllers.Check(ctx, "s3fs") if check != nil { return check @@ -40,8 +33,8 @@ func (c *S3fsController) List(ctx http.Context) http.Response { page := ctx.Request().QueryInt("page", 1) limit := ctx.Request().QueryInt("limit", 10) - var s3fsList []s3fs - err := json.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList) + var s3fsList []S3fsMount + err := json.UnmarshalString(r.setting.Get("s3fs", "[]"), &s3fsList) if err != nil { return controllers.Error(ctx, http.StatusUnprocessableEntity, "获取 S3fs 挂载失败") } @@ -51,7 +44,7 @@ func (c *S3fsController) List(ctx http.Context) http.Response { if startIndex > len(s3fsList) { return controllers.Success(ctx, http.Json{ "total": 0, - "items": []s3fs{}, + "items": []S3fsMount{}, }) } if endIndex > len(s3fsList) { @@ -59,7 +52,7 @@ func (c *S3fsController) List(ctx http.Context) http.Response { } pagedS3fsList := s3fsList[startIndex:endIndex] if pagedS3fsList == nil { - pagedS3fsList = []s3fs{} + pagedS3fsList = []S3fsMount{} } return controllers.Success(ctx, http.Json{ @@ -69,7 +62,7 @@ func (c *S3fsController) List(ctx http.Context) http.Response { } // Add 添加 S3fs 挂载 -func (c *S3fsController) Add(ctx http.Context) http.Response { +func (r *S3fsController) Add(ctx http.Context) http.Response { check := controllers.Check(ctx, "s3fs") if check != nil { return check @@ -108,8 +101,8 @@ func (c *S3fsController) Add(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载目录必须为空") } - var s3fsList []s3fs - err = json.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList) + var s3fsList []S3fsMount + err = json.UnmarshalString(r.setting.Get("s3fs", "[]"), &s3fsList) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败") } @@ -135,7 +128,7 @@ func (c *S3fsController) Add(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusInternalServerError, "挂载失败,请检查配置是否正确") } - s3fsList = append(s3fsList, s3fs{ + s3fsList = append(s3fsList, S3fsMount{ ID: id, Path: path, Bucket: bucket, @@ -145,7 +138,7 @@ func (c *S3fsController) Add(ctx http.Context) http.Response { if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败") } - err = c.setting.Set("s3fs", encoded) + err = r.setting.Set("s3fs", encoded) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "添加 S3fs 挂载失败") } @@ -154,7 +147,7 @@ func (c *S3fsController) Add(ctx http.Context) http.Response { } // Delete 删除 S3fs 挂载 -func (c *S3fsController) Delete(ctx http.Context) http.Response { +func (r *S3fsController) Delete(ctx http.Context) http.Response { check := controllers.Check(ctx, "s3fs") if check != nil { return check @@ -165,13 +158,13 @@ func (c *S3fsController) Delete(ctx http.Context) http.Response { return controllers.Error(ctx, http.StatusUnprocessableEntity, "挂载ID不能为空") } - var s3fsList []s3fs - err := json.UnmarshalString(c.setting.Get("s3fs", "[]"), &s3fsList) + var s3fsList []S3fsMount + err := json.UnmarshalString(r.setting.Get("s3fs", "[]"), &s3fsList) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "获取 S3fs 挂载失败") } - var mount s3fs + var mount S3fsMount for _, s := range s3fsList { if cast.ToString(s.ID) == id { mount = s @@ -191,7 +184,7 @@ func (c *S3fsController) Delete(ctx http.Context) http.Response { } tools.Remove("/etc/passwd-s3fs-" + cast.ToString(mount.ID)) - var newS3fsList []s3fs + var newS3fsList []S3fsMount for _, s := range s3fsList { if s.ID != mount.ID { newS3fsList = append(newS3fsList, s) @@ -201,7 +194,7 @@ func (c *S3fsController) Delete(ctx http.Context) http.Response { if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败") } - err = c.setting.Set("s3fs", encoded) + err = r.setting.Set("s3fs", encoded) if err != nil { return controllers.Error(ctx, http.StatusInternalServerError, "删除 S3fs 挂载失败") } diff --git a/app/http/controllers/plugins/supervisor/supervisor_controller.go b/app/http/controllers/plugins/supervisor_controller.go similarity index 84% rename from app/http/controllers/plugins/supervisor/supervisor_controller.go rename to app/http/controllers/plugins/supervisor_controller.go index ab31c382..32d0aa76 100644 --- a/app/http/controllers/plugins/supervisor/supervisor_controller.go +++ b/app/http/controllers/plugins/supervisor_controller.go @@ -1,4 +1,4 @@ -package supervisor +package plugins import ( "strconv" @@ -28,13 +28,13 @@ func NewSupervisorController() *SupervisorController { } // Status 状态 -func (c *SupervisorController) Status(ctx http.Context) http.Response { +func (r *SupervisorController) Status(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check } - status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) + status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) if status == "active" { return controllers.Success(ctx, true) } else { @@ -43,14 +43,14 @@ func (c *SupervisorController) Status(ctx http.Context) http.Response { } // Start 启动 -func (c *SupervisorController) Start(ctx http.Context) http.Response { +func (r *SupervisorController) Start(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check } - tools.Exec(`systemctl start ` + c.ServiceName) - status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) + tools.Exec(`systemctl start ` + r.ServiceName) + status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) if status == "active" { return controllers.Success(ctx, true) } else { @@ -59,14 +59,14 @@ func (c *SupervisorController) Start(ctx http.Context) http.Response { } // Stop 停止 -func (c *SupervisorController) Stop(ctx http.Context) http.Response { +func (r *SupervisorController) Stop(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check } - tools.Exec(`systemctl stop ` + c.ServiceName) - status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) + tools.Exec(`systemctl stop ` + r.ServiceName) + status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) if status != "active" { return controllers.Success(ctx, true) } else { @@ -75,14 +75,14 @@ func (c *SupervisorController) Stop(ctx http.Context) http.Response { } // Restart 重启 -func (c *SupervisorController) Restart(ctx http.Context) http.Response { +func (r *SupervisorController) Restart(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check } - tools.Exec(`systemctl restart ` + c.ServiceName) - status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) + tools.Exec(`systemctl restart ` + r.ServiceName) + status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) if status == "active" { return controllers.Success(ctx, true) } else { @@ -91,14 +91,14 @@ func (c *SupervisorController) Restart(ctx http.Context) http.Response { } // Reload 重载 -func (c *SupervisorController) Reload(ctx http.Context) http.Response { +func (r *SupervisorController) Reload(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check } - tools.Exec(`systemctl reload ` + c.ServiceName) - status := tools.Exec(`systemctl status ` + c.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) + tools.Exec(`systemctl reload ` + r.ServiceName) + status := tools.Exec(`systemctl status ` + r.ServiceName + ` | grep Active | grep -v grep | awk '{print $2}'`) if status == "active" { return controllers.Success(ctx, true) } else { @@ -107,7 +107,7 @@ func (c *SupervisorController) Reload(ctx http.Context) http.Response { } // Log 日志 -func (c *SupervisorController) Log(ctx http.Context) http.Response { +func (r *SupervisorController) Log(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -118,7 +118,7 @@ func (c *SupervisorController) Log(ctx http.Context) http.Response { } // ClearLog 清空日志 -func (c *SupervisorController) ClearLog(ctx http.Context) http.Response { +func (r *SupervisorController) ClearLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -129,7 +129,7 @@ func (c *SupervisorController) ClearLog(ctx http.Context) http.Response { } // Config 获取配置 -func (c *SupervisorController) Config(ctx http.Context) http.Response { +func (r *SupervisorController) Config(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -145,7 +145,7 @@ func (c *SupervisorController) Config(ctx http.Context) http.Response { } // SaveConfig 保存配置 -func (c *SupervisorController) SaveConfig(ctx http.Context) http.Response { +func (r *SupervisorController) SaveConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -158,11 +158,11 @@ func (c *SupervisorController) SaveConfig(ctx http.Context) http.Response { tools.Write(`/etc/supervisor/supervisord.conf`, config, 0644) } - return c.Restart(ctx) + return r.Restart(ctx) } // Processes 进程列表 -func (c *SupervisorController) Processes(ctx http.Context) http.Response { +func (r *SupervisorController) Processes(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -221,7 +221,7 @@ func (c *SupervisorController) Processes(ctx http.Context) http.Response { } // StartProcess 启动进程 -func (c *SupervisorController) StartProcess(ctx http.Context) http.Response { +func (r *SupervisorController) StartProcess(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -233,7 +233,7 @@ func (c *SupervisorController) StartProcess(ctx http.Context) http.Response { } // StopProcess 停止进程 -func (c *SupervisorController) StopProcess(ctx http.Context) http.Response { +func (r *SupervisorController) StopProcess(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -245,7 +245,7 @@ func (c *SupervisorController) StopProcess(ctx http.Context) http.Response { } // RestartProcess 重启进程 -func (c *SupervisorController) RestartProcess(ctx http.Context) http.Response { +func (r *SupervisorController) RestartProcess(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -257,7 +257,7 @@ func (c *SupervisorController) RestartProcess(ctx http.Context) http.Response { } // ProcessLog 进程日志 -func (c *SupervisorController) ProcessLog(ctx http.Context) http.Response { +func (r *SupervisorController) ProcessLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -276,7 +276,7 @@ func (c *SupervisorController) ProcessLog(ctx http.Context) http.Response { } // ClearProcessLog 清空进程日志 -func (c *SupervisorController) ClearProcessLog(ctx http.Context) http.Response { +func (r *SupervisorController) ClearProcessLog(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -295,7 +295,7 @@ func (c *SupervisorController) ClearProcessLog(ctx http.Context) http.Response { } // ProcessConfig 获取进程配置 -func (c *SupervisorController) ProcessConfig(ctx http.Context) http.Response { +func (r *SupervisorController) ProcessConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -313,7 +313,7 @@ func (c *SupervisorController) ProcessConfig(ctx http.Context) http.Response { } // SaveProcessConfig 保存进程配置 -func (c *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response { +func (r *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -334,7 +334,7 @@ func (c *SupervisorController) SaveProcessConfig(ctx http.Context) http.Response } // AddProcess 添加进程 -func (c *SupervisorController) AddProcess(ctx http.Context) http.Response { +func (r *SupervisorController) AddProcess(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check @@ -384,7 +384,7 @@ stdout_logfile_maxbytes=2MB } // DeleteProcess 删除进程 -func (c *SupervisorController) DeleteProcess(ctx http.Context) http.Response { +func (r *SupervisorController) DeleteProcess(ctx http.Context) http.Response { check := controllers.Check(ctx, "supervisor") if check != nil { return check diff --git a/app/http/controllers/plugins/toolbox/toolbox.go b/app/http/controllers/plugins/toolbox_controller.go similarity index 90% rename from app/http/controllers/plugins/toolbox/toolbox.go rename to app/http/controllers/plugins/toolbox_controller.go index 1562ee61..786aad20 100644 --- a/app/http/controllers/plugins/toolbox/toolbox.go +++ b/app/http/controllers/plugins/toolbox_controller.go @@ -1,4 +1,4 @@ -package toolbox +package plugins import ( "regexp" @@ -19,7 +19,7 @@ func NewToolBoxController() *ToolBoxController { } // GetDNS 获取 DNS 信息 -func (c *ToolBoxController) GetDNS(ctx http.Context) http.Response { +func (r *ToolBoxController) GetDNS(ctx http.Context) http.Response { check := controllers.Check(ctx, "toolbox") if check != nil { return check @@ -40,7 +40,7 @@ func (c *ToolBoxController) GetDNS(ctx http.Context) http.Response { } // SetDNS 设置 DNS 信息 -func (c *ToolBoxController) SetDNS(ctx http.Context) http.Response { +func (r *ToolBoxController) SetDNS(ctx http.Context) http.Response { check := controllers.Check(ctx, "toolbox") if check != nil { return check @@ -62,7 +62,7 @@ func (c *ToolBoxController) SetDNS(ctx http.Context) http.Response { } // GetSWAP 获取 SWAP 信息 -func (c *ToolBoxController) GetSWAP(ctx http.Context) http.Response { +func (r *ToolBoxController) GetSWAP(ctx http.Context) http.Response { check := controllers.Check(ctx, "toolbox") if check != nil { return check @@ -95,7 +95,7 @@ func (c *ToolBoxController) GetSWAP(ctx http.Context) http.Response { } // SetSWAP 设置 SWAP 信息 -func (c *ToolBoxController) SetSWAP(ctx http.Context) http.Response { +func (r *ToolBoxController) SetSWAP(ctx http.Context) http.Response { check := controllers.Check(ctx, "toolbox") if check != nil { return check @@ -130,7 +130,7 @@ func (c *ToolBoxController) SetSWAP(ctx http.Context) http.Response { } // GetTimezone 获取时区 -func (c *ToolBoxController) GetTimezone(ctx http.Context) http.Response { +func (r *ToolBoxController) GetTimezone(ctx http.Context) http.Response { check := controllers.Check(ctx, "toolbox") if check != nil { return check @@ -165,7 +165,7 @@ func (c *ToolBoxController) GetTimezone(ctx http.Context) http.Response { } // SetTimezone 设置时区 -func (c *ToolBoxController) SetTimezone(ctx http.Context) http.Response { +func (r *ToolBoxController) SetTimezone(ctx http.Context) http.Response { check := controllers.Check(ctx, "toolbox") if check != nil { return check @@ -182,7 +182,7 @@ func (c *ToolBoxController) SetTimezone(ctx http.Context) http.Response { } // GetHosts 获取 hosts 信息 -func (c *ToolBoxController) GetHosts(ctx http.Context) http.Response { +func (r *ToolBoxController) GetHosts(ctx http.Context) http.Response { check := controllers.Check(ctx, "toolbox") if check != nil { return check @@ -192,7 +192,7 @@ func (c *ToolBoxController) GetHosts(ctx http.Context) http.Response { } // SetHosts 设置 hosts 信息 -func (c *ToolBoxController) SetHosts(ctx http.Context) http.Response { +func (r *ToolBoxController) SetHosts(ctx http.Context) http.Response { check := controllers.Check(ctx, "toolbox") if check != nil { return check @@ -209,7 +209,7 @@ func (c *ToolBoxController) SetHosts(ctx http.Context) http.Response { } // SetRootPassword 设置 root 密码 -func (c *ToolBoxController) SetRootPassword(ctx http.Context) http.Response { +func (r *ToolBoxController) SetRootPassword(ctx http.Context) http.Response { check := controllers.Check(ctx, "toolbox") if check != nil { return check diff --git a/app/http/controllers/user_controller.go b/app/http/controllers/user_controller.go index b0140013..1849d099 100644 --- a/app/http/controllers/user_controller.go +++ b/app/http/controllers/user_controller.go @@ -4,7 +4,7 @@ import ( "github.com/goravel/framework/contracts/http" "github.com/goravel/framework/facades" - "panel/app/http/requests" + "panel/app/http/requests/user" "panel/app/models" ) @@ -18,22 +18,31 @@ func NewUserController() *UserController { } } -// Login 用户登录 +// Login +// @Summary 用户登录 +// @Description 通过用户名和密码获取访问令牌 +// @Tags 用户 +// @Accept json +// @Produce json +// @Param data body requests.Login true "登录信息" +// @Success 200 {object} SuccessResponse +// @Failure 403 {object} ErrorResponse "用户名或密码错误" +// @Failure 500 {object} ErrorResponse "系统内部错误 +// @Router /panel/user/login [post] func (r *UserController) Login(ctx http.Context) http.Response { - var loginRequest requests.LoginRequest - errors, err := ctx.Request().ValidateRequest(&loginRequest) - if err != nil { - return Error(ctx, http.StatusUnprocessableEntity, err.Error()) - } - if errors != nil { - return Error(ctx, http.StatusUnprocessableEntity, errors.One()) + var loginRequest requests.Login + sanitize := Sanitize(ctx, &loginRequest) + if sanitize != nil { + return sanitize } var user models.User - err = facades.Orm().Query().Where("username", loginRequest.Username).First(&user) + err := facades.Orm().Query().Where("username", loginRequest.Username).First(&user) if err != nil { - facades.Log().Error("[面板][UserController] 查询用户失败 ", err) - return Error(ctx, http.StatusInternalServerError, "系统内部错误") + facades.Log().Request(ctx.Request()).With(map[string]any{ + "error": err.Error(), + }).Tags("面板", "用户").Error("查询用户失败") + return ErrorSystem(ctx) } if user.ID == 0 || !facades.Hash().Check(loginRequest.Password, user.Password) { @@ -43,15 +52,19 @@ func (r *UserController) Login(ctx http.Context) http.Response { if facades.Hash().NeedsRehash(user.Password) { user.Password, err = facades.Hash().Make(loginRequest.Password) if err != nil { - facades.Log().Error("[面板][UserController] 更新密码失败 ", err) - return Error(ctx, http.StatusInternalServerError, "系统内部错误") + facades.Log().Request(ctx.Request()).With(map[string]any{ + "error": err.Error(), + }).Tags("面板", "用户").Error("更新密码失败") + return ErrorSystem(ctx) } } token, loginErr := facades.Auth().LoginUsingID(ctx, user.ID) if loginErr != nil { - facades.Log().Error("[面板][UserController] 登录失败 ", loginErr) - return Error(ctx, http.StatusInternalServerError, loginErr.Error()) + facades.Log().Request(ctx.Request()).With(map[string]any{ + "error": err.Error(), + }).Tags("面板", "用户").Error("登录失败") + return ErrorSystem(ctx) } return Success(ctx, http.Json{ diff --git a/app/http/requests/cert/cert_add.go b/app/http/requests/cert/cert_add.go new file mode 100644 index 00000000..7bb44dc7 --- /dev/null +++ b/app/http/requests/cert/cert_add.go @@ -0,0 +1,49 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type CertAdd struct { + Type string `form:"type" json:"type"` + Domains []string `form:"domains" json:"domains"` + UserID uint `form:"user_id" json:"user_id"` + DNSID *uint `form:"dns_id" json:"dns_id"` +} + +func (r *CertAdd) Authorize(ctx http.Context) error { + return nil +} + +func (r *CertAdd) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "type": "required|in:P256,P384,2048,4096", + "domains": "required|array", + "user_id": "required|exists:cert_users,id", + } +} + +func (r *CertAdd) Messages(ctx http.Context) map[string]string { + return map[string]string{ + "type.required": "类型不能为空", + "type.in": "类型必须为 P256, P384, 2048, 4096 中的一个", + "domains.required": "域名不能为空", + "domains.slice": "域名必须为数组", + "user_id.required": "ACME 用户 ID 不能为空", + "user_id.exists": "ACME 用户 ID 不存在", + } +} + +func (r *CertAdd) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *CertAdd) PrepareForValidation(ctx http.Context, data validation.Data) error { + err := data.Set("user_id", uint(ctx.Request().InputInt("user_id"))) + if err != nil { + return err + } + + return nil +} diff --git a/app/http/requests/cert/dns_add.go b/app/http/requests/cert/dns_add.go new file mode 100644 index 00000000..0d69fbfe --- /dev/null +++ b/app/http/requests/cert/dns_add.go @@ -0,0 +1,51 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" + "panel/pkg/acme" +) + +type DNSAdd struct { + Type string `form:"type" json:"type"` + Data acme.DNSParam `form:"data" json:"data"` +} + +func (r *DNSAdd) Authorize(ctx http.Context) error { + return nil +} + +func (r *DNSAdd) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "type": "required|in:dnspod,aliyun,cloudflare", + "data": "required", + "data.id": "required_if:type,dnspod", + "data.token": "required_if:type,dnspod", + "data.access_key": "required_if:type,aliyun", + "data.secret_key": "required_if:type,aliyun", + "data.email": "required_if:type,cloudflare", + "data.api_key": "required_if:type,cloudflare", + } +} + +func (r *DNSAdd) Messages(ctx http.Context) map[string]string { + return map[string]string{ + "type.required": "类型不能为空", + "type.in": "类型必须为 dnspod, aliyun, cloudflare 中的一个", + "data.required": "数据不能为空", + "data.id.required_if": "ID 不能为空", + "data.token.required_if": "Token 不能为空", + "data.access_key.required": "Access Key 不能为空", + "data.secret_key.required": "Secret Key 不能为空", + "data.email.required": "Email 不能为空", + "data.api_key.required": "API Key 不能为空", + } +} + +func (r *DNSAdd) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *DNSAdd) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/obtain.go b/app/http/requests/cert/obtain.go new file mode 100644 index 00000000..cc991533 --- /dev/null +++ b/app/http/requests/cert/obtain.go @@ -0,0 +1,35 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type Obtain struct { + ID uint `form:"id" json:"id"` +} + +func (r *Obtain) Authorize(ctx http.Context) error { + return nil +} + +func (r *Obtain) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|exists:certs,id", + } +} + +func (r *Obtain) Messages(ctx http.Context) map[string]string { + return map[string]string{ + "id.required": "证书 ID 不能为空", + "id.exists": "证书 ID 不存在", + } +} + +func (r *Obtain) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *Obtain) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/renew.go b/app/http/requests/cert/renew.go new file mode 100644 index 00000000..ace894be --- /dev/null +++ b/app/http/requests/cert/renew.go @@ -0,0 +1,35 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type Renew struct { + ID uint `form:"id" json:"id"` +} + +func (r *Renew) Authorize(ctx http.Context) error { + return nil +} + +func (r *Renew) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|exists:certs,id", + } +} + +func (r *Renew) Messages(ctx http.Context) map[string]string { + return map[string]string{ + "id.required": "证书 ID 不能为空", + "id.exists": "证书 ID 不存在", + } +} + +func (r *Renew) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *Renew) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/user_add.go b/app/http/requests/cert/user_add.go new file mode 100644 index 00000000..ff065218 --- /dev/null +++ b/app/http/requests/cert/user_add.go @@ -0,0 +1,49 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type UserAdd struct { + CA string `form:"ca" json:"ca"` + Email string `form:"email" json:"email"` + Kid string `form:"kid" json:"kid"` + HmacEncoded string `form:"hmac_encoded" json:"hmac_encoded"` + KeyType string `form:"key_type" json:"key_type"` +} + +func (r *UserAdd) Authorize(ctx http.Context) error { + return nil +} + +func (r *UserAdd) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "ca": "required|in:letsencrypt,zerossl,sslcom,google,buypass", + "email": "required|email", + "kid": "required_unless:ca,letsencrypt,buypass", + "hmac_encoded": "required_unless:ca,letsencrypt,buypass", + "key_type": "required|in:P256,P384,2048,4096", + } +} + +func (r *UserAdd) Messages(ctx http.Context) map[string]string { + return map[string]string{ + "ca.required": "CA 不能为空", + "ca.in": "CA 必须为 letsencrypt, zerossl, sslcom, google, buypass 中的一个", + "email.required": "邮箱不能为空", + "email.email": "邮箱格式不正确", + "kid.required_unless": "KID 不能为空", + "hmac_encoded.required": "HMAC Encoded 不能为空", + "key_type.required": "密钥类型不能为空", + "key_type.in": "密钥类型必须为 P256, P384, 2048, 4096 中的一个", + } +} + +func (r *UserAdd) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *UserAdd) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/common/paginate.go b/app/http/requests/common/paginate.go new file mode 100644 index 00000000..2375b90f --- /dev/null +++ b/app/http/requests/common/paginate.go @@ -0,0 +1,44 @@ +package commonrequests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type Paginate struct { + Page int `form:"page" json:"page"` + Limit int `form:"limit" json:"limit"` +} + +func (r *Paginate) Authorize(ctx http.Context) error { + return nil +} + +func (r *Paginate) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "page": "required|uint|min:1", + "limit": "required|uint|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", + } +} + +func (r *Paginate) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *Paginate) PrepareForValidation(ctx http.Context, data validation.Data) error { + _ = data.Set("page", ctx.Request().QueryInt("page")) + _ = data.Set("limit", ctx.Request().QueryInt("limit")) + + return nil +} diff --git a/app/http/requests/login_request.go b/app/http/requests/user/login.go similarity index 50% rename from app/http/requests/login_request.go rename to app/http/requests/user/login.go index eefb67df..758de9a4 100644 --- a/app/http/requests/login_request.go +++ b/app/http/requests/user/login.go @@ -5,39 +5,34 @@ import ( "github.com/goravel/framework/contracts/validation" ) -type LoginRequest struct { +type Login struct { Username string `json:"username" form:"username"` Password string `json:"password" form:"password"` - - CaptchaID string `json:"captcha_id" form:"captcha_id"` - Captcha string `json:"captcha" form:"captcha"` } -func (r *LoginRequest) Authorize(ctx http.Context) error { +func (r *Login) Authorize(ctx http.Context) error { return nil } -func (r *LoginRequest) Rules(ctx http.Context) map[string]string { +func (r *Login) Rules(ctx http.Context) map[string]string { return map[string]string{ "username": "required", "password": "required|min_len:8", - "captcha": "captcha:true", } } -func (r *LoginRequest) Messages(ctx http.Context) map[string]string { +func (r *Login) Messages(ctx http.Context) map[string]string { return map[string]string{ "username.required": "登录名不能为空", "password.required": "密码不能为空", "password.min_len": "密码长度不能小于 8 位", - "captcha.captcha": "验证码错误", } } -func (r *LoginRequest) Attributes(ctx http.Context) map[string]string { +func (r *Login) Attributes(ctx http.Context) map[string]string { return map[string]string{} } -func (r *LoginRequest) PrepareForValidation(ctx http.Context, data validation.Data) error { +func (r *Login) PrepareForValidation(ctx http.Context, data validation.Data) error { return nil } diff --git a/app/http/responses/cert/cert_list.go b/app/http/responses/cert/cert_list.go new file mode 100644 index 00000000..3bbbcc27 --- /dev/null +++ b/app/http/responses/cert/cert_list.go @@ -0,0 +1,8 @@ +package responses + +import "panel/app/models" + +type CertList struct { + Total int64 `json:"total"` + Items []models.Cert `json:"items"` +} diff --git a/app/http/responses/cert/dns_list.go b/app/http/responses/cert/dns_list.go new file mode 100644 index 00000000..9440b205 --- /dev/null +++ b/app/http/responses/cert/dns_list.go @@ -0,0 +1,8 @@ +package responses + +import "panel/app/models" + +type DNSList struct { + Total int64 `json:"total"` + Items []models.CertDNS `json:"items"` +} diff --git a/app/http/responses/cert/user_list.go b/app/http/responses/cert/user_list.go new file mode 100644 index 00000000..460d8cff --- /dev/null +++ b/app/http/responses/cert/user_list.go @@ -0,0 +1,8 @@ +package responses + +import "panel/app/models" + +type UserList struct { + Total int64 `json:"total"` + Items []models.CertUser `json:"items"` +} diff --git a/app/models/cert.go b/app/models/cert.go new file mode 100644 index 00000000..22f0e1fc --- /dev/null +++ b/app/models/cert.go @@ -0,0 +1,25 @@ +package models + +import ( + "github.com/goravel/framework/support/carbon" +) + +type Cert struct { + ID uint `gorm:"primaryKey" json:"id"` + UserID uint `gorm:"default:null" json:"user_id"` // 关联的 ACME 用户 ID + WebsiteID *uint `gorm:"default:null" json:"website_id"` // 关联的网站 ID + DNSID *uint `gorm:"column:dns_id;default:null" json:"dns_id"` // 关联的 DNS ID + CronID *uint `gorm:"default:null" json:"cron_id"` // 关联的计划任务 ID + Type string `gorm:"not null" json:"type"` // 证书类型 (P256, P384, 2048, 4096) + Domains []string `gorm:"type:json;serializer:json" json:"domains"` + CertURL *string `gorm:"default:null" json:"cert_url"` // 证书 URL (续签时使用) + Cert string `gorm:"default:null" json:"cert"` // 证书内容 + Key string `gorm:"default:null" json:"key"` // 私钥内容 + CreatedAt carbon.DateTime `gorm:"autoCreateTime;column:created_at" json:"created_at"` + UpdatedAt carbon.DateTime `gorm:"autoUpdateTime;column:updated_at" json:"updated_at"` + + Website *Website `gorm:"foreignKey:WebsiteID" json:"website"` + User *CertUser `gorm:"foreignKey:UserID" json:"user"` + DNS *CertDNS `gorm:"foreignKey:DNSID" json:"dns"` + Cron *Cron `gorm:"foreignKey:CronID" json:"cron"` +} diff --git a/app/models/cert_dns.go b/app/models/cert_dns.go new file mode 100644 index 00000000..c9f0f155 --- /dev/null +++ b/app/models/cert_dns.go @@ -0,0 +1,21 @@ +package models + +import ( + "github.com/goravel/framework/support/carbon" + + "panel/pkg/acme" +) + +type CertDNS struct { + ID uint `gorm:"primaryKey" json:"id"` + Type string `gorm:"not null" json:"type"` // DNS 提供商 (dnspod, aliyun, cloudflare) + Data acme.DNSParam `gorm:"type:json;serializer:json" json:"dns_param"` + CreatedAt carbon.DateTime `gorm:"autoCreateTime;column:created_at" json:"created_at"` + UpdatedAt carbon.DateTime `gorm:"autoUpdateTime;column:updated_at" json:"updated_at"` + + Certs []*Cert `gorm:"foreignKey:DNSID" json:"certs"` +} + +func (CertDNS) TableName() string { + return "cert_dns" +} diff --git a/app/models/cert_user.go b/app/models/cert_user.go new file mode 100644 index 00000000..d44e14e1 --- /dev/null +++ b/app/models/cert_user.go @@ -0,0 +1,19 @@ +package models + +import ( + "github.com/goravel/framework/support/carbon" +) + +type CertUser struct { + ID uint `gorm:"primaryKey" json:"id"` + Email string `gorm:"not null" json:"email"` + CA string `gorm:"not null" json:"ca"` // CA 提供商 (letsencrypt, zerossl, sslcom, google, buypass) + Kid *string `gorm:"default:null" json:"kid"` + HmacEncoded *string `gorm:"default:null" json:"hmac_encoded"` + PrivateKey string `gorm:"not null" json:"private_key"` + KeyType string `gorm:"not null" json:"key_type"` + CreatedAt carbon.DateTime `gorm:"autoCreateTime;column:created_at" json:"created_at"` + UpdatedAt carbon.DateTime `gorm:"autoUpdateTime;column:updated_at" json:"updated_at"` + + Certs []*Cert `gorm:"foreignKey:UserID" json:"certs"` +} diff --git a/app/plugins/certbot/certbot.go b/app/plugins/certbot/certbot.go deleted file mode 100644 index 752cb693..00000000 --- a/app/plugins/certbot/certbot.go +++ /dev/null @@ -1,13 +0,0 @@ -package certbot - -var ( - Name = "证书管理器" - Description = "证书管理器使用 ACME 协议为服务器从证书颁发机构自动获取 HTTPS 证书。" - Slug = "certbot" - Version = "1.0.0" - Requires = []string{} - Excludes = []string{} - Install = `bash /www/panel/scripts/certbot/install.sh` - Uninstall = `bash /www/panel/scripts/certbot/uninstall.sh` - Update = `bash /www/panel/scripts/certbot/update.sh` -) diff --git a/app/rules/exists.go b/app/rules/exists.go index e1e4e64b..6909eb9e 100644 --- a/app/rules/exists.go +++ b/app/rules/exists.go @@ -3,6 +3,7 @@ package rules import ( "github.com/goravel/framework/contracts/validation" "github.com/goravel/framework/facades" + "github.com/spf13/cast" ) type Exists struct { @@ -21,8 +22,8 @@ func (receiver *Exists) Passes(_ validation.Data, val any, options ...any) bool // 第二个参数,字段名称,如 id fieldName := options[1].(string) // 用户请求过来的数据 - requestValue, ok := val.(string) - if !ok { + requestValue, err := cast.ToStringE(val) + if err != nil { return false } @@ -40,7 +41,7 @@ func (receiver *Exists) Passes(_ validation.Data, val any, options ...any) bool query = query.OrWhere(options[i].(string), requestValue) } } - err := query.Count(&count) + err = query.Count(&count) if err != nil { return false } diff --git a/app/rules/not_exists.go b/app/rules/not_exists.go index ad61fa6a..e8231459 100644 --- a/app/rules/not_exists.go +++ b/app/rules/not_exists.go @@ -3,6 +3,7 @@ package rules import ( "github.com/goravel/framework/contracts/validation" "github.com/goravel/framework/facades" + "github.com/spf13/cast" ) type NotExists struct { @@ -21,8 +22,8 @@ func (receiver *NotExists) Passes(_ validation.Data, val any, options ...any) bo // 第二个参数,字段名称,如 id fieldName := options[1].(string) // 用户请求过来的数据 - requestValue, ok := val.(string) - if !ok { + requestValue, err := cast.ToStringE(val) + if err != nil { return false } @@ -40,7 +41,7 @@ func (receiver *NotExists) Passes(_ validation.Data, val any, options ...any) bo query = query.OrWhere(options[i].(string), requestValue) } } - err := query.Count(&count) + err = query.Count(&count) if err != nil { return false } diff --git a/app/services/cert.go b/app/services/cert.go new file mode 100644 index 00000000..3c563c63 --- /dev/null +++ b/app/services/cert.go @@ -0,0 +1,371 @@ +// Package services 证书服务 +package services + +import ( + "errors" + + "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/certificate" + "github.com/goravel/framework/facades" + "panel/pkg/tools" + + requests "panel/app/http/requests/cert" + "panel/app/models" + "panel/pkg/acme" +) + +type Cert interface { + GetByID(ID uint) (models.Cert, error) + UserAdd(request requests.UserAdd) error + UserDelete(ID uint) error + DNSAdd(request requests.DNSAdd) error + DNSDelete(ID uint) error + CertAdd(request requests.CertAdd) error + CertDelete(ID uint) error + ObtainAuto(ID uint) (certificate.Resource, error) + ObtainManual(ID uint) (certificate.Resource, error) + ManualDNS(ID uint) (map[string]acme.Resolve, error) + Renew(ID uint) (certificate.Resource, error) +} + +type CertImpl struct { +} + +func NewCertImpl() *CertImpl { + return &CertImpl{} +} + +// GetByID 根据 ID 获取证书 +func (s *CertImpl) GetByID(ID uint) (models.Cert, error) { + var cert models.Cert + err := facades.Orm().Query().With("User").With("DNS").With("Website").Where("id = ?", ID).First(&cert) + return cert, err +} + +// UserAdd 添加用户 +func (s *CertImpl) UserAdd(request requests.UserAdd) error { + var user models.CertUser + user.CA = request.CA + user.Email = request.Email + user.Kid = &request.Kid + user.HmacEncoded = &request.HmacEncoded + user.KeyType = request.KeyType + + var err error + var client *acme.Client + switch user.CA { + case "letsencrypt": + client, err = acme.NewRegisterClient(user.Email, acme.CALetEncrypt, certcrypto.KeyType(user.KeyType)) + case "buypass": + client, err = acme.NewRegisterClient(user.Email, acme.CABuypass, certcrypto.KeyType(user.KeyType)) + case "zerossl": + client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAZeroSSL, certcrypto.KeyType(user.KeyType)) + case "sslcom": + client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CASSLcom, certcrypto.KeyType(user.KeyType)) + case "google": + client, err = acme.NewRegisterWithExternalAccountBindingClient(user.Email, *user.Kid, *user.HmacEncoded, acme.CAGoogle, certcrypto.KeyType(user.KeyType)) + default: + return errors.New("CA 提供商不支持") + } + + if err != nil { + return errors.New("向 CA 注册账号失败,请检查参数是否正确") + } + + privateKey, err := acme.GetPrivateKey(client.User.GetPrivateKey(), acme.KeyType(user.KeyType)) + if err != nil { + return errors.New("获取私钥失败") + } + user.PrivateKey = string(privateKey) + + return facades.Orm().Query().Create(&user) +} + +// UserDelete 删除用户 +func (s *CertImpl) UserDelete(ID uint) error { + var user models.CertUser + err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&user) + if err != nil { + return err + } + + if user.Certs != nil { + return errors.New("该用户下存在证书,无法删除") + } + + _, err = facades.Orm().Query().Delete(&models.CertUser{}, ID) + return err +} + +// DNSAdd 添加 DNS +func (s *CertImpl) DNSAdd(request requests.DNSAdd) error { + var dns models.CertDNS + dns.Type = request.Type + dns.Data = request.Data + + return facades.Orm().Query().Create(&dns) +} + +// DNSDelete 删除 DNS +func (s *CertImpl) DNSDelete(ID uint) error { + var dns models.CertDNS + err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&dns) + if err != nil { + return err + } + + if dns.Certs != nil { + return errors.New("该 DNS 接口下存在证书,无法删除") + } + + _, err = facades.Orm().Query().Delete(&models.CertDNS{}, ID) + return err +} + +// CertAdd 添加证书 +func (s *CertImpl) CertAdd(request requests.CertAdd) error { + var cert models.Cert + cert.Type = request.Type + cert.Domains = request.Domains + cert.UserID = request.UserID + + if request.DNSID != nil { + cert.DNSID = request.DNSID + // TODO 生成计划任务 + } + + return facades.Orm().Query().Create(&cert) +} + +// CertDelete 删除证书 +func (s *CertImpl) CertDelete(ID uint) error { + var cert models.Cert + err := facades.Orm().Query().Where("id = ?", ID).First(&cert) + if err != nil { + return err + } + + if cert.CronID != nil { + // TODO 删除计划任务 + } + + _, err = facades.Orm().Query().Delete(&models.Cert{}, ID) + return err +} + +// ObtainAuto 自动签发证书 +func (s *CertImpl) ObtainAuto(ID uint) (certificate.Resource, error) { + var cert models.Cert + err := facades.Orm().Query().With("Website").With("User").With("DNS").Where("id = ?", ID).First(&cert) + if err != nil { + return certificate.Resource{}, err + } + + var ca string + switch cert.User.CA { + case "letsencrypt": + ca = acme.CALetEncrypt + case "buypass": + ca = acme.CABuypass + case "zerossl": + ca = acme.CAZeroSSL + case "sslcom": + ca = acme.CASSLcom + case "google": + ca = acme.CAGoogle + } + + client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType)) + if err != nil { + return certificate.Resource{}, err + } + + if cert.DNS != nil { + err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data) + } else { + if cert.Website == nil { + return certificate.Resource{}, errors.New("该证书没有关联网站,无法自动签发") + } else { + err = client.UseHTTP(cert.Website.Path) + } + } + if err != nil { + return certificate.Resource{}, err + } + + ssl, err := client.ObtainSSL(cert.Domains) + if err != nil { + return certificate.Resource{}, err + } + + cert.CertURL = &ssl.CertURL + cert.Cert = string(ssl.Certificate) + cert.Key = string(ssl.PrivateKey) + err = facades.Orm().Query().Save(&cert) + if err != nil { + return certificate.Resource{}, err + } + + if cert.Website != nil { + tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644) + tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644) + tools.Exec("systemctl reload openresty") + } + + return ssl, nil +} + +// ObtainManual 手动签发证书 +func (s *CertImpl) ObtainManual(ID uint) (certificate.Resource, error) { + var cert models.Cert + err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert) + if err != nil { + return certificate.Resource{}, err + } + + var ca string + switch cert.User.CA { + case "letsencrypt": + ca = acme.CALetEncrypt + case "buypass": + ca = acme.CABuypass + case "zerossl": + ca = acme.CAZeroSSL + case "sslcom": + ca = acme.CASSLcom + case "google": + ca = acme.CAGoogle + } + + client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType)) + if err != nil { + return certificate.Resource{}, err + } + + err = client.UseManualDns() + if err != nil { + return certificate.Resource{}, err + } + + ssl, err := client.ObtainSSL(cert.Domains) + if err != nil { + return certificate.Resource{}, err + } + + cert.CertURL = &ssl.CertURL + cert.Cert = string(ssl.Certificate) + cert.Key = string(ssl.PrivateKey) + err = facades.Orm().Query().Save(&cert) + if err != nil { + return certificate.Resource{}, err + } + + if cert.Website != nil { + tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644) + tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644) + tools.Exec("systemctl reload openresty") + } + + return ssl, nil +} + +// ManualDNS 获取手动 DNS 解析信息 +func (s *CertImpl) ManualDNS(ID uint) (map[string]acme.Resolve, error) { + var cert models.Cert + err := facades.Orm().Query().With("User").Where("id = ?", ID).First(&cert) + if err != nil { + return nil, err + } + + var ca string + switch cert.User.CA { + case "letsencrypt": + ca = acme.CALetEncrypt + case "buypass": + ca = acme.CABuypass + case "zerossl": + ca = acme.CAZeroSSL + case "sslcom": + ca = acme.CASSLcom + case "google": + ca = acme.CAGoogle + } + + client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType)) + if err != nil { + return nil, err + } + + err = client.UseManualDns() + if err != nil { + return nil, err + } + + return client.GetDNSResolve(cert.Domains) +} + +// Renew 续签证书 +func (s *CertImpl) Renew(ID uint) (certificate.Resource, error) { + var cert models.Cert + err := facades.Orm().Query().With("User").With("DNS").Where("id = ?", ID).First(&cert) + if err != nil { + return certificate.Resource{}, err + } + + var ca string + switch cert.User.CA { + case "letsencrypt": + ca = acme.CALetEncrypt + case "buypass": + ca = acme.CABuypass + case "zerossl": + ca = acme.CAZeroSSL + case "sslcom": + ca = acme.CASSLcom + case "google": + ca = acme.CAGoogle + } + + client, err := acme.NewPrivateKeyClient(cert.User.Email, cert.User.PrivateKey, ca, certcrypto.KeyType(cert.User.KeyType)) + if err != nil { + return certificate.Resource{}, err + } + + if cert.CertURL == nil { + return certificate.Resource{}, errors.New("该证书没有签发成功,无法续签") + } + + if cert.DNS != nil { + err = client.UseDns(acme.DnsType(cert.DNS.Type), cert.DNS.Data) + } else { + if cert.Website == nil { + return certificate.Resource{}, errors.New("该证书没有关联网站,无法续签,可以尝试手动签发") + } else { + err = client.UseHTTP(cert.Website.Path) + } + } + if err != nil { + return certificate.Resource{}, err + } + + ssl, err := client.RenewSSL(*cert.CertURL) + if err != nil { + return certificate.Resource{}, err + } + + cert.CertURL = &ssl.CertURL + cert.Cert = string(ssl.Certificate) + cert.Key = string(ssl.PrivateKey) + err = facades.Orm().Query().Save(&cert) + if err != nil { + return certificate.Resource{}, err + } + + if cert.Website != nil { + tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".pem", string(ssl.Certificate), 0644) + tools.Write("/www/server/vhost/ssl/"+cert.Website.Name+".key", string(ssl.PrivateKey), 0644) + tools.Exec("systemctl reload openresty") + } + + return ssl, nil +} diff --git a/app/services/plugin.go b/app/services/plugin.go index 76b08224..31273425 100644 --- a/app/services/plugin.go +++ b/app/services/plugin.go @@ -5,7 +5,6 @@ import ( "github.com/goravel/framework/facades" "panel/app/models" - "panel/app/plugins/certbot" "panel/app/plugins/fail2ban" "panel/app/plugins/mysql57" "panel/app/plugins/mysql80" @@ -197,17 +196,6 @@ func (r *PluginImpl) All() []PanelPlugin { Uninstall: redis.Uninstall, Update: redis.Update, }) - p = append(p, PanelPlugin{ - Name: certbot.Name, - Description: certbot.Description, - Slug: certbot.Slug, - Version: certbot.Version, - Requires: certbot.Requires, - Excludes: certbot.Excludes, - Install: certbot.Install, - Uninstall: certbot.Uninstall, - Update: certbot.Update, - }) p = append(p, PanelPlugin{ Name: s3fs.Name, Description: s3fs.Description, diff --git a/database/migrations/20231101121706_create_cert_users_table.down.sql b/database/migrations/20231101121706_create_cert_users_table.down.sql new file mode 100644 index 00000000..f2820ad1 --- /dev/null +++ b/database/migrations/20231101121706_create_cert_users_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS cert_users; diff --git a/database/migrations/20231101121706_create_cert_users_table.up.sql b/database/migrations/20231101121706_create_cert_users_table.up.sql new file mode 100644 index 00000000..f42cb064 --- /dev/null +++ b/database/migrations/20231101121706_create_cert_users_table.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE cert_users +( + id integer PRIMARY KEY AUTOINCREMENT NOT NULL, + email varchar(255) NOT NULL, + ca varchar(255) NOT NULL, + kid varchar(255) DEFAULT NULL, + hmac_encoded varchar(255) DEFAULT NULL, + private_key text NOT NULL, + key_type varchar(255) NOT NULL, + created_at datetime NOT NULL, + updated_at datetime NOT NULL +); + +CREATE INDEX idx_cert_users_email ON cert_users (email); diff --git a/database/migrations/20231101121912_create_cert_dns_table.down.sql b/database/migrations/20231101121912_create_cert_dns_table.down.sql new file mode 100644 index 00000000..2a02392e --- /dev/null +++ b/database/migrations/20231101121912_create_cert_dns_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS cert_dns; diff --git a/database/migrations/20231101121912_create_cert_dns_table.up.sql b/database/migrations/20231101121912_create_cert_dns_table.up.sql new file mode 100644 index 00000000..2927749f --- /dev/null +++ b/database/migrations/20231101121912_create_cert_dns_table.up.sql @@ -0,0 +1,8 @@ +CREATE TABLE cert_dns +( + id integer PRIMARY KEY AUTOINCREMENT NOT NULL, + type varchar(255) NOT NULL, + data text NOT NULL, + created_at datetime NOT NULL, + updated_at datetime NOT NULL +); diff --git a/database/migrations/20231101121929_create_certs_table.down.sql b/database/migrations/20231101121929_create_certs_table.down.sql new file mode 100644 index 00000000..56ff77db --- /dev/null +++ b/database/migrations/20231101121929_create_certs_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS certs; diff --git a/database/migrations/20231101121929_create_certs_table.up.sql b/database/migrations/20231101121929_create_certs_table.up.sql new file mode 100644 index 00000000..94421a25 --- /dev/null +++ b/database/migrations/20231101121929_create_certs_table.up.sql @@ -0,0 +1,15 @@ +CREATE TABLE certs +( + id integer PRIMARY KEY AUTOINCREMENT NOT NULL, + user_id integer NOT NULL, + website_id integer DEFAULT NULL, + dns_id integer DEFAULT NULL, + cron_id integer DEFAULT NULL, + type varchar(255) NOT NULL, + domains text NOT NULL, + cert_url varchar(255) DEFAULT NULL, + cert text DEFAULT NULL, + key text DEFAULT NULL, + created_at datetime NOT NULL, + updated_at datetime NOT NULL +); diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 00000000..c551ce6e --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,1157 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "name": "耗子科技", + "email": "i@haozi.net" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/panel/cert/algorithms": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理支持的算法列表", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取算法列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/caProviders": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理支持的 CA 提供商", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 CA 提供商", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/certs": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的证书列表", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取证书列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CertList" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "添加证书到面板证书管理", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "添加证书", + "parameters": [ + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CertAdd" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/certs/{id}": { + "delete": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "删除面板证书管理的证书", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "删除证书", + "parameters": [ + { + "type": "integer", + "description": "证书 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/dns": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的 DNS 接口列表", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 DNS 接口列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.DNSList" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "添加 DNS 接口到面板证书管理", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "添加 DNS 接口", + "parameters": [ + { + "description": "DNS 接口信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.DNSAdd" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/dns/{id}": { + "delete": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "删除面板证书管理的 DNS 接口", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "删除 DNS 接口", + "parameters": [ + { + "type": "integer", + "description": "DNS 接口 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/dnsProviders": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理支持的 DNS 提供商", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 DNS 提供商", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/manualDNS": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取签发证书所需的 DNS 记录", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取手动 DNS 记录", + "parameters": [ + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Obtain" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/acme.Resolve" + } + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/obtain": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "签发面板证书管理的证书", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "签发证书", + "parameters": [ + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Obtain" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/renew": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "续签面板证书管理的证书", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "续签证书", + "parameters": [ + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Renew" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/users": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的 ACME 用户列表", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取用户列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CertList" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "添加 ACME 用户到面板证书管理", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "添加 ACME 用户", + "parameters": [ + { + "description": "用户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserAdd" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/users/{id}": { + "delete": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "删除面板证书管理的 ACME 用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "删除 ACME 用户", + "parameters": [ + { + "type": "integer", + "description": "用户 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/user/login": { + "post": { + "description": "通过用户名和密码获取访问令牌", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户" + ], + "summary": "用户登录", + "parameters": [ + { + "description": "登录信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Login" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "403": { + "description": "用户名或密码错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "acme.DNSParam": { + "type": "object", + "properties": { + "access_key": { + "type": "string" + }, + "api_key": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "secret_key": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "acme.Resolve": { + "type": "object", + "properties": { + "err": { + "type": "string" + }, + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "controllers.ErrorResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "message": { + "type": "string" + } + } + }, + "controllers.SuccessResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "message": { + "type": "string" + } + } + }, + "models.Cert": { + "type": "object", + "properties": { + "cert": { + "description": "证书内容", + "type": "string" + }, + "cert_url": { + "description": "证书 URL (续签时使用)", + "type": "string" + }, + "created_at": { + "type": "string" + }, + "cron": { + "$ref": "#/definitions/models.Cron" + }, + "cron_id": { + "description": "关联的计划任务 ID", + "type": "integer" + }, + "dns": { + "$ref": "#/definitions/models.CertDNS" + }, + "dns_id": { + "description": "关联的 DNS ID", + "type": "integer" + }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "key": { + "description": "私钥内容", + "type": "string" + }, + "type": { + "description": "证书类型 (P256, P384, 2048, 4096)", + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/models.CertUser" + }, + "user_id": { + "description": "关联的 ACME 用户 ID", + "type": "integer" + }, + "website": { + "$ref": "#/definitions/models.Website" + }, + "website_id": { + "description": "关联的网站 ID", + "type": "integer" + } + } + }, + "models.CertDNS": { + "type": "object", + "properties": { + "certs": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Cert" + } + }, + "created_at": { + "type": "string" + }, + "dns_param": { + "$ref": "#/definitions/acme.DNSParam" + }, + "id": { + "type": "integer" + }, + "type": { + "description": "DNS 提供商 (dnspod, aliyun, cloudflare)", + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "models.CertUser": { + "type": "object", + "properties": { + "ca": { + "description": "CA 提供商 (letsencrypt, zerossl, sslcom, google, buypass)", + "type": "string" + }, + "certs": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Cert" + } + }, + "created_at": { + "type": "string" + }, + "email": { + "type": "string" + }, + "hmac_encoded": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "key_type": { + "type": "string" + }, + "kid": { + "type": "string" + }, + "private_key": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "models.Cron": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "log": { + "type": "string" + }, + "name": { + "type": "string" + }, + "shell": { + "type": "string" + }, + "status": { + "type": "boolean" + }, + "time": { + "type": "string" + }, + "type": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "models.Website": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "php": { + "type": "integer" + }, + "remark": { + "type": "string" + }, + "ssl": { + "type": "boolean" + }, + "status": { + "type": "boolean" + }, + "updated_at": { + "type": "string" + } + } + }, + "requests.CertAdd": { + "type": "object", + "properties": { + "dns_id": { + "type": "integer" + }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string" + }, + "user_id": { + "type": "integer" + } + } + }, + "requests.DNSAdd": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/acme.DNSParam" + }, + "type": { + "type": "string" + } + } + }, + "requests.Login": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "requests.Obtain": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, + "requests.Renew": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, + "requests.UserAdd": { + "type": "object", + "properties": { + "ca": { + "type": "string" + }, + "email": { + "type": "string" + }, + "hmac_encoded": { + "type": "string" + }, + "key_type": { + "type": "string" + }, + "kid": { + "type": "string" + } + } + }, + "responses.CertList": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Cert" + } + }, + "total": { + "type": "integer" + } + } + }, + "responses.DNSList": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/models.CertDNS" + } + }, + "total": { + "type": "integer" + } + } + } + }, + "securityDefinitions": { + "BearerToken": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "2", + Host: "", + BasePath: "/api", + Schemes: []string{}, + Title: "耗子 Linux 面板 API", + Description: "耗子 Linux 面板的 API 信息", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 00000000..1768df19 --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,1132 @@ +{ + "swagger": "2.0", + "info": { + "description": "耗子 Linux 面板的 API 信息", + "title": "耗子 Linux 面板 API", + "contact": { + "name": "耗子科技", + "email": "i@haozi.net" + }, + "version": "2" + }, + "basePath": "/api", + "paths": { + "/panel/cert/algorithms": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理支持的算法列表", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取算法列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/caProviders": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理支持的 CA 提供商", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 CA 提供商", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/certs": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的证书列表", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取证书列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CertList" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "添加证书到面板证书管理", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "添加证书", + "parameters": [ + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CertAdd" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/certs/{id}": { + "delete": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "删除面板证书管理的证书", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "删除证书", + "parameters": [ + { + "type": "integer", + "description": "证书 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/dns": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的 DNS 接口列表", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 DNS 接口列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.DNSList" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "添加 DNS 接口到面板证书管理", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "添加 DNS 接口", + "parameters": [ + { + "description": "DNS 接口信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.DNSAdd" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/dns/{id}": { + "delete": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "删除面板证书管理的 DNS 接口", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "删除 DNS 接口", + "parameters": [ + { + "type": "integer", + "description": "DNS 接口 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/dnsProviders": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理支持的 DNS 提供商", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 DNS 提供商", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/manualDNS": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取签发证书所需的 DNS 记录", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取手动 DNS 记录", + "parameters": [ + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Obtain" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/acme.Resolve" + } + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/obtain": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "签发面板证书管理的证书", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "签发证书", + "parameters": [ + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Obtain" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/renew": { + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "续签面板证书管理的证书", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "续签证书", + "parameters": [ + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Renew" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/users": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的 ACME 用户列表", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取用户列表", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/responses.CertList" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "添加 ACME 用户到面板证书管理", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "添加 ACME 用户", + "parameters": [ + { + "description": "用户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserAdd" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/cert/users/{id}": { + "delete": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "删除面板证书管理的 ACME 用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "删除 ACME 用户", + "parameters": [ + { + "type": "integer", + "description": "用户 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + }, + "/panel/user/login": { + "post": { + "description": "通过用户名和密码获取访问令牌", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户" + ], + "summary": "用户登录", + "parameters": [ + { + "description": "登录信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.Login" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "403": { + "description": "用户名或密码错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "acme.DNSParam": { + "type": "object", + "properties": { + "access_key": { + "type": "string" + }, + "api_key": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "secret_key": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "acme.Resolve": { + "type": "object", + "properties": { + "err": { + "type": "string" + }, + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "controllers.ErrorResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "message": { + "type": "string" + } + } + }, + "controllers.SuccessResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": {}, + "message": { + "type": "string" + } + } + }, + "models.Cert": { + "type": "object", + "properties": { + "cert": { + "description": "证书内容", + "type": "string" + }, + "cert_url": { + "description": "证书 URL (续签时使用)", + "type": "string" + }, + "created_at": { + "type": "string" + }, + "cron": { + "$ref": "#/definitions/models.Cron" + }, + "cron_id": { + "description": "关联的计划任务 ID", + "type": "integer" + }, + "dns": { + "$ref": "#/definitions/models.CertDNS" + }, + "dns_id": { + "description": "关联的 DNS ID", + "type": "integer" + }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "key": { + "description": "私钥内容", + "type": "string" + }, + "type": { + "description": "证书类型 (P256, P384, 2048, 4096)", + "type": "string" + }, + "updated_at": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/models.CertUser" + }, + "user_id": { + "description": "关联的 ACME 用户 ID", + "type": "integer" + }, + "website": { + "$ref": "#/definitions/models.Website" + }, + "website_id": { + "description": "关联的网站 ID", + "type": "integer" + } + } + }, + "models.CertDNS": { + "type": "object", + "properties": { + "certs": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Cert" + } + }, + "created_at": { + "type": "string" + }, + "dns_param": { + "$ref": "#/definitions/acme.DNSParam" + }, + "id": { + "type": "integer" + }, + "type": { + "description": "DNS 提供商 (dnspod, aliyun, cloudflare)", + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "models.CertUser": { + "type": "object", + "properties": { + "ca": { + "description": "CA 提供商 (letsencrypt, zerossl, sslcom, google, buypass)", + "type": "string" + }, + "certs": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Cert" + } + }, + "created_at": { + "type": "string" + }, + "email": { + "type": "string" + }, + "hmac_encoded": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "key_type": { + "type": "string" + }, + "kid": { + "type": "string" + }, + "private_key": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "models.Cron": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "log": { + "type": "string" + }, + "name": { + "type": "string" + }, + "shell": { + "type": "string" + }, + "status": { + "type": "boolean" + }, + "time": { + "type": "string" + }, + "type": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + } + }, + "models.Website": { + "type": "object", + "properties": { + "created_at": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "php": { + "type": "integer" + }, + "remark": { + "type": "string" + }, + "ssl": { + "type": "boolean" + }, + "status": { + "type": "boolean" + }, + "updated_at": { + "type": "string" + } + } + }, + "requests.CertAdd": { + "type": "object", + "properties": { + "dns_id": { + "type": "integer" + }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string" + }, + "user_id": { + "type": "integer" + } + } + }, + "requests.DNSAdd": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/acme.DNSParam" + }, + "type": { + "type": "string" + } + } + }, + "requests.Login": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "requests.Obtain": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, + "requests.Renew": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + } + }, + "requests.UserAdd": { + "type": "object", + "properties": { + "ca": { + "type": "string" + }, + "email": { + "type": "string" + }, + "hmac_encoded": { + "type": "string" + }, + "key_type": { + "type": "string" + }, + "kid": { + "type": "string" + } + } + }, + "responses.CertList": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Cert" + } + }, + "total": { + "type": "integer" + } + } + }, + "responses.DNSList": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/models.CertDNS" + } + }, + "total": { + "type": "integer" + } + } + } + }, + "securityDefinitions": { + "BearerToken": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 00000000..e3b09b7f --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,714 @@ +basePath: /api +definitions: + acme.DNSParam: + properties: + access_key: + type: string + api_key: + type: string + email: + type: string + id: + type: string + secret_key: + type: string + token: + type: string + type: object + acme.Resolve: + properties: + err: + type: string + key: + type: string + value: + type: string + type: object + controllers.ErrorResponse: + properties: + code: + type: integer + message: + type: string + type: object + controllers.SuccessResponse: + properties: + code: + type: integer + data: {} + message: + type: string + type: object + models.Cert: + properties: + cert: + description: 证书内容 + type: string + cert_url: + description: 证书 URL (续签时使用) + type: string + created_at: + type: string + cron: + $ref: '#/definitions/models.Cron' + cron_id: + description: 关联的计划任务 ID + type: integer + dns: + $ref: '#/definitions/models.CertDNS' + dns_id: + description: 关联的 DNS ID + type: integer + domains: + items: + type: string + type: array + id: + type: integer + key: + description: 私钥内容 + type: string + type: + description: 证书类型 (P256, P384, 2048, 4096) + type: string + updated_at: + type: string + user: + $ref: '#/definitions/models.CertUser' + user_id: + description: 关联的 ACME 用户 ID + type: integer + website: + $ref: '#/definitions/models.Website' + website_id: + description: 关联的网站 ID + type: integer + type: object + models.CertDNS: + properties: + certs: + items: + $ref: '#/definitions/models.Cert' + type: array + created_at: + type: string + dns_param: + $ref: '#/definitions/acme.DNSParam' + id: + type: integer + type: + description: DNS 提供商 (dnspod, aliyun, cloudflare) + type: string + updated_at: + type: string + type: object + models.CertUser: + properties: + ca: + description: CA 提供商 (letsencrypt, zerossl, sslcom, google, buypass) + type: string + certs: + items: + $ref: '#/definitions/models.Cert' + type: array + created_at: + type: string + email: + type: string + hmac_encoded: + type: string + id: + type: integer + key_type: + type: string + kid: + type: string + private_key: + type: string + updated_at: + type: string + type: object + models.Cron: + properties: + created_at: + type: string + id: + type: integer + log: + type: string + name: + type: string + shell: + type: string + status: + type: boolean + time: + type: string + type: + type: string + updated_at: + type: string + type: object + models.Website: + properties: + created_at: + type: string + id: + type: integer + name: + type: string + path: + type: string + php: + type: integer + remark: + type: string + ssl: + type: boolean + status: + type: boolean + updated_at: + type: string + type: object + requests.CertAdd: + properties: + dns_id: + type: integer + domains: + items: + type: string + type: array + type: + type: string + user_id: + type: integer + type: object + requests.DNSAdd: + properties: + data: + $ref: '#/definitions/acme.DNSParam' + type: + type: string + type: object + requests.Login: + properties: + password: + type: string + username: + type: string + type: object + requests.Obtain: + properties: + id: + type: integer + type: object + requests.Renew: + properties: + id: + type: integer + type: object + requests.UserAdd: + properties: + ca: + type: string + email: + type: string + hmac_encoded: + type: string + key_type: + type: string + kid: + type: string + type: object + responses.CertList: + properties: + items: + items: + $ref: '#/definitions/models.Cert' + type: array + total: + type: integer + type: object + responses.DNSList: + properties: + items: + items: + $ref: '#/definitions/models.CertDNS' + type: array + total: + type: integer + type: object +info: + contact: + email: i@haozi.net + name: 耗子科技 + description: 耗子 Linux 面板的 API 信息 + title: 耗子 Linux 面板 API + version: "2" +paths: + /panel/cert/algorithms: + get: + description: 获取面板证书管理支持的算法列表 + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取算法列表 + tags: + - 证书 + /panel/cert/caProviders: + get: + description: 获取面板证书管理支持的 CA 提供商 + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取 CA 提供商 + tags: + - 证书 + /panel/cert/certs: + get: + description: 获取面板证书管理的证书列表 + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/controllers.SuccessResponse' + - properties: + data: + $ref: '#/definitions/responses.CertList' + type: object + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取证书列表 + tags: + - 证书 + post: + consumes: + - application/json + description: 添加证书到面板证书管理 + parameters: + - description: 证书信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.CertAdd' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 添加证书 + tags: + - 证书 + /panel/cert/certs/{id}: + delete: + consumes: + - application/json + description: 删除面板证书管理的证书 + parameters: + - description: 证书 ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 删除证书 + tags: + - 证书 + /panel/cert/dns: + get: + description: 获取面板证书管理的 DNS 接口列表 + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/controllers.SuccessResponse' + - properties: + data: + $ref: '#/definitions/responses.DNSList' + type: object + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取 DNS 接口列表 + tags: + - 证书 + post: + consumes: + - application/json + description: 添加 DNS 接口到面板证书管理 + parameters: + - description: DNS 接口信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.DNSAdd' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 添加 DNS 接口 + tags: + - 证书 + /panel/cert/dns/{id}: + delete: + consumes: + - application/json + description: 删除面板证书管理的 DNS 接口 + parameters: + - description: DNS 接口 ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 删除 DNS 接口 + tags: + - 证书 + /panel/cert/dnsProviders: + get: + description: 获取面板证书管理支持的 DNS 提供商 + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取 DNS 提供商 + tags: + - 证书 + /panel/cert/manualDNS: + post: + consumes: + - application/json + description: 获取签发证书所需的 DNS 记录 + parameters: + - description: 证书信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.Obtain' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/controllers.SuccessResponse' + - properties: + data: + additionalProperties: + $ref: '#/definitions/acme.Resolve' + type: object + type: object + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取手动 DNS 记录 + tags: + - 证书 + /panel/cert/obtain: + post: + consumes: + - application/json + description: 签发面板证书管理的证书 + parameters: + - description: 证书信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.Obtain' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 签发证书 + tags: + - 证书 + /panel/cert/renew: + post: + consumes: + - application/json + description: 续签面板证书管理的证书 + parameters: + - description: 证书信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.Renew' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 续签证书 + tags: + - 证书 + /panel/cert/users: + get: + description: 获取面板证书管理的 ACME 用户列表 + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/controllers.SuccessResponse' + - properties: + data: + $ref: '#/definitions/responses.CertList' + type: object + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取用户列表 + tags: + - 证书 + post: + consumes: + - application/json + description: 添加 ACME 用户到面板证书管理 + parameters: + - description: 用户信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.UserAdd' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 添加 ACME 用户 + tags: + - 证书 + /panel/cert/users/{id}: + delete: + consumes: + - application/json + description: 删除面板证书管理的 ACME 用户 + parameters: + - description: 用户 ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 删除 ACME 用户 + tags: + - 证书 + /panel/user/login: + post: + consumes: + - application/json + description: 通过用户名和密码获取访问令牌 + parameters: + - description: 登录信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.Login' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/controllers.SuccessResponse' + "403": + description: 用户名或密码错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + summary: 用户登录 + tags: + - 用户 +securityDefinitions: + BearerToken: + in: header + name: Authorization + type: apiKey +swagger: "2.0" diff --git a/main.go b/main.go index d3f296db..2f3f1316 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,18 @@ import ( "panel/bootstrap" ) +// @title 耗子 Linux 面板 API +// @version 2 +// @description 耗子 Linux 面板的 API 信息 + +// @contact.name 耗子科技 +// @contact.email i@haozi.net + +// @securityDefinitions.apikey BearerToken +// @in header +// @name Authorization + +// @BasePath /api func main() { // 启动框架 bootstrap.Boot() diff --git a/pkg/acme/acme.go b/pkg/acme/acme.go index 36a19246..0f2bbac1 100644 --- a/pkg/acme/acme.go +++ b/pkg/acme/acme.go @@ -2,10 +2,10 @@ package acme import ( "crypto" + "crypto/ecdsa" "crypto/rsa" "crypto/x509" "encoding/pem" - "errors" "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/lego" @@ -20,6 +20,8 @@ const ( CASSLcom = "https://acme.ssl.com/sslcom-dv-rsa" ) +type KeyType = certcrypto.KeyType + const ( KeyEC256 = certcrypto.EC256 KeyEC384 = certcrypto.EC384 @@ -50,14 +52,32 @@ func (u *User) GetPrivateKey() crypto.PrivateKey { return u.Key } -func GetPrivateKey(priKey crypto.PrivateKey) []byte { - rsaKey := priKey.(*rsa.PrivateKey) - marshal := x509.MarshalPKCS1PrivateKey(rsaKey) - block := &pem.Block{ - Type: "privateKey", - Bytes: marshal, +func GetPrivateKey(priKey crypto.PrivateKey, keyType KeyType) ([]byte, error) { + var marshal []byte + var block *pem.Block + var err error + + switch keyType { + case KeyEC256, KeyEC384: + key := priKey.(*ecdsa.PrivateKey) + marshal, err = x509.MarshalECPrivateKey(key) + if err != nil { + return nil, err + } + block = &pem.Block{ + Type: "PRIVATE KEY", + Bytes: marshal, + } + case KeyRSA2048, KeyRSA3072, KeyRSA4096: + key := priKey.(*rsa.PrivateKey) + marshal = x509.MarshalPKCS1PrivateKey(key) + block = &pem.Block{ + Type: "privateKey", + Bytes: marshal, + } } - return pem.EncodeToMemory(block) + + return pem.EncodeToMemory(block), nil } func NewRegisterClient(email string, CA string, keyType certcrypto.KeyType) (*Client, error) { @@ -125,12 +145,7 @@ func NewRegisterWithExternalAccountBindingClient(email, kid, hmac, CA string, ke } func NewPrivateKeyClient(email string, privateKey string, CA string, keyType certcrypto.KeyType) (*Client, error) { - block, _ := pem.Decode([]byte(privateKey)) - if block == nil { - return nil, errors.New("无法解析私钥") - } - - key, err := certcrypto.ParsePEMPrivateKey(block.Bytes) + key, err := certcrypto.ParsePEMPrivateKey([]byte(privateKey)) if err != nil { return nil, err } diff --git a/pkg/acme/client.go b/pkg/acme/client.go index e440d417..fdbdd3ee 100644 --- a/pkg/acme/client.go +++ b/pkg/acme/client.go @@ -24,18 +24,18 @@ type Client struct { type DnsType string const ( - DnsPod DnsType = "DnsPod" - AliYun DnsType = "AliYun" - CloudFlare DnsType = "CloudFlare" + DnsPod DnsType = "dnspod" + AliYun DnsType = "aliyun" + CloudFlare DnsType = "cloudflare" ) type DNSParam struct { ID string `json:"id"` Token string `json:"token"` - AccessKey string `json:"accessKey"` - SecretKey string `json:"secretKey"` + AccessKey string `json:"access_key"` + SecretKey string `json:"secret_key"` Email string `json:"email"` - APIkey string `json:"APIkey"` + APIkey string `json:"api_key"` } // UseDns 使用 DNS 接口验证 diff --git a/routes/api.go b/routes/api.go index 3252a357..61b5c79c 100644 --- a/routes/api.go +++ b/routes/api.go @@ -11,7 +11,6 @@ import ( ) func Api() { - facades.Route().StaticFile("favicon.ico", "public/favicon.ico") facades.Route().Prefix("api/panel").Group(func(r route.Router) { r.Prefix("info").Group(func(r route.Router) { infoController := controllers.NewInfoController() @@ -57,6 +56,25 @@ func Api() { r.Post("resetConfig/{id}", websiteController.ResetConfig) r.Post("status/{id}", websiteController.Status) }) + r.Prefix("cert").Middleware(middleware.Jwt()).Group(func(r route.Router) { + certController := controllers.NewCertController() + r.Get("caProviders", certController.CAProviders) + r.Get("dnsProviders", certController.DNSProviders) + r.Get("algorithms", certController.Algorithms) + r.Get("users", certController.UserList) + r.Post("users", certController.UserAdd) + r.Delete("users/{id}", certController.UserDelete) + r.Get("dns", certController.DNSList) + r.Post("dns", certController.DNSAdd) + r.Delete("dns/{id}", certController.DNSDelete) + r.Get("certs", certController.CertList) + r.Post("certs", certController.CertAdd) + r.Delete("certs/{id}", certController.CertDelete) + // r.Get("certs/{id}", certController.CertInfo) + r.Post("obtain", certController.Obtain) + r.Post("renew", certController.Renew) + r.Post("manualDNS", certController.ManualDNS) + }) r.Prefix("plugin").Middleware(middleware.Jwt()).Group(func(r route.Router) { pluginController := controllers.NewPluginController() r.Get("list", pluginController.List) @@ -110,6 +128,10 @@ func Api() { }) }) + // 静态文件 + facades.Route().StaticFile("favicon.ico", "public/favicon.ico") + + // 文档 facades.Route().StaticFile("/swagger.json", "docs/swagger.json") facades.Route().Get("/swagger", func(ctx http.Context) http.Response { return ctx.Response().Redirect(http.StatusMovedPermanently, "/swagger/") @@ -121,6 +143,7 @@ func Api() { return nil }) + // 404 facades.Route().Fallback(func(ctx http.Context) http.Response { return ctx.Response().Data(http.StatusNotFound, "text/html; charset=utf-8", []byte(` 404 Not Found diff --git a/routes/plugin.go b/routes/plugin.go index 7a9b653f..14c78987 100644 --- a/routes/plugin.go +++ b/routes/plugin.go @@ -4,308 +4,295 @@ import ( "github.com/goravel/framework/contracts/route" "github.com/goravel/framework/facades" - "panel/app/http/controllers/plugins/fail2ban" - "panel/app/http/controllers/plugins/mysql57" - "panel/app/http/controllers/plugins/mysql80" - "panel/app/http/controllers/plugins/openresty" - "panel/app/http/controllers/plugins/php74" - "panel/app/http/controllers/plugins/php80" - "panel/app/http/controllers/plugins/php81" - "panel/app/http/controllers/plugins/php82" - "panel/app/http/controllers/plugins/phpmyadmin" - "panel/app/http/controllers/plugins/postgresql15" - "panel/app/http/controllers/plugins/postgresql16" - "panel/app/http/controllers/plugins/pureftpd" - "panel/app/http/controllers/plugins/redis" - "panel/app/http/controllers/plugins/s3fs" - "panel/app/http/controllers/plugins/supervisor" - "panel/app/http/controllers/plugins/toolbox" + "panel/app/http/controllers/plugins" "panel/app/http/middleware" ) // Plugin 加载插件路由 func Plugin() { - facades.Route().Prefix("api/plugins/openresty").Middleware(middleware.Jwt()).Group(func(route route.Router) { - openRestyController := openresty.NewOpenrestyController() - route.Get("status", openRestyController.Status) - route.Post("reload", openRestyController.Reload) - route.Post("start", openRestyController.Start) - route.Post("stop", openRestyController.Stop) - route.Post("restart", openRestyController.Restart) - route.Get("load", openRestyController.Load) - route.Get("config", openRestyController.GetConfig) - route.Post("config", openRestyController.SaveConfig) - route.Get("errorLog", openRestyController.ErrorLog) - route.Post("clearErrorLog", openRestyController.ClearErrorLog) - }) - facades.Route().Prefix("api/plugins/mysql57").Middleware(middleware.Jwt()).Group(func(route route.Router) { - mysql57Controller := mysql57.NewMysql57Controller() - route.Get("status", mysql57Controller.Status) - route.Post("reload", mysql57Controller.Reload) - route.Post("start", mysql57Controller.Start) - route.Post("stop", mysql57Controller.Stop) - route.Post("restart", mysql57Controller.Restart) - route.Get("load", mysql57Controller.Load) - route.Get("config", mysql57Controller.GetConfig) - route.Post("config", mysql57Controller.SaveConfig) - route.Get("errorLog", mysql57Controller.ErrorLog) - route.Post("clearErrorLog", mysql57Controller.ClearErrorLog) - route.Get("slowLog", mysql57Controller.SlowLog) - route.Post("clearSlowLog", mysql57Controller.ClearSlowLog) - route.Get("rootPassword", mysql57Controller.GetRootPassword) - route.Post("rootPassword", mysql57Controller.SetRootPassword) - route.Get("databases", mysql57Controller.DatabaseList) - route.Post("databases", mysql57Controller.AddDatabase) - route.Delete("databases", mysql57Controller.DeleteDatabase) - route.Get("backups", mysql57Controller.BackupList) - route.Post("backups", mysql57Controller.CreateBackup) - route.Put("backups", mysql57Controller.UploadBackup) - route.Delete("backups", mysql57Controller.DeleteBackup) - route.Post("backups/restore", mysql57Controller.RestoreBackup) - route.Get("users", mysql57Controller.UserList) - route.Post("users", mysql57Controller.AddUser) - route.Delete("users", mysql57Controller.DeleteUser) - route.Post("users/password", mysql57Controller.SetUserPassword) - route.Post("users/privileges", mysql57Controller.SetUserPrivileges) - }) - facades.Route().Prefix("api/plugins/mysql80").Middleware(middleware.Jwt()).Group(func(route route.Router) { - mysql80Controller := mysql80.NewMysql80Controller() - route.Get("status", mysql80Controller.Status) - route.Post("reload", mysql80Controller.Reload) - route.Post("start", mysql80Controller.Start) - route.Post("stop", mysql80Controller.Stop) - route.Post("restart", mysql80Controller.Restart) - route.Get("load", mysql80Controller.Load) - route.Get("config", mysql80Controller.GetConfig) - route.Post("config", mysql80Controller.SaveConfig) - route.Get("errorLog", mysql80Controller.ErrorLog) - route.Post("clearErrorLog", mysql80Controller.ClearErrorLog) - route.Get("slowLog", mysql80Controller.SlowLog) - route.Post("clearSlowLog", mysql80Controller.ClearSlowLog) - route.Get("rootPassword", mysql80Controller.GetRootPassword) - route.Post("rootPassword", mysql80Controller.SetRootPassword) - route.Get("databases", mysql80Controller.DatabaseList) - route.Post("databases", mysql80Controller.AddDatabase) - route.Delete("databases", mysql80Controller.DeleteDatabase) - route.Get("backups", mysql80Controller.BackupList) - route.Post("backups", mysql80Controller.CreateBackup) - route.Put("backups", mysql80Controller.UploadBackup) - route.Delete("backups", mysql80Controller.DeleteBackup) - route.Post("backups/restore", mysql80Controller.RestoreBackup) - route.Get("users", mysql80Controller.UserList) - route.Post("users", mysql80Controller.AddUser) - route.Delete("users", mysql80Controller.DeleteUser) - route.Post("users/password", mysql80Controller.SetUserPassword) - route.Post("users/privileges", mysql80Controller.SetUserPrivileges) - }) - facades.Route().Prefix("api/plugins/postgresql15").Middleware(middleware.Jwt()).Group(func(route route.Router) { - postgresql15Controller := postgresql15.NewPostgresql15Controller() - route.Get("status", postgresql15Controller.Status) - route.Post("reload", postgresql15Controller.Reload) - route.Post("start", postgresql15Controller.Start) - route.Post("stop", postgresql15Controller.Stop) - route.Post("restart", postgresql15Controller.Restart) - route.Get("load", postgresql15Controller.Load) - route.Get("config", postgresql15Controller.GetConfig) - route.Post("config", postgresql15Controller.SaveConfig) - route.Get("userConfig", postgresql15Controller.GetUserConfig) - route.Post("userConfig", postgresql15Controller.SaveUserConfig) - route.Get("log", postgresql15Controller.Log) - route.Post("clearLog", postgresql15Controller.ClearLog) - route.Get("databases", postgresql15Controller.DatabaseList) - route.Post("databases", postgresql15Controller.AddDatabase) - route.Delete("databases", postgresql15Controller.DeleteDatabase) - route.Get("backups", postgresql15Controller.BackupList) - route.Post("backups", postgresql15Controller.CreateBackup) - route.Put("backups", postgresql15Controller.UploadBackup) - route.Delete("backups", postgresql15Controller.DeleteBackup) - route.Post("backups/restore", postgresql15Controller.RestoreBackup) - route.Get("users", postgresql15Controller.UserList) - route.Post("users", postgresql15Controller.AddUser) - route.Delete("users", postgresql15Controller.DeleteUser) - route.Post("users/password", postgresql15Controller.SetUserPassword) - }) - facades.Route().Prefix("api/plugins/postgresql16").Middleware(middleware.Jwt()).Group(func(route route.Router) { - postgresql16Controller := postgresql16.NewPostgresql16Controller() - route.Get("status", postgresql16Controller.Status) - route.Post("reload", postgresql16Controller.Reload) - route.Post("start", postgresql16Controller.Start) - route.Post("stop", postgresql16Controller.Stop) - route.Post("restart", postgresql16Controller.Restart) - route.Get("load", postgresql16Controller.Load) - route.Get("config", postgresql16Controller.GetConfig) - route.Post("config", postgresql16Controller.SaveConfig) - route.Get("userConfig", postgresql16Controller.GetUserConfig) - route.Post("userConfig", postgresql16Controller.SaveUserConfig) - route.Get("log", postgresql16Controller.Log) - route.Post("clearLog", postgresql16Controller.ClearLog) - route.Get("databases", postgresql16Controller.DatabaseList) - route.Post("databases", postgresql16Controller.AddDatabase) - route.Delete("databases", postgresql16Controller.DeleteDatabase) - route.Get("backups", postgresql16Controller.BackupList) - route.Post("backups", postgresql16Controller.CreateBackup) - route.Put("backups", postgresql16Controller.UploadBackup) - route.Delete("backups", postgresql16Controller.DeleteBackup) - route.Post("backups/restore", postgresql16Controller.RestoreBackup) - route.Get("users", postgresql16Controller.UserList) - route.Post("users", postgresql16Controller.AddUser) - route.Delete("users", postgresql16Controller.DeleteUser) - route.Post("users/password", postgresql16Controller.SetUserPassword) - }) - facades.Route().Prefix("api/plugins/php74").Middleware(middleware.Jwt()).Group(func(route route.Router) { - php74Controller := php74.NewPhp74Controller() - route.Get("status", php74Controller.Status) - route.Post("reload", php74Controller.Reload) - route.Post("start", php74Controller.Start) - route.Post("stop", php74Controller.Stop) - route.Post("restart", php74Controller.Restart) - route.Get("load", php74Controller.Load) - route.Get("config", php74Controller.GetConfig) - route.Post("config", php74Controller.SaveConfig) - route.Get("errorLog", php74Controller.ErrorLog) - route.Get("slowLog", php74Controller.SlowLog) - route.Post("clearErrorLog", php74Controller.ClearErrorLog) - route.Post("clearSlowLog", php74Controller.ClearSlowLog) - route.Get("extensions", php74Controller.GetExtensionList) - route.Post("extensions", php74Controller.InstallExtension) - route.Delete("extensions", php74Controller.UninstallExtension) - }) - facades.Route().Prefix("api/plugins/php80").Middleware(middleware.Jwt()).Group(func(route route.Router) { - php80Controller := php80.NewPhp80Controller() - route.Get("status", php80Controller.Status) - route.Post("reload", php80Controller.Reload) - route.Post("start", php80Controller.Start) - route.Post("stop", php80Controller.Stop) - route.Post("restart", php80Controller.Restart) - route.Get("load", php80Controller.Load) - route.Get("config", php80Controller.GetConfig) - route.Post("config", php80Controller.SaveConfig) - route.Get("errorLog", php80Controller.ErrorLog) - route.Get("slowLog", php80Controller.SlowLog) - route.Post("clearErrorLog", php80Controller.ClearErrorLog) - route.Post("clearSlowLog", php80Controller.ClearSlowLog) - route.Get("extensions", php80Controller.GetExtensionList) - route.Post("extensions", php80Controller.InstallExtension) - route.Delete("extensions", php80Controller.UninstallExtension) - }) - facades.Route().Prefix("api/plugins/php81").Middleware(middleware.Jwt()).Group(func(route route.Router) { - php81Controller := php81.NewPhp81Controller() - route.Get("status", php81Controller.Status) - route.Post("reload", php81Controller.Reload) - route.Post("start", php81Controller.Start) - route.Post("stop", php81Controller.Stop) - route.Post("restart", php81Controller.Restart) - route.Get("load", php81Controller.Load) - route.Get("config", php81Controller.GetConfig) - route.Post("config", php81Controller.SaveConfig) - route.Get("errorLog", php81Controller.ErrorLog) - route.Get("slowLog", php81Controller.SlowLog) - route.Post("clearErrorLog", php81Controller.ClearErrorLog) - route.Post("clearSlowLog", php81Controller.ClearSlowLog) - route.Get("extensions", php81Controller.GetExtensionList) - route.Post("extensions", php81Controller.InstallExtension) - route.Delete("extensions", php81Controller.UninstallExtension) - }) - facades.Route().Prefix("api/plugins/php82").Middleware(middleware.Jwt()).Group(func(route route.Router) { - php82Controller := php82.NewPhp82Controller() - route.Get("status", php82Controller.Status) - route.Post("reload", php82Controller.Reload) - route.Post("start", php82Controller.Start) - route.Post("stop", php82Controller.Stop) - route.Post("restart", php82Controller.Restart) - route.Get("load", php82Controller.Load) - route.Get("config", php82Controller.GetConfig) - route.Post("config", php82Controller.SaveConfig) - route.Get("errorLog", php82Controller.ErrorLog) - route.Get("slowLog", php82Controller.SlowLog) - route.Post("clearErrorLog", php82Controller.ClearErrorLog) - route.Post("clearSlowLog", php82Controller.ClearSlowLog) - route.Get("extensions", php82Controller.GetExtensionList) - route.Post("extensions", php82Controller.InstallExtension) - route.Delete("extensions", php82Controller.UninstallExtension) - }) - facades.Route().Prefix("api/plugins/phpmyadmin").Middleware(middleware.Jwt()).Group(func(route route.Router) { - phpMyAdminController := phpmyadmin.NewPhpMyAdminController() - route.Get("info", phpMyAdminController.Info) - route.Post("port", phpMyAdminController.SetPort) - }) - facades.Route().Prefix("api/plugins/pureftpd").Middleware(middleware.Jwt()).Group(func(route route.Router) { - pureFtpdController := pureftpd.NewPureFtpdController() - route.Get("status", pureFtpdController.Status) - route.Post("start", pureFtpdController.Start) - route.Post("stop", pureFtpdController.Stop) - route.Post("restart", pureFtpdController.Restart) - route.Get("list", pureFtpdController.List) - route.Post("add", pureFtpdController.Add) - route.Delete("delete", pureFtpdController.Delete) - route.Post("changePassword", pureFtpdController.ChangePassword) - route.Get("port", pureFtpdController.GetPort) - route.Post("port", pureFtpdController.SetPort) - }) - facades.Route().Prefix("api/plugins/redis").Middleware(middleware.Jwt()).Group(func(route route.Router) { - redisController := redis.NewRedisController() - route.Get("status", redisController.Status) - route.Post("start", redisController.Start) - route.Post("stop", redisController.Stop) - route.Post("restart", redisController.Restart) - route.Get("load", redisController.Load) - route.Get("config", redisController.GetConfig) - route.Post("config", redisController.SaveConfig) - }) - facades.Route().Prefix("api/plugins/s3fs").Middleware(middleware.Jwt()).Group(func(route route.Router) { - s3fsController := s3fs.NewS3fsController() - route.Get("list", s3fsController.List) - route.Post("add", s3fsController.Add) - route.Post("delete", s3fsController.Delete) - }) - facades.Route().Prefix("api/plugins/supervisor").Middleware(middleware.Jwt()).Group(func(route route.Router) { - supervisorController := supervisor.NewSupervisorController() - route.Get("status", supervisorController.Status) - route.Post("start", supervisorController.Start) - route.Post("stop", supervisorController.Stop) - route.Post("restart", supervisorController.Restart) - route.Post("reload", supervisorController.Reload) - route.Get("log", supervisorController.Log) - route.Post("clearLog", supervisorController.ClearLog) - route.Get("config", supervisorController.Config) - route.Post("config", supervisorController.SaveConfig) - route.Get("processes", supervisorController.Processes) - route.Post("startProcess", supervisorController.StartProcess) - route.Post("stopProcess", supervisorController.StopProcess) - route.Post("restartProcess", supervisorController.RestartProcess) - route.Get("processLog", supervisorController.ProcessLog) - route.Post("clearProcessLog", supervisorController.ClearProcessLog) - route.Get("processConfig", supervisorController.ProcessConfig) - route.Post("processConfig", supervisorController.SaveProcessConfig) - route.Post("deleteProcess", supervisorController.DeleteProcess) - route.Post("addProcess", supervisorController.AddProcess) + facades.Route().Prefix("api/plugins").Middleware(middleware.Jwt()).Group(func(r route.Router) { + r.Prefix("openresty").Group(func(route route.Router) { + openRestyController := plugins.NewOpenrestyController() + route.Get("status", openRestyController.Status) + route.Post("reload", openRestyController.Reload) + route.Post("start", openRestyController.Start) + route.Post("stop", openRestyController.Stop) + route.Post("restart", openRestyController.Restart) + route.Get("load", openRestyController.Load) + route.Get("config", openRestyController.GetConfig) + route.Post("config", openRestyController.SaveConfig) + route.Get("errorLog", openRestyController.ErrorLog) + route.Post("clearErrorLog", openRestyController.ClearErrorLog) + }) + r.Prefix("mysql57").Group(func(route route.Router) { + mysql57Controller := plugins.NewMysql57Controller() + route.Get("status", mysql57Controller.Status) + route.Post("reload", mysql57Controller.Reload) + route.Post("start", mysql57Controller.Start) + route.Post("stop", mysql57Controller.Stop) + route.Post("restart", mysql57Controller.Restart) + route.Get("load", mysql57Controller.Load) + route.Get("config", mysql57Controller.GetConfig) + route.Post("config", mysql57Controller.SaveConfig) + route.Get("errorLog", mysql57Controller.ErrorLog) + route.Post("clearErrorLog", mysql57Controller.ClearErrorLog) + route.Get("slowLog", mysql57Controller.SlowLog) + route.Post("clearSlowLog", mysql57Controller.ClearSlowLog) + route.Get("rootPassword", mysql57Controller.GetRootPassword) + route.Post("rootPassword", mysql57Controller.SetRootPassword) + route.Get("databases", mysql57Controller.DatabaseList) + route.Post("databases", mysql57Controller.AddDatabase) + route.Delete("databases", mysql57Controller.DeleteDatabase) + route.Get("backups", mysql57Controller.BackupList) + route.Post("backups", mysql57Controller.CreateBackup) + route.Put("backups", mysql57Controller.UploadBackup) + route.Delete("backups", mysql57Controller.DeleteBackup) + route.Post("backups/restore", mysql57Controller.RestoreBackup) + route.Get("users", mysql57Controller.UserList) + route.Post("users", mysql57Controller.AddUser) + route.Delete("users", mysql57Controller.DeleteUser) + route.Post("users/password", mysql57Controller.SetUserPassword) + route.Post("users/privileges", mysql57Controller.SetUserPrivileges) + }) + r.Prefix("mysql80").Group(func(route route.Router) { + mysql80Controller := plugins.NewMysql80Controller() + route.Get("status", mysql80Controller.Status) + route.Post("reload", mysql80Controller.Reload) + route.Post("start", mysql80Controller.Start) + route.Post("stop", mysql80Controller.Stop) + route.Post("restart", mysql80Controller.Restart) + route.Get("load", mysql80Controller.Load) + route.Get("config", mysql80Controller.GetConfig) + route.Post("config", mysql80Controller.SaveConfig) + route.Get("errorLog", mysql80Controller.ErrorLog) + route.Post("clearErrorLog", mysql80Controller.ClearErrorLog) + route.Get("slowLog", mysql80Controller.SlowLog) + route.Post("clearSlowLog", mysql80Controller.ClearSlowLog) + route.Get("rootPassword", mysql80Controller.GetRootPassword) + route.Post("rootPassword", mysql80Controller.SetRootPassword) + route.Get("databases", mysql80Controller.DatabaseList) + route.Post("databases", mysql80Controller.AddDatabase) + route.Delete("databases", mysql80Controller.DeleteDatabase) + route.Get("backups", mysql80Controller.BackupList) + route.Post("backups", mysql80Controller.CreateBackup) + route.Put("backups", mysql80Controller.UploadBackup) + route.Delete("backups", mysql80Controller.DeleteBackup) + route.Post("backups/restore", mysql80Controller.RestoreBackup) + route.Get("users", mysql80Controller.UserList) + route.Post("users", mysql80Controller.AddUser) + route.Delete("users", mysql80Controller.DeleteUser) + route.Post("users/password", mysql80Controller.SetUserPassword) + route.Post("users/privileges", mysql80Controller.SetUserPrivileges) + }) + r.Prefix("postgresql15").Group(func(route route.Router) { + postgresql15Controller := plugins.NewPostgresql15Controller() + route.Get("status", postgresql15Controller.Status) + route.Post("reload", postgresql15Controller.Reload) + route.Post("start", postgresql15Controller.Start) + route.Post("stop", postgresql15Controller.Stop) + route.Post("restart", postgresql15Controller.Restart) + route.Get("load", postgresql15Controller.Load) + route.Get("config", postgresql15Controller.GetConfig) + route.Post("config", postgresql15Controller.SaveConfig) + route.Get("userConfig", postgresql15Controller.GetUserConfig) + route.Post("userConfig", postgresql15Controller.SaveUserConfig) + route.Get("log", postgresql15Controller.Log) + route.Post("clearLog", postgresql15Controller.ClearLog) + route.Get("databases", postgresql15Controller.DatabaseList) + route.Post("databases", postgresql15Controller.AddDatabase) + route.Delete("databases", postgresql15Controller.DeleteDatabase) + route.Get("backups", postgresql15Controller.BackupList) + route.Post("backups", postgresql15Controller.CreateBackup) + route.Put("backups", postgresql15Controller.UploadBackup) + route.Delete("backups", postgresql15Controller.DeleteBackup) + route.Post("backups/restore", postgresql15Controller.RestoreBackup) + route.Get("users", postgresql15Controller.UserList) + route.Post("users", postgresql15Controller.AddUser) + route.Delete("users", postgresql15Controller.DeleteUser) + route.Post("users/password", postgresql15Controller.SetUserPassword) + }) + r.Prefix("postgresql16").Group(func(route route.Router) { + postgresql16Controller := plugins.NewPostgresql16Controller() + route.Get("status", postgresql16Controller.Status) + route.Post("reload", postgresql16Controller.Reload) + route.Post("start", postgresql16Controller.Start) + route.Post("stop", postgresql16Controller.Stop) + route.Post("restart", postgresql16Controller.Restart) + route.Get("load", postgresql16Controller.Load) + route.Get("config", postgresql16Controller.GetConfig) + route.Post("config", postgresql16Controller.SaveConfig) + route.Get("userConfig", postgresql16Controller.GetUserConfig) + route.Post("userConfig", postgresql16Controller.SaveUserConfig) + route.Get("log", postgresql16Controller.Log) + route.Post("clearLog", postgresql16Controller.ClearLog) + route.Get("databases", postgresql16Controller.DatabaseList) + route.Post("databases", postgresql16Controller.AddDatabase) + route.Delete("databases", postgresql16Controller.DeleteDatabase) + route.Get("backups", postgresql16Controller.BackupList) + route.Post("backups", postgresql16Controller.CreateBackup) + route.Put("backups", postgresql16Controller.UploadBackup) + route.Delete("backups", postgresql16Controller.DeleteBackup) + route.Post("backups/restore", postgresql16Controller.RestoreBackup) + route.Get("users", postgresql16Controller.UserList) + route.Post("users", postgresql16Controller.AddUser) + route.Delete("users", postgresql16Controller.DeleteUser) + route.Post("users/password", postgresql16Controller.SetUserPassword) + }) + r.Prefix("php74").Group(func(route route.Router) { + php74Controller := plugins.NewPhp74Controller() + route.Get("status", php74Controller.Status) + route.Post("reload", php74Controller.Reload) + route.Post("start", php74Controller.Start) + route.Post("stop", php74Controller.Stop) + route.Post("restart", php74Controller.Restart) + route.Get("load", php74Controller.Load) + route.Get("config", php74Controller.GetConfig) + route.Post("config", php74Controller.SaveConfig) + route.Get("errorLog", php74Controller.ErrorLog) + route.Get("slowLog", php74Controller.SlowLog) + route.Post("clearErrorLog", php74Controller.ClearErrorLog) + route.Post("clearSlowLog", php74Controller.ClearSlowLog) + route.Get("extensions", php74Controller.GetExtensionList) + route.Post("extensions", php74Controller.InstallExtension) + route.Delete("extensions", php74Controller.UninstallExtension) + }) + r.Prefix("php80").Group(func(route route.Router) { + php80Controller := plugins.NewPhp80Controller() + route.Get("status", php80Controller.Status) + route.Post("reload", php80Controller.Reload) + route.Post("start", php80Controller.Start) + route.Post("stop", php80Controller.Stop) + route.Post("restart", php80Controller.Restart) + route.Get("load", php80Controller.Load) + route.Get("config", php80Controller.GetConfig) + route.Post("config", php80Controller.SaveConfig) + route.Get("errorLog", php80Controller.ErrorLog) + route.Get("slowLog", php80Controller.SlowLog) + route.Post("clearErrorLog", php80Controller.ClearErrorLog) + route.Post("clearSlowLog", php80Controller.ClearSlowLog) + route.Get("extensions", php80Controller.GetExtensionList) + route.Post("extensions", php80Controller.InstallExtension) + route.Delete("extensions", php80Controller.UninstallExtension) + }) + r.Prefix("php81").Group(func(route route.Router) { + php81Controller := plugins.NewPhp81Controller() + route.Get("status", php81Controller.Status) + route.Post("reload", php81Controller.Reload) + route.Post("start", php81Controller.Start) + route.Post("stop", php81Controller.Stop) + route.Post("restart", php81Controller.Restart) + route.Get("load", php81Controller.Load) + route.Get("config", php81Controller.GetConfig) + route.Post("config", php81Controller.SaveConfig) + route.Get("errorLog", php81Controller.ErrorLog) + route.Get("slowLog", php81Controller.SlowLog) + route.Post("clearErrorLog", php81Controller.ClearErrorLog) + route.Post("clearSlowLog", php81Controller.ClearSlowLog) + route.Get("extensions", php81Controller.GetExtensionList) + route.Post("extensions", php81Controller.InstallExtension) + route.Delete("extensions", php81Controller.UninstallExtension) + }) + r.Prefix("php82").Group(func(route route.Router) { + php82Controller := plugins.NewPhp82Controller() + route.Get("status", php82Controller.Status) + route.Post("reload", php82Controller.Reload) + route.Post("start", php82Controller.Start) + route.Post("stop", php82Controller.Stop) + route.Post("restart", php82Controller.Restart) + route.Get("load", php82Controller.Load) + route.Get("config", php82Controller.GetConfig) + route.Post("config", php82Controller.SaveConfig) + route.Get("errorLog", php82Controller.ErrorLog) + route.Get("slowLog", php82Controller.SlowLog) + route.Post("clearErrorLog", php82Controller.ClearErrorLog) + route.Post("clearSlowLog", php82Controller.ClearSlowLog) + route.Get("extensions", php82Controller.GetExtensionList) + route.Post("extensions", php82Controller.InstallExtension) + route.Delete("extensions", php82Controller.UninstallExtension) + }) + r.Prefix("phpmyadmin").Group(func(route route.Router) { + phpMyAdminController := plugins.NewPhpMyAdminController() + route.Get("info", phpMyAdminController.Info) + route.Post("port", phpMyAdminController.SetPort) + }) + r.Prefix("pureftpd").Group(func(route route.Router) { + pureFtpdController := plugins.NewPureFtpdController() + route.Get("status", pureFtpdController.Status) + route.Post("start", pureFtpdController.Start) + route.Post("stop", pureFtpdController.Stop) + route.Post("restart", pureFtpdController.Restart) + route.Get("list", pureFtpdController.List) + route.Post("add", pureFtpdController.Add) + route.Delete("delete", pureFtpdController.Delete) + route.Post("changePassword", pureFtpdController.ChangePassword) + route.Get("port", pureFtpdController.GetPort) + route.Post("port", pureFtpdController.SetPort) + }) + r.Prefix("redis").Group(func(route route.Router) { + redisController := plugins.NewRedisController() + route.Get("status", redisController.Status) + route.Post("start", redisController.Start) + route.Post("stop", redisController.Stop) + route.Post("restart", redisController.Restart) + route.Get("load", redisController.Load) + route.Get("config", redisController.GetConfig) + route.Post("config", redisController.SaveConfig) + }) + r.Prefix("s3fs").Group(func(route route.Router) { + s3fsController := plugins.NewS3fsController() + route.Get("list", s3fsController.List) + route.Post("add", s3fsController.Add) + route.Post("delete", s3fsController.Delete) + }) + r.Prefix("supervisor").Group(func(route route.Router) { + supervisorController := plugins.NewSupervisorController() + route.Get("status", supervisorController.Status) + route.Post("start", supervisorController.Start) + route.Post("stop", supervisorController.Stop) + route.Post("restart", supervisorController.Restart) + route.Post("reload", supervisorController.Reload) + route.Get("log", supervisorController.Log) + route.Post("clearLog", supervisorController.ClearLog) + route.Get("config", supervisorController.Config) + route.Post("config", supervisorController.SaveConfig) + route.Get("processes", supervisorController.Processes) + route.Post("startProcess", supervisorController.StartProcess) + route.Post("stopProcess", supervisorController.StopProcess) + route.Post("restartProcess", supervisorController.RestartProcess) + route.Get("processLog", supervisorController.ProcessLog) + route.Post("clearProcessLog", supervisorController.ClearProcessLog) + route.Get("processConfig", supervisorController.ProcessConfig) + route.Post("processConfig", supervisorController.SaveProcessConfig) + route.Post("deleteProcess", supervisorController.DeleteProcess) + route.Post("addProcess", supervisorController.AddProcess) - }) - facades.Route().Prefix("api/plugins/fail2ban").Middleware(middleware.Jwt()).Group(func(route route.Router) { - fail2banController := fail2ban.NewFail2banController() - route.Get("status", fail2banController.Status) - route.Post("start", fail2banController.Start) - route.Post("stop", fail2banController.Stop) - route.Post("restart", fail2banController.Restart) - route.Post("reload", fail2banController.Reload) - route.Get("jails", fail2banController.List) - route.Post("jails", fail2banController.Add) - route.Delete("jails", fail2banController.Delete) - route.Get("jails/{name}", fail2banController.BanList) - route.Post("unban", fail2banController.Unban) - route.Post("whiteList", fail2banController.SetWhiteList) - route.Get("whiteList", fail2banController.GetWhiteList) - }) - facades.Route().Prefix("api/plugins/toolbox").Middleware(middleware.Jwt()).Group(func(route route.Router) { - toolboxController := toolbox.NewToolBoxController() - route.Get("dns", toolboxController.GetDNS) - route.Post("dns", toolboxController.SetDNS) - route.Get("swap", toolboxController.GetSWAP) - route.Post("swap", toolboxController.SetSWAP) - route.Get("timezone", toolboxController.GetTimezone) - route.Post("timezone", toolboxController.SetTimezone) - route.Get("hosts", toolboxController.GetHosts) - route.Post("hosts", toolboxController.SetHosts) - route.Post("rootPassword", toolboxController.SetRootPassword) + }) + r.Prefix("fail2ban").Group(func(route route.Router) { + fail2banController := plugins.NewFail2banController() + route.Get("status", fail2banController.Status) + route.Post("start", fail2banController.Start) + route.Post("stop", fail2banController.Stop) + route.Post("restart", fail2banController.Restart) + route.Post("reload", fail2banController.Reload) + route.Get("jails", fail2banController.List) + route.Post("jails", fail2banController.Add) + route.Delete("jails", fail2banController.Delete) + route.Get("jails/{name}", fail2banController.BanList) + route.Post("unban", fail2banController.Unban) + route.Post("whiteList", fail2banController.SetWhiteList) + route.Get("whiteList", fail2banController.GetWhiteList) + }) + r.Prefix("toolbox").Group(func(route route.Router) { + toolboxController := plugins.NewToolBoxController() + route.Get("dns", toolboxController.GetDNS) + route.Post("dns", toolboxController.SetDNS) + route.Get("swap", toolboxController.GetSWAP) + route.Post("swap", toolboxController.SetSWAP) + route.Get("timezone", toolboxController.GetTimezone) + route.Post("timezone", toolboxController.SetTimezone) + route.Get("hosts", toolboxController.GetHosts) + route.Post("hosts", toolboxController.SetHosts) + route.Post("rootPassword", toolboxController.SetRootPassword) + }) }) }