From 70601dfb006fcd4b81aafef0b7c6146a67b09612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Tue, 7 Nov 2023 02:05:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=AF=81=E4=B9=A6=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E8=A1=A5=E5=85=A8=E6=9B=B4=E6=96=B0=E5=92=8C=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/http/controllers/cert_controller.go | 268 +++++++++-- app/http/requests/cert/cert_add.go | 49 -- app/http/requests/cert/cert_deploy.go | 34 ++ .../requests/cert/cert_show_and_destroy.go | 32 ++ app/http/requests/cert/cert_store.go | 40 ++ app/http/requests/cert/cert_update.go | 42 ++ app/http/requests/cert/dns_add.go | 54 --- .../requests/cert/dns_show_and_destroy.go | 32 ++ app/http/requests/cert/dns_store.go | 44 ++ app/http/requests/cert/dns_update.go | 46 ++ app/http/requests/cert/obtain.go | 5 +- app/http/requests/cert/renew.go | 5 +- app/http/requests/cert/user_add.go | 49 -- .../requests/cert/user_show_and_destroy.go | 32 ++ app/http/requests/cert/user_store.go | 40 ++ app/http/requests/cert/user_update.go | 42 ++ app/services/cert.go | 155 +++++- docs/docs.go | 447 +++++++++++++++++- docs/swagger.json | 447 +++++++++++++++++- docs/swagger.yaml | 284 ++++++++++- routes/api.go | 19 +- 21 files changed, 1920 insertions(+), 246 deletions(-) delete mode 100644 app/http/requests/cert/cert_add.go create mode 100644 app/http/requests/cert/cert_deploy.go create mode 100644 app/http/requests/cert/cert_show_and_destroy.go create mode 100644 app/http/requests/cert/cert_store.go create mode 100644 app/http/requests/cert/cert_update.go delete mode 100644 app/http/requests/cert/dns_add.go create mode 100644 app/http/requests/cert/dns_show_and_destroy.go create mode 100644 app/http/requests/cert/dns_store.go create mode 100644 app/http/requests/cert/dns_update.go delete mode 100644 app/http/requests/cert/user_add.go create mode 100644 app/http/requests/cert/user_show_and_destroy.go create mode 100644 app/http/requests/cert/user_store.go create mode 100644 app/http/requests/cert/user_update.go diff --git a/app/http/controllers/cert_controller.go b/app/http/controllers/cert_controller.go index 0f815c25..e587c8fc 100644 --- a/app/http/controllers/cert_controller.go +++ b/app/http/controllers/cert_controller.go @@ -3,8 +3,8 @@ package controllers import ( "github.com/goravel/framework/contracts/http" "github.com/goravel/framework/facades" - requests "panel/app/http/requests/cert" + requests "panel/app/http/requests/cert" commonrequests "panel/app/http/requests/common" responses "panel/app/http/responses/cert" "panel/app/models" @@ -147,26 +147,26 @@ func (r *CertController) UserList(ctx http.Context) http.Response { }) } -// UserAdd +// UserStore // @Summary 添加 ACME 用户 // @Description 添加 ACME 用户到面板证书管理 // @Tags 证书 // @Accept json // @Produce json // @Security BearerToken -// @Param data body requests.UserAdd true "用户信息" +// @Param data body requests.UserStore 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) +func (r *CertController) UserStore(ctx http.Context) http.Response { + var storeRequest requests.UserStore + sanitize := Sanitize(ctx, &storeRequest) if sanitize != nil { return sanitize } - err := r.cert.UserAdd(addRequest) + err := r.cert.UserStore(storeRequest) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ "error": err.Error(), @@ -177,7 +177,69 @@ func (r *CertController) UserAdd(ctx http.Context) http.Response { return Success(ctx, nil) } -// UserDelete +// UserUpdate +// @Summary 更新 ACME 用户 +// @Description 更新面板证书管理的 ACME 用户 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param id path int true "用户 ID" +// @Param data body requests.UserUpdate true "用户信息" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/users/{id} [put] +func (r *CertController) UserUpdate(ctx http.Context) http.Response { + var updateRequest requests.UserUpdate + sanitize := Sanitize(ctx, &updateRequest) + if sanitize != nil { + return sanitize + } + + err := r.cert.UserUpdate(updateRequest) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "userID": updateRequest.ID, + "error": err.Error(), + }).Error("更新ACME用户失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// UserShow +// @Summary 获取 ACME 用户 +// @Description 获取面板证书管理的 ACME 用户 +// @Tags 证书 +// @Produce json +// @Security BearerToken +// @Param id path int true "用户 ID" +// @Success 200 {object} SuccessResponse{data=models.CertUser} +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/users/{id} [get] +func (r *CertController) UserShow(ctx http.Context) http.Response { + var showAndDestroyRequest requests.UserShowAndDestroy + sanitize := Sanitize(ctx, &showAndDestroyRequest) + if sanitize != nil { + return sanitize + } + + user, err := r.cert.UserShow(showAndDestroyRequest.ID) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "userID": showAndDestroyRequest.ID, + "error": err.Error(), + }).Error("获取ACME用户失败") + return ErrorSystem(ctx) + } + + return Success(ctx, user) +} + +// UserDestroy // @Summary 删除 ACME 用户 // @Description 删除面板证书管理的 ACME 用户 // @Tags 证书 @@ -189,13 +251,17 @@ func (r *CertController) UserAdd(ctx http.Context) http.Response { // @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") +func (r *CertController) UserDestroy(ctx http.Context) http.Response { + var showAndDestroyRequest requests.UserShowAndDestroy + sanitize := Sanitize(ctx, &showAndDestroyRequest) + if sanitize != nil { + return sanitize + } - err := r.cert.UserDelete(uint(userID)) + err := r.cert.UserDestroy(showAndDestroyRequest.ID) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ - "userID": userID, + "userID": showAndDestroyRequest.ID, "error": err.Error(), }).Error("删除ACME用户失败") return ErrorSystem(ctx) @@ -237,26 +303,26 @@ func (r *CertController) DNSList(ctx http.Context) http.Response { }) } -// DNSAdd +// DNSStore // @Summary 添加 DNS 接口 // @Description 添加 DNS 接口到面板证书管理 // @Tags 证书 // @Accept json // @Produce json // @Security BearerToken -// @Param data body requests.DNSAdd true "DNS 接口信息" +// @Param data body requests.DNSStore 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) +func (r *CertController) DNSStore(ctx http.Context) http.Response { + var storeRequest requests.DNSStore + sanitize := Sanitize(ctx, &storeRequest) if sanitize != nil { return sanitize } - err := r.cert.DNSAdd(addRequest) + err := r.cert.DNSStore(storeRequest) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ "error": err.Error(), @@ -267,7 +333,69 @@ func (r *CertController) DNSAdd(ctx http.Context) http.Response { return Success(ctx, nil) } -// DNSDelete +// DNSShow +// @Summary 获取 DNS 接口 +// @Description 获取面板证书管理的 DNS 接口 +// @Tags 证书 +// @Produce json +// @Security BearerToken +// @Param id path int true "DNS 接口 ID" +// @Success 200 {object} SuccessResponse{data=models.CertDNS} +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/dns/{id} [get] +func (r *CertController) DNSShow(ctx http.Context) http.Response { + var showAndDestroyRequest requests.DNSShowAndDestroy + sanitize := Sanitize(ctx, &showAndDestroyRequest) + if sanitize != nil { + return sanitize + } + + dns, err := r.cert.DNSShow(showAndDestroyRequest.ID) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "dnsID": showAndDestroyRequest.ID, + "error": err.Error(), + }).Error("获取DNS接口失败") + return ErrorSystem(ctx) + } + + return Success(ctx, dns) +} + +// DNSUpdate +// @Summary 更新 DNS 接口 +// @Description 更新面板证书管理的 DNS 接口 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param id path int true "DNS 接口 ID" +// @Param data body requests.DNSUpdate true "DNS 接口信息" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/dns/{id} [put] +func (r *CertController) DNSUpdate(ctx http.Context) http.Response { + var updateRequest requests.DNSUpdate + sanitize := Sanitize(ctx, &updateRequest) + if sanitize != nil { + return sanitize + } + + err := r.cert.DNSUpdate(updateRequest) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "dnsID": updateRequest.ID, + "error": err.Error(), + }).Error("更新DNS接口失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// DNSDestroy // @Summary 删除 DNS 接口 // @Description 删除面板证书管理的 DNS 接口 // @Tags 证书 @@ -279,13 +407,17 @@ func (r *CertController) DNSAdd(ctx http.Context) http.Response { // @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") +func (r *CertController) DNSDestroy(ctx http.Context) http.Response { + var showAndDestroyRequest requests.DNSShowAndDestroy + sanitize := Sanitize(ctx, &showAndDestroyRequest) + if sanitize != nil { + return sanitize + } - err := r.cert.DNSDelete(uint(dnsID)) + err := r.cert.DNSDestroy(showAndDestroyRequest.ID) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ - "dnsID": dnsID, + "dnsID": showAndDestroyRequest.ID, "error": err.Error(), }).Error("删除DNS接口失败") return ErrorSystem(ctx) @@ -327,26 +459,26 @@ func (r *CertController) CertList(ctx http.Context) http.Response { }) } -// CertAdd +// CertStore // @Summary 添加证书 // @Description 添加证书到面板证书管理 // @Tags 证书 // @Accept json // @Produce json // @Security BearerToken -// @Param data body requests.CertAdd true "证书信息" +// @Param data body requests.CertStore 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) +func (r *CertController) CertStore(ctx http.Context) http.Response { + var storeRequest requests.CertStore + sanitize := Sanitize(ctx, &storeRequest) if sanitize != nil { return sanitize } - err := r.cert.CertAdd(addRequest) + err := r.cert.CertStore(storeRequest) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ "error": err.Error(), @@ -357,7 +489,69 @@ func (r *CertController) CertAdd(ctx http.Context) http.Response { return Success(ctx, nil) } -// CertDelete +// CertUpdate +// @Summary 更新证书 +// @Description 更新面板证书管理的证书 +// @Tags 证书 +// @Accept json +// @Produce json +// @Security BearerToken +// @Param id path int true "证书 ID" +// @Param data body requests.CertUpdate true "证书信息" +// @Success 200 {object} SuccessResponse +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/certs/{id} [put] +func (r *CertController) CertUpdate(ctx http.Context) http.Response { + var updateRequest requests.CertUpdate + sanitize := Sanitize(ctx, &updateRequest) + if sanitize != nil { + return sanitize + } + + err := r.cert.CertUpdate(updateRequest) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "certID": updateRequest.ID, + "error": err.Error(), + }).Error("更新证书失败") + return ErrorSystem(ctx) + } + + return Success(ctx, nil) +} + +// CertShow +// @Summary 获取证书 +// @Description 获取面板证书管理的证书 +// @Tags 证书 +// @Produce json +// @Security BearerToken +// @Param id path int true "证书 ID" +// @Success 200 {object} SuccessResponse{data=models.Cert} +// @Failure 401 {object} ErrorResponse "登录已过期" +// @Failure 500 {object} ErrorResponse "系统内部错误" +// @Router /panel/cert/certs/{id} [get] +func (r *CertController) CertShow(ctx http.Context) http.Response { + var showAndDestroyRequest requests.CertShowAndDestroy + sanitize := Sanitize(ctx, &showAndDestroyRequest) + if sanitize != nil { + return sanitize + } + + cert, err := r.cert.CertShow(showAndDestroyRequest.ID) + if err != nil { + facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ + "certID": showAndDestroyRequest.ID, + "error": err.Error(), + }).Error("获取证书失败") + return ErrorSystem(ctx) + } + + return Success(ctx, cert) +} + +// CertDestroy // @Summary 删除证书 // @Description 删除面板证书管理的证书 // @Tags 证书 @@ -369,13 +563,17 @@ func (r *CertController) CertAdd(ctx http.Context) http.Response { // @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") +func (r *CertController) CertDestroy(ctx http.Context) http.Response { + var showAndDestroyRequest requests.CertShowAndDestroy + sanitize := Sanitize(ctx, &showAndDestroyRequest) + if sanitize != nil { + return sanitize + } - err := r.cert.CertDelete(uint(certID)) + err := r.cert.CertDestroy(showAndDestroyRequest.ID) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ - "certID": certID, + "certID": showAndDestroyRequest.ID, "error": err.Error(), }).Error("删除证书失败") return ErrorSystem(ctx) @@ -403,7 +601,7 @@ func (r *CertController) Obtain(ctx http.Context) http.Response { return sanitize } - cert, err := r.cert.GetByID(obtainRequest.ID) + cert, err := r.cert.CertShow(obtainRequest.ID) if err != nil { facades.Log().Request(ctx.Request()).Tags("面板", "证书管理").With(map[string]any{ "certID": obtainRequest.ID, diff --git a/app/http/requests/cert/cert_add.go b/app/http/requests/cert/cert_add.go deleted file mode 100644 index da05259f..00000000 --- a/app/http/requests/cert/cert_add.go +++ /dev/null @@ -1,49 +0,0 @@ -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"` - AutoRenew bool `form:"auto_renew" json:"auto_renew"` - UserID uint `form:"user_id" json:"user_id"` - DNSID *uint `form:"dns_id" json:"dns_id"` - WebsiteID *uint `form:"website_id" json:"website_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", - "auto_renew": "required|bool", - "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.array": "域名必须为数组", - "auto_renew.required": "自动续签不能为空", - "auto_renew.bool": "自动续签必须为布尔值", - "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 { - return nil -} diff --git a/app/http/requests/cert/cert_deploy.go b/app/http/requests/cert/cert_deploy.go new file mode 100644 index 00000000..473cdc37 --- /dev/null +++ b/app/http/requests/cert/cert_deploy.go @@ -0,0 +1,34 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type CertDeploy struct { + ID uint `form:"id" json:"id"` + WebsiteID uint `form:"website_id" json:"website_id"` +} + +func (r *CertDeploy) Authorize(ctx http.Context) error { + return nil +} + +func (r *CertDeploy) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|uint|min:1|exists:certs,id", + "website_id": "required|uint|min:1|exists:websites,id", + } +} + +func (r *CertDeploy) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *CertDeploy) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *CertDeploy) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/cert_show_and_destroy.go b/app/http/requests/cert/cert_show_and_destroy.go new file mode 100644 index 00000000..924d7512 --- /dev/null +++ b/app/http/requests/cert/cert_show_and_destroy.go @@ -0,0 +1,32 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type CertShowAndDestroy struct { + ID uint `form:"id" json:"id"` +} + +func (r *CertShowAndDestroy) Authorize(ctx http.Context) error { + return nil +} + +func (r *CertShowAndDestroy) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|uint|min:1|exists:certs,id", + } +} + +func (r *CertShowAndDestroy) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *CertShowAndDestroy) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *CertShowAndDestroy) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/cert_store.go b/app/http/requests/cert/cert_store.go new file mode 100644 index 00000000..33e2d7ae --- /dev/null +++ b/app/http/requests/cert/cert_store.go @@ -0,0 +1,40 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type CertStore struct { + Type string `form:"type" json:"type"` + Domains []string `form:"domains" json:"domains"` + AutoRenew bool `form:"auto_renew" json:"auto_renew"` + UserID uint `form:"user_id" json:"user_id"` + DNSID *uint `form:"dns_id" json:"dns_id"` + WebsiteID *uint `form:"website_id" json:"website_id"` +} + +func (r *CertStore) Authorize(ctx http.Context) error { + return nil +} + +func (r *CertStore) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "type": "required|in:P256,P384,2048,4096", + "domains": "required|array", + "auto_renew": "required|bool", + "user_id": "required|exists:cert_users,id", + } +} + +func (r *CertStore) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *CertStore) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *CertStore) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/cert_update.go b/app/http/requests/cert/cert_update.go new file mode 100644 index 00000000..a5804551 --- /dev/null +++ b/app/http/requests/cert/cert_update.go @@ -0,0 +1,42 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type CertUpdate struct { + ID uint `form:"id" json:"id"` + Type string `form:"type" json:"type"` + Domains []string `form:"domains" json:"domains"` + AutoRenew bool `form:"auto_renew" json:"auto_renew"` + UserID uint `form:"user_id" json:"user_id"` + DNSID *uint `form:"dns_id" json:"dns_id"` + WebsiteID *uint `form:"website_id" json:"website_id"` +} + +func (r *CertUpdate) Authorize(ctx http.Context) error { + return nil +} + +func (r *CertUpdate) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|uint|min:1|exists:certs,id", + "type": "required|in:P256,P384,2048,4096", + "domains": "required|array", + "auto_renew": "required|bool", + "user_id": "required|exists:cert_users,id", + } +} + +func (r *CertUpdate) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *CertUpdate) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *CertUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/dns_add.go b/app/http/requests/cert/dns_add.go deleted file mode 100644 index 2f89cd13..00000000 --- a/app/http/requests/cert/dns_add.go +++ /dev/null @@ -1,54 +0,0 @@ -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"` - Name string `form:"name" json:"name"` - 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", - "name": "required", - "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 中的一个", - "name.required": "备注名称不能为空", - "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/dns_show_and_destroy.go b/app/http/requests/cert/dns_show_and_destroy.go new file mode 100644 index 00000000..ed0aaecc --- /dev/null +++ b/app/http/requests/cert/dns_show_and_destroy.go @@ -0,0 +1,32 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type DNSShowAndDestroy struct { + ID uint `form:"id" json:"id"` +} + +func (r *DNSShowAndDestroy) Authorize(ctx http.Context) error { + return nil +} + +func (r *DNSShowAndDestroy) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|uint|min:1|exists:cert_dns,id", + } +} + +func (r *DNSShowAndDestroy) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *DNSShowAndDestroy) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *DNSShowAndDestroy) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/dns_store.go b/app/http/requests/cert/dns_store.go new file mode 100644 index 00000000..043831a8 --- /dev/null +++ b/app/http/requests/cert/dns_store.go @@ -0,0 +1,44 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" + + "panel/pkg/acme" +) + +type DNSStore struct { + Type string `form:"type" json:"type"` + Name string `form:"name" json:"name"` + Data acme.DNSParam `form:"data" json:"data"` +} + +func (r *DNSStore) Authorize(ctx http.Context) error { + return nil +} + +func (r *DNSStore) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "type": "required|in:dnspod,aliyun,cloudflare", + "name": "required", + "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 *DNSStore) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *DNSStore) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *DNSStore) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/dns_update.go b/app/http/requests/cert/dns_update.go new file mode 100644 index 00000000..0f00aa08 --- /dev/null +++ b/app/http/requests/cert/dns_update.go @@ -0,0 +1,46 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" + + "panel/pkg/acme" +) + +type DNSUpdate struct { + ID uint `form:"id" json:"id"` + Type string `form:"type" json:"type"` + Name string `form:"name" json:"name"` + Data acme.DNSParam `form:"data" json:"data"` +} + +func (r *DNSUpdate) Authorize(ctx http.Context) error { + return nil +} + +func (r *DNSUpdate) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|uint|min:1|exists:cert_dns,id", + "type": "required|in:dnspod,aliyun,cloudflare", + "name": "required", + "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 *DNSUpdate) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *DNSUpdate) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *DNSUpdate) 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 index cc991533..172d6b60 100644 --- a/app/http/requests/cert/obtain.go +++ b/app/http/requests/cert/obtain.go @@ -20,10 +20,7 @@ func (r *Obtain) Rules(ctx http.Context) map[string]string { } func (r *Obtain) Messages(ctx http.Context) map[string]string { - return map[string]string{ - "id.required": "证书 ID 不能为空", - "id.exists": "证书 ID 不存在", - } + return map[string]string{} } func (r *Obtain) Attributes(ctx http.Context) map[string]string { diff --git a/app/http/requests/cert/renew.go b/app/http/requests/cert/renew.go index ace894be..6c0e3b9f 100644 --- a/app/http/requests/cert/renew.go +++ b/app/http/requests/cert/renew.go @@ -20,10 +20,7 @@ func (r *Renew) Rules(ctx http.Context) map[string]string { } func (r *Renew) Messages(ctx http.Context) map[string]string { - return map[string]string{ - "id.required": "证书 ID 不能为空", - "id.exists": "证书 ID 不存在", - } + return map[string]string{} } func (r *Renew) Attributes(ctx http.Context) map[string]string { diff --git a/app/http/requests/cert/user_add.go b/app/http/requests/cert/user_add.go deleted file mode 100644 index ff065218..00000000 --- a/app/http/requests/cert/user_add.go +++ /dev/null @@ -1,49 +0,0 @@ -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/cert/user_show_and_destroy.go b/app/http/requests/cert/user_show_and_destroy.go new file mode 100644 index 00000000..5c58ae5a --- /dev/null +++ b/app/http/requests/cert/user_show_and_destroy.go @@ -0,0 +1,32 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type UserShowAndDestroy struct { + ID uint `form:"id" json:"id"` +} + +func (r *UserShowAndDestroy) Authorize(ctx http.Context) error { + return nil +} + +func (r *UserShowAndDestroy) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|uint|min:1|exists:cert_users,id", + } +} + +func (r *UserShowAndDestroy) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *UserShowAndDestroy) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *UserShowAndDestroy) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/user_store.go b/app/http/requests/cert/user_store.go new file mode 100644 index 00000000..bb7e1585 --- /dev/null +++ b/app/http/requests/cert/user_store.go @@ -0,0 +1,40 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type UserStore 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 *UserStore) Authorize(ctx http.Context) error { + return nil +} + +func (r *UserStore) 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 *UserStore) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *UserStore) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *UserStore) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/http/requests/cert/user_update.go b/app/http/requests/cert/user_update.go new file mode 100644 index 00000000..3f39a6d2 --- /dev/null +++ b/app/http/requests/cert/user_update.go @@ -0,0 +1,42 @@ +package requests + +import ( + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/contracts/validation" +) + +type UserUpdate struct { + ID uint `form:"id" json:"id"` + 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 *UserUpdate) Authorize(ctx http.Context) error { + return nil +} + +func (r *UserUpdate) Rules(ctx http.Context) map[string]string { + return map[string]string{ + "id": "required|uint|min:1|exists:cert_users,id", + "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 *UserUpdate) Messages(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *UserUpdate) Attributes(ctx http.Context) map[string]string { + return map[string]string{} +} + +func (r *UserUpdate) PrepareForValidation(ctx http.Context, data validation.Data) error { + return nil +} diff --git a/app/services/cert.go b/app/services/cert.go index 112d3318..27fa190c 100644 --- a/app/services/cert.go +++ b/app/services/cert.go @@ -15,13 +15,18 @@ import ( ) 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 + UserStore(request requests.UserStore) error + UserUpdate(request requests.UserUpdate) error + UserShow(ID uint) (models.CertUser, error) + UserDestroy(ID uint) error + DNSStore(request requests.DNSStore) error + DNSUpdate(request requests.DNSUpdate) error + DNSShow(ID uint) (models.CertDNS, error) + DNSDestroy(ID uint) error + CertStore(request requests.CertStore) error + CertUpdate(request requests.CertUpdate) error + CertShow(ID uint) (models.Cert, error) + CertDestroy(ID uint) error ObtainAuto(ID uint) (certificate.Resource, error) ObtainManual(ID uint) (certificate.Resource, error) ManualDNS(ID uint) (map[string]acme.Resolve, error) @@ -35,15 +40,8 @@ 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 { +// UserStore 添加用户 +func (s *CertImpl) UserStore(request requests.UserStore) error { var user models.CertUser user.CA = request.CA user.Email = request.Email @@ -81,8 +79,59 @@ func (s *CertImpl) UserAdd(request requests.UserAdd) error { return facades.Orm().Query().Create(&user) } -// UserDelete 删除用户 -func (s *CertImpl) UserDelete(ID uint) error { +// UserUpdate 更新用户 +func (s *CertImpl) UserUpdate(request requests.UserUpdate) error { + var user models.CertUser + err := facades.Orm().Query().Where("id = ?", request.ID).First(&user) + if err != nil { + return err + } + + user.CA = request.CA + user.Email = request.Email + user.Kid = &request.Kid + user.HmacEncoded = &request.HmacEncoded + user.KeyType = request.KeyType + + 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().Save(&user) +} + +// UserShow 根据 ID 获取用户 +func (s *CertImpl) UserShow(ID uint) (models.CertUser, error) { + var user models.CertUser + err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&user) + + return user, err +} + +// UserDestroy 删除用户 +func (s *CertImpl) UserDestroy(ID uint) error { var user models.CertUser err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&user) if err != nil { @@ -97,8 +146,8 @@ func (s *CertImpl) UserDelete(ID uint) error { return err } -// DNSAdd 添加 DNS -func (s *CertImpl) DNSAdd(request requests.DNSAdd) error { +// DNSStore 添加 DNS +func (s *CertImpl) DNSStore(request requests.DNSStore) error { var dns models.CertDNS dns.Type = request.Type dns.Name = request.Name @@ -107,8 +156,31 @@ func (s *CertImpl) DNSAdd(request requests.DNSAdd) error { return facades.Orm().Query().Create(&dns) } -// DNSDelete 删除 DNS -func (s *CertImpl) DNSDelete(ID uint) error { +// DNSUpdate 更新 DNS +func (s *CertImpl) DNSUpdate(request requests.DNSUpdate) error { + var dns models.CertDNS + err := facades.Orm().Query().Where("id = ?", request.ID).First(&dns) + if err != nil { + return err + } + + dns.Type = request.Type + dns.Name = request.Name + dns.Data = request.Data + + return facades.Orm().Query().Save(&dns) +} + +// DNSShow 根据 ID 获取 DNS +func (s *CertImpl) DNSShow(ID uint) (models.CertDNS, error) { + var dns models.CertDNS + err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&dns) + + return dns, err +} + +// DNSDestroy 删除 DNS +func (s *CertImpl) DNSDestroy(ID uint) error { var dns models.CertDNS err := facades.Orm().Query().With("Certs").Where("id = ?", ID).First(&dns) if err != nil { @@ -123,8 +195,8 @@ func (s *CertImpl) DNSDelete(ID uint) error { return err } -// CertAdd 添加证书 -func (s *CertImpl) CertAdd(request requests.CertAdd) error { +// CertStore 添加证书 +func (s *CertImpl) CertStore(request requests.CertStore) error { var cert models.Cert cert.Type = request.Type cert.Domains = request.Domains @@ -141,8 +213,39 @@ func (s *CertImpl) CertAdd(request requests.CertAdd) error { return facades.Orm().Query().Create(&cert) } -// CertDelete 删除证书 -func (s *CertImpl) CertDelete(ID uint) error { +// CertUpdate 更新证书 +func (s *CertImpl) CertUpdate(request requests.CertUpdate) error { + var cert models.Cert + err := facades.Orm().Query().Where("id = ?", request.ID).First(&cert) + if err != nil { + return err + } + + cert.Type = request.Type + cert.Domains = request.Domains + cert.AutoRenew = request.AutoRenew + cert.UserID = request.UserID + + if request.DNSID != nil { + cert.DNSID = request.DNSID + } + if request.WebsiteID != nil { + cert.WebsiteID = request.WebsiteID + } + + return facades.Orm().Query().Save(&cert) +} + +// CertShow 根据 ID 获取证书 +func (s *CertImpl) CertShow(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 +} + +// CertDestroy 删除证书 +func (s *CertImpl) CertDestroy(ID uint) error { var cert models.Cert err := facades.Orm().Query().Where("id = ?", ID).First(&cert) if err != nil { diff --git a/docs/docs.go b/docs/docs.go index f3ac3e4b..b1cc4eff 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -152,7 +152,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/requests.CertAdd" + "$ref": "#/definitions/requests.CertStore" } } ], @@ -179,6 +179,118 @@ const docTemplate = `{ } }, "/panel/cert/certs/{id}": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的证书", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取证书", + "parameters": [ + { + "type": "integer", + "description": "证书 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.Cert" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "更新面板证书管理的证书", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "更新证书", + "parameters": [ + { + "type": "integer", + "description": "证书 ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CertUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, "delete": { "security": [ { @@ -299,7 +411,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/requests.DNSAdd" + "$ref": "#/definitions/requests.DNSStore" } } ], @@ -326,6 +438,118 @@ const docTemplate = `{ } }, "/panel/cert/dns/{id}": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的 DNS 接口", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 DNS 接口", + "parameters": [ + { + "type": "integer", + "description": "DNS 接口 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.CertDNS" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "put": { + "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 + }, + { + "description": "DNS 接口信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.DNSUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, "delete": { "security": [ { @@ -645,7 +869,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/requests.UserAdd" + "$ref": "#/definitions/requests.UserStore" } } ], @@ -672,6 +896,118 @@ const docTemplate = `{ } }, "/panel/cert/users/{id}": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的 ACME 用户", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 ACME 用户", + "parameters": [ + { + "type": "integer", + "description": "用户 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.CertUser" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "更新面板证书管理的 ACME 用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "更新 ACME 用户", + "parameters": [ + { + "type": "integer", + "description": "用户 ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "用户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, "delete": { "security": [ { @@ -765,6 +1101,23 @@ const docTemplate = `{ } } } + }, + "/swagger": { + "get": { + "description": "Swagger UI", + "tags": [ + "Swagger" + ], + "summary": "Swagger UI", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error" + } + } + } } }, "definitions": { @@ -907,6 +1260,10 @@ const docTemplate = `{ "id": { "type": "integer" }, + "name": { + "description": "备注名称", + "type": "string" + }, "type": { "description": "DNS 提供商 (dnspod, aliyun, cloudflare)", "type": "string" @@ -958,6 +1315,9 @@ const docTemplate = `{ "models.Website": { "type": "object", "properties": { + "cert": { + "$ref": "#/definitions/models.Cert" + }, "created_at": { "type": "string" }, @@ -987,7 +1347,7 @@ const docTemplate = `{ } } }, - "requests.CertAdd": { + "requests.CertStore": { "type": "object", "properties": { "auto_renew": { @@ -1007,15 +1367,67 @@ const docTemplate = `{ }, "user_id": { "type": "integer" + }, + "website_id": { + "type": "integer" } } }, - "requests.DNSAdd": { + "requests.CertUpdate": { + "type": "object", + "properties": { + "auto_renew": { + "type": "boolean" + }, + "dns_id": { + "type": "integer" + }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "user_id": { + "type": "integer" + }, + "website_id": { + "type": "integer" + } + } + }, + "requests.DNSStore": { "type": "object", "properties": { "data": { "$ref": "#/definitions/acme.DNSParam" }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "requests.DNSUpdate": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/acme.DNSParam" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, "type": { "type": "string" } @@ -1048,7 +1460,7 @@ const docTemplate = `{ } } }, - "requests.UserAdd": { + "requests.UserStore": { "type": "object", "properties": { "ca": { @@ -1068,6 +1480,29 @@ const docTemplate = `{ } } }, + "requests.UserUpdate": { + "type": "object", + "properties": { + "ca": { + "type": "string" + }, + "email": { + "type": "string" + }, + "hmac_encoded": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "key_type": { + "type": "string" + }, + "kid": { + "type": "string" + } + } + }, "responses.CertList": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 4ef90301..f7e0b260 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -145,7 +145,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/requests.CertAdd" + "$ref": "#/definitions/requests.CertStore" } } ], @@ -172,6 +172,118 @@ } }, "/panel/cert/certs/{id}": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的证书", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取证书", + "parameters": [ + { + "type": "integer", + "description": "证书 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.Cert" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "更新面板证书管理的证书", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "更新证书", + "parameters": [ + { + "type": "integer", + "description": "证书 ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "证书信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.CertUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, "delete": { "security": [ { @@ -292,7 +404,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/requests.DNSAdd" + "$ref": "#/definitions/requests.DNSStore" } } ], @@ -319,6 +431,118 @@ } }, "/panel/cert/dns/{id}": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的 DNS 接口", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 DNS 接口", + "parameters": [ + { + "type": "integer", + "description": "DNS 接口 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.CertDNS" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "put": { + "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 + }, + { + "description": "DNS 接口信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.DNSUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, "delete": { "security": [ { @@ -638,7 +862,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/requests.UserAdd" + "$ref": "#/definitions/requests.UserStore" } } ], @@ -665,6 +889,118 @@ } }, "/panel/cert/users/{id}": { + "get": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "获取面板证书管理的 ACME 用户", + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "获取 ACME 用户", + "parameters": [ + { + "type": "integer", + "description": "用户 ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/controllers.SuccessResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.CertUser" + } + } + } + ] + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, + "put": { + "security": [ + { + "BearerToken": [] + } + ], + "description": "更新面板证书管理的 ACME 用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "证书" + ], + "summary": "更新 ACME 用户", + "parameters": [ + { + "type": "integer", + "description": "用户 ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "用户信息", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/controllers.SuccessResponse" + } + }, + "401": { + "description": "登录已过期", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + }, + "500": { + "description": "系统内部错误", + "schema": { + "$ref": "#/definitions/controllers.ErrorResponse" + } + } + } + }, "delete": { "security": [ { @@ -758,6 +1094,23 @@ } } } + }, + "/swagger": { + "get": { + "description": "Swagger UI", + "tags": [ + "Swagger" + ], + "summary": "Swagger UI", + "responses": { + "200": { + "description": "OK" + }, + "500": { + "description": "Internal Server Error" + } + } + } } }, "definitions": { @@ -900,6 +1253,10 @@ "id": { "type": "integer" }, + "name": { + "description": "备注名称", + "type": "string" + }, "type": { "description": "DNS 提供商 (dnspod, aliyun, cloudflare)", "type": "string" @@ -951,6 +1308,9 @@ "models.Website": { "type": "object", "properties": { + "cert": { + "$ref": "#/definitions/models.Cert" + }, "created_at": { "type": "string" }, @@ -980,7 +1340,7 @@ } } }, - "requests.CertAdd": { + "requests.CertStore": { "type": "object", "properties": { "auto_renew": { @@ -1000,15 +1360,67 @@ }, "user_id": { "type": "integer" + }, + "website_id": { + "type": "integer" } } }, - "requests.DNSAdd": { + "requests.CertUpdate": { + "type": "object", + "properties": { + "auto_renew": { + "type": "boolean" + }, + "dns_id": { + "type": "integer" + }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "user_id": { + "type": "integer" + }, + "website_id": { + "type": "integer" + } + } + }, + "requests.DNSStore": { "type": "object", "properties": { "data": { "$ref": "#/definitions/acme.DNSParam" }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "requests.DNSUpdate": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/acme.DNSParam" + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, "type": { "type": "string" } @@ -1041,7 +1453,7 @@ } } }, - "requests.UserAdd": { + "requests.UserStore": { "type": "object", "properties": { "ca": { @@ -1061,6 +1473,29 @@ } } }, + "requests.UserUpdate": { + "type": "object", + "properties": { + "ca": { + "type": "string" + }, + "email": { + "type": "string" + }, + "hmac_encoded": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "key_type": { + "type": "string" + }, + "kid": { + "type": "string" + } + } + }, "responses.CertList": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 3d759819..8115c5fa 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -94,6 +94,9 @@ definitions: $ref: '#/definitions/acme.DNSParam' id: type: integer + name: + description: 备注名称 + type: string type: description: DNS 提供商 (dnspod, aliyun, cloudflare) type: string @@ -128,6 +131,8 @@ definitions: type: object models.Website: properties: + cert: + $ref: '#/definitions/models.Cert' created_at: type: string id: @@ -147,7 +152,7 @@ definitions: updated_at: type: string type: object - requests.CertAdd: + requests.CertStore: properties: auto_renew: type: boolean @@ -161,11 +166,45 @@ definitions: type: string user_id: type: integer + website_id: + type: integer type: object - requests.DNSAdd: + requests.CertUpdate: + properties: + auto_renew: + type: boolean + dns_id: + type: integer + domains: + items: + type: string + type: array + id: + type: integer + type: + type: string + user_id: + type: integer + website_id: + type: integer + type: object + requests.DNSStore: properties: data: $ref: '#/definitions/acme.DNSParam' + name: + type: string + type: + type: string + type: object + requests.DNSUpdate: + properties: + data: + $ref: '#/definitions/acme.DNSParam' + id: + type: integer + name: + type: string type: type: string type: object @@ -186,7 +225,7 @@ definitions: id: type: integer type: object - requests.UserAdd: + requests.UserStore: properties: ca: type: string @@ -199,6 +238,21 @@ definitions: kid: type: string type: object + requests.UserUpdate: + properties: + ca: + type: string + email: + type: string + hmac_encoded: + type: string + id: + type: integer + key_type: + type: string + kid: + type: string + type: object responses.CertList: properties: items: @@ -301,7 +355,7 @@ paths: name: data required: true schema: - $ref: '#/definitions/requests.CertAdd' + $ref: '#/definitions/requests.CertStore' produces: - application/json responses: @@ -353,6 +407,75 @@ paths: summary: 删除证书 tags: - 证书 + get: + description: 获取面板证书管理的证书 + parameters: + - description: 证书 ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/controllers.SuccessResponse' + - properties: + data: + $ref: '#/definitions/models.Cert' + type: object + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取证书 + tags: + - 证书 + put: + consumes: + - application/json + description: 更新面板证书管理的证书 + parameters: + - description: 证书 ID + in: path + name: id + required: true + type: integer + - description: 证书信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.CertUpdate' + 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 接口列表 @@ -391,7 +514,7 @@ paths: name: data required: true schema: - $ref: '#/definitions/requests.DNSAdd' + $ref: '#/definitions/requests.DNSStore' produces: - application/json responses: @@ -443,6 +566,75 @@ paths: summary: 删除 DNS 接口 tags: - 证书 + get: + description: 获取面板证书管理的 DNS 接口 + parameters: + - description: DNS 接口 ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/controllers.SuccessResponse' + - properties: + data: + $ref: '#/definitions/models.CertDNS' + type: object + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取 DNS 接口 + tags: + - 证书 + put: + consumes: + - application/json + description: 更新面板证书管理的 DNS 接口 + parameters: + - description: DNS 接口 ID + in: path + name: id + required: true + type: integer + - description: DNS 接口信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.DNSUpdate' + 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 提供商 @@ -603,7 +795,7 @@ paths: name: data required: true schema: - $ref: '#/definitions/requests.UserAdd' + $ref: '#/definitions/requests.UserStore' produces: - application/json responses: @@ -655,6 +847,75 @@ paths: summary: 删除 ACME 用户 tags: - 证书 + get: + description: 获取面板证书管理的 ACME 用户 + parameters: + - description: 用户 ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/controllers.SuccessResponse' + - properties: + data: + $ref: '#/definitions/models.CertUser' + type: object + "401": + description: 登录已过期 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + "500": + description: 系统内部错误 + schema: + $ref: '#/definitions/controllers.ErrorResponse' + security: + - BearerToken: [] + summary: 获取 ACME 用户 + tags: + - 证书 + put: + consumes: + - application/json + description: 更新面板证书管理的 ACME 用户 + parameters: + - description: 用户 ID + in: path + name: id + required: true + type: integer + - description: 用户信息 + in: body + name: data + required: true + schema: + $ref: '#/definitions/requests.UserUpdate' + 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: @@ -685,6 +946,17 @@ paths: summary: 用户登录 tags: - 用户 + /swagger: + get: + description: Swagger UI + responses: + "200": + description: OK + "500": + description: Internal Server Error + summary: Swagger UI + tags: + - Swagger securityDefinitions: BearerToken: in: header diff --git a/routes/api.go b/routes/api.go index aac6f994..79d93aca 100644 --- a/routes/api.go +++ b/routes/api.go @@ -60,15 +60,20 @@ func Api() { 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.Post("users", certController.UserStore) + r.Put("users/{id}", certController.UserUpdate) + r.Get("users/{id}", certController.UserShow) + r.Delete("users/{id}", certController.UserDestroy) r.Get("dns", certController.DNSList) - r.Post("dns", certController.DNSAdd) - r.Delete("dns/{id}", certController.DNSDelete) + r.Post("dns", certController.DNSStore) + r.Put("dns/{id}", certController.DNSUpdate) + r.Get("dns/{id}", certController.DNSShow) + r.Delete("dns/{id}", certController.DNSDestroy) r.Get("certs", certController.CertList) - r.Post("certs", certController.CertAdd) - r.Delete("certs/{id}", certController.CertDelete) - // r.Get("certs/{id}", certController.CertInfo) + r.Post("certs", certController.CertStore) + r.Put("certs/{id}", certController.CertUpdate) + r.Get("certs/{id}", certController.CertShow) + r.Delete("certs/{id}", certController.CertDestroy) r.Post("obtain", certController.Obtain) r.Post("renew", certController.Renew) r.Post("manualDNS", certController.ManualDNS)