package deployer import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "github.com/qiniu/go-sdk/v7/auth" "github.com/usual2970/certimate/internal/domain" xhttp "github.com/usual2970/certimate/internal/utils/http" ) const qiniuGateway = "http://api.qiniu.com" type QiniuCDNDeployer struct { option *DeployerOption info []string credentials *auth.Credentials } func NewQiniuCDNDeployer(option *DeployerOption) (*QiniuCDNDeployer, error) { access := &domain.QiniuAccess{} json.Unmarshal([]byte(option.Access), access) return &QiniuCDNDeployer{ option: option, info: make([]string, 0), credentials: auth.New(access.AccessKey, access.SecretKey), }, nil } func (d *QiniuCDNDeployer) GetID() string { return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id) } func (d *QiniuCDNDeployer) GetInfo() []string { return d.info } func (d *QiniuCDNDeployer) Deploy(ctx context.Context) error { // 上传证书 certId, err := d.uploadCert() if err != nil { return fmt.Errorf("uploadCert failed: %w", err) } // 获取域名信息 domainInfo, err := d.getDomainInfo() if err != nil { return fmt.Errorf("getDomainInfo failed: %w", err) } // 判断域名是否启用 https if domainInfo.Https != nil && domainInfo.Https.CertID != "" { // 启用了 https // 修改域名证书 err = d.modifyDomainCert(certId, domainInfo.Https.ForceHttps, domainInfo.Https.Http2Enable) if err != nil { return fmt.Errorf("modifyDomainCert failed: %w", err) } } else { // 没启用 https // 启用 https err = d.enableHttps(certId) if err != nil { return fmt.Errorf("enableHttps failed: %w", err) } } return nil } func (d *QiniuCDNDeployer) enableHttps(certId string) error { domain := d.option.DeployConfig.GetDomain() path := fmt.Sprintf("/domain/%s/sslize", domain) body := &qiniuModifyDomainCertReq{ CertID: certId, ForceHttps: true, Http2Enable: true, } bodyBytes, err := json.Marshal(body) if err != nil { return fmt.Errorf("enable https failed: %w", err) } _, err = d.req(qiniuGateway+path, http.MethodPut, bytes.NewReader(bodyBytes)) if err != nil { return fmt.Errorf("enable https failed: %w", err) } return nil } type qiniuDomainInfo struct { Https *qiniuModifyDomainCertReq `json:"https"` } func (d *QiniuCDNDeployer) getDomainInfo() (*qiniuDomainInfo, error) { domain := d.option.DeployConfig.GetDomain() path := fmt.Sprintf("/domain/%s", domain) res, err := d.req(qiniuGateway+path, http.MethodGet, nil) if err != nil { return nil, fmt.Errorf("req failed: %w", err) } resp := &qiniuDomainInfo{} err = json.Unmarshal(res, resp) if err != nil { return nil, fmt.Errorf("json.Unmarshal failed: %w", err) } return resp, nil } type qiniuUploadCertReq struct { Name string `json:"name"` CommonName string `json:"common_name"` Pri string `json:"pri"` Ca string `json:"ca"` } type qiniuUploadCertResp struct { CertID string `json:"certID"` } func (d *QiniuCDNDeployer) uploadCert() (string, error) { path := "/sslcert" body := &qiniuUploadCertReq{ Name: getDeployString(d.option.DeployConfig, "domain"), CommonName: getDeployString(d.option.DeployConfig, "domain"), Pri: d.option.Certificate.PrivateKey, Ca: d.option.Certificate.Certificate, } bodyBytes, err := json.Marshal(body) if err != nil { return "", fmt.Errorf("json.Marshal failed: %w", err) } res, err := d.req(qiniuGateway+path, http.MethodPost, bytes.NewReader(bodyBytes)) if err != nil { return "", fmt.Errorf("req failed: %w", err) } resp := &qiniuUploadCertResp{} err = json.Unmarshal(res, resp) if err != nil { return "", fmt.Errorf("json.Unmarshal failed: %w", err) } return resp.CertID, nil } type qiniuModifyDomainCertReq struct { CertID string `json:"certId"` ForceHttps bool `json:"forceHttps"` Http2Enable bool `json:"http2Enable"` } func (d *QiniuCDNDeployer) modifyDomainCert(certId string, forceHttps, http2Enable bool) error { domain := d.option.DeployConfig.GetDomain() path := fmt.Sprintf("/domain/%s/httpsconf", domain) body := &qiniuModifyDomainCertReq{ CertID: certId, ForceHttps: forceHttps, Http2Enable: http2Enable, } bodyBytes, err := json.Marshal(body) if err != nil { return fmt.Errorf("json.Marshal failed: %w", err) } _, err = d.req(qiniuGateway+path, http.MethodPut, bytes.NewReader(bodyBytes)) if err != nil { return fmt.Errorf("req failed: %w", err) } return nil } func (d *QiniuCDNDeployer) req(url, method string, body io.Reader) ([]byte, error) { req := xhttp.BuildReq(url, method, body, map[string]string{ "Content-Type": "application/json", }) if err := d.credentials.AddToken(auth.TokenQBox, req); err != nil { return nil, fmt.Errorf("credentials.AddToken failed: %w", err) } respBody, err := xhttp.ToRequest(req) if err != nil { return nil, fmt.Errorf("ToRequest failed: %w", err) } defer respBody.Close() res, err := io.ReadAll(respBody) if err != nil { return nil, fmt.Errorf("io.ReadAll failed: %w", err) } return res, nil }