mirror of https://github.com/usual2970/certimate
				
				
				
			add deploy info
							parent
							
								
									bae35536b8
								
							
						
					
					
						commit
						fb40caa25c
					
				|  | @ -19,6 +19,7 @@ import ( | |||
| type aliyun struct { | ||||
| 	client *cas20200407.Client | ||||
| 	option *DeployerOption | ||||
| 	infos  []string | ||||
| } | ||||
| 
 | ||||
| func NewAliyun(option *DeployerOption) (Deployer, error) { | ||||
|  | @ -26,6 +27,7 @@ func NewAliyun(option *DeployerOption) (Deployer, error) { | |||
| 	json.Unmarshal([]byte(option.Access), access) | ||||
| 	a := &aliyun{ | ||||
| 		option: option, | ||||
| 		infos:  make([]string, 0), | ||||
| 	} | ||||
| 	client, err := a.createClient(access.AccessKeyId, access.AccessKeySecret) | ||||
| 	if err != nil { | ||||
|  | @ -36,6 +38,10 @@ func NewAliyun(option *DeployerOption) (Deployer, error) { | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (a *aliyun) GetInfo() []string { | ||||
| 	return a.infos | ||||
| } | ||||
| 
 | ||||
| func (a *aliyun) Deploy(ctx context.Context) error { | ||||
| 
 | ||||
| 	// 查询有没有对应的资源
 | ||||
|  | @ -44,24 +50,32 @@ func (a *aliyun) Deploy(ctx context.Context) error { | |||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	a.infos = append(a.infos, toStr("查询对应的资源", resource)) | ||||
| 
 | ||||
| 	// 查询有没有对应的联系人
 | ||||
| 	contacts, err := a.contacts() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	a.infos = append(a.infos, toStr("查询联系人", contacts)) | ||||
| 
 | ||||
| 	// 上传证书
 | ||||
| 	certId, err := a.uploadCert(&a.option.Certificate) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	a.infos = append(a.infos, toStr("上传证书", certId)) | ||||
| 
 | ||||
| 	// 部署证书
 | ||||
| 	jobId, err := a.deploy(resource, certId, contacts) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	a.infos = append(a.infos, toStr("创建部署证书任务", jobId)) | ||||
| 
 | ||||
| 	// 等待部署成功
 | ||||
| 	err = a.updateDeployStatus(*jobId) | ||||
| 	if err != nil { | ||||
|  | @ -80,10 +94,11 @@ func (a *aliyun) updateDeployStatus(jobId int64) error { | |||
| 		JobId: tea.Int64(jobId), | ||||
| 	} | ||||
| 
 | ||||
| 	_, err := a.client.UpdateDeploymentJobStatus(req) | ||||
| 	resp, err := a.client.UpdateDeploymentJobStatus(req) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	a.infos = append(a.infos, toStr("查询对应的资源", resp)) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ import ( | |||
| type AliyunCdn struct { | ||||
| 	client *cdn20180510.Client | ||||
| 	option *DeployerOption | ||||
| 	infos  []string | ||||
| } | ||||
| 
 | ||||
| func NewAliyunCdn(option *DeployerOption) (*AliyunCdn, error) { | ||||
|  | @ -31,9 +32,14 @@ func NewAliyunCdn(option *DeployerOption) (*AliyunCdn, error) { | |||
| 	return &AliyunCdn{ | ||||
| 		client: client, | ||||
| 		option: option, | ||||
| 		infos:  make([]string, 0), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (a *AliyunCdn) GetInfo() []string { | ||||
| 	return a.infos | ||||
| } | ||||
| 
 | ||||
| func (a *AliyunCdn) Deploy(ctx context.Context) error { | ||||
| 
 | ||||
| 	certName := fmt.Sprintf("%s-%s", a.option.Domain, a.option.DomainId) | ||||
|  | @ -49,11 +55,13 @@ func (a *AliyunCdn) Deploy(ctx context.Context) error { | |||
| 
 | ||||
| 	runtime := &util.RuntimeOptions{} | ||||
| 
 | ||||
| 	_, err := a.client.SetCdnDomainSSLCertificateWithOptions(setCdnDomainSSLCertificateRequest, runtime) | ||||
| 	resp, err := a.client.SetCdnDomainSSLCertificateWithOptions(setCdnDomainSSLCertificateRequest, runtime) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	a.infos = append(a.infos, toStr("cdn设置证书", resp)) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ package deployer | |||
| import ( | ||||
| 	"certimate/internal/applicant" | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"strings" | ||||
| 
 | ||||
|  | @ -31,6 +32,7 @@ type DeployerOption struct { | |||
| 
 | ||||
| type Deployer interface { | ||||
| 	Deploy(ctx context.Context) error | ||||
| 	GetInfo() []string | ||||
| } | ||||
| 
 | ||||
| func Get(record *models.Record, cert *applicant.Certificate) (Deployer, error) { | ||||
|  | @ -73,3 +75,11 @@ func getProduct(record *models.Record) string { | |||
| 	} | ||||
| 	return rs[1] | ||||
| } | ||||
| 
 | ||||
| func toStr(tag string, data any) string { | ||||
| 	if data == nil { | ||||
| 		return tag | ||||
| 	} | ||||
| 	byts, _ := json.Marshal(data) | ||||
| 	return tag + ":" + string(byts) | ||||
| } | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ import ( | |||
| 
 | ||||
| type ssh struct { | ||||
| 	option *DeployerOption | ||||
| 	infos  []string | ||||
| } | ||||
| 
 | ||||
| type sshAccess struct { | ||||
|  | @ -30,9 +31,14 @@ type sshAccess struct { | |||
| func NewSSH(option *DeployerOption) (Deployer, error) { | ||||
| 	return &ssh{ | ||||
| 		option: option, | ||||
| 		infos:  make([]string, 0), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (s *ssh) GetInfo() []string { | ||||
| 	return s.infos | ||||
| } | ||||
| 
 | ||||
| func (s *ssh) Deploy(ctx context.Context) error { | ||||
| 	access := &sshAccess{} | ||||
| 	if err := json.Unmarshal([]byte(s.option.Access), access); err != nil { | ||||
|  | @ -45,6 +51,8 @@ func (s *ssh) Deploy(ctx context.Context) error { | |||
| 	} | ||||
| 	defer client.Close() | ||||
| 
 | ||||
| 	s.infos = append(s.infos, toStr("ssh连接成功", nil)) | ||||
| 
 | ||||
| 	// 上传
 | ||||
| 	session, err := client.NewSession() | ||||
| 	if err != nil { | ||||
|  | @ -52,16 +60,22 @@ func (s *ssh) Deploy(ctx context.Context) error { | |||
| 	} | ||||
| 	defer session.Close() | ||||
| 
 | ||||
| 	s.infos = append(s.infos, toStr("ssh创建session成功", nil)) | ||||
| 
 | ||||
| 	// 上传证书
 | ||||
| 	if err := s.upload(client, s.option.Certificate.Certificate, access.CertPath); err != nil { | ||||
| 		return fmt.Errorf("failed to upload certificate: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	s.infos = append(s.infos, toStr("ssh上传证书成功", nil)) | ||||
| 
 | ||||
| 	// 上传私钥
 | ||||
| 	if err := s.upload(client, s.option.Certificate.PrivateKey, access.KeyPath); err != nil { | ||||
| 		return fmt.Errorf("failed to upload private key: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	s.infos = append(s.infos, toStr("ssh上传私钥成功", nil)) | ||||
| 
 | ||||
| 	// 执行命令
 | ||||
| 	var stdoutBuf bytes.Buffer | ||||
| 	session.Stdout = &stdoutBuf | ||||
|  | @ -72,6 +86,8 @@ func (s *ssh) Deploy(ctx context.Context) error { | |||
| 		return fmt.Errorf("failed to run command: %w, stdout: %s, stderr: %s", err, stdoutBuf.String(), stderrBuf.String()) | ||||
| 	} | ||||
| 
 | ||||
| 	s.infos = append(s.infos, toStr("ssh执行命令成功", []string{stdoutBuf.String()})) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ import ( | |||
| type tencentCdn struct { | ||||
| 	option     *DeployerOption | ||||
| 	credential *common.Credential | ||||
| 	infos      []string | ||||
| } | ||||
| 
 | ||||
| func NewTencentCdn(option *DeployerOption) (Deployer, error) { | ||||
|  | @ -34,9 +35,14 @@ func NewTencentCdn(option *DeployerOption) (Deployer, error) { | |||
| 	return &tencentCdn{ | ||||
| 		option:     option, | ||||
| 		credential: credential, | ||||
| 		infos:      make([]string, 0), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (t *tencentCdn) GetInfo() []string { | ||||
| 	return t.infos | ||||
| } | ||||
| 
 | ||||
| func (t *tencentCdn) Deploy(ctx context.Context) error { | ||||
| 
 | ||||
| 	// 查询有没有对应的资源
 | ||||
|  | @ -45,11 +51,14 @@ func (t *tencentCdn) Deploy(ctx context.Context) error { | |||
| 		return fmt.Errorf("failed to get resource: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	t.infos = append(t.infos, toStr("查询对应的资源", resource)) | ||||
| 
 | ||||
| 	// 上传证书
 | ||||
| 	certId, err := t.uploadCert() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to upload certificate: %w", err) | ||||
| 	} | ||||
| 	t.infos = append(t.infos, toStr("上传证书", certId)) | ||||
| 
 | ||||
| 	if err := t.deploy(resource, certId); err != nil { | ||||
| 		return fmt.Errorf("failed to deploy: %w", err) | ||||
|  | @ -100,11 +109,12 @@ func (t *tencentCdn) deploy(resource *tag.ResourceTagMapping, certId string) err | |||
| 	request.Status = common.Int64Ptr(1) | ||||
| 
 | ||||
| 	// 返回的resp是一个DeployCertificateInstanceResponse的实例,与请求对象对应
 | ||||
| 	_, err = client.DeployCertificateInstance(request) | ||||
| 	resp, err := client.DeployCertificateInstance(request) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to deploy certificate: %w", err) | ||||
| 	} | ||||
| 	t.infos = append(t.infos, toStr("部署证书", resp.Response)) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,15 +21,21 @@ type hookData struct { | |||
| 
 | ||||
| type webhook struct { | ||||
| 	option *DeployerOption | ||||
| 	infos  []string | ||||
| } | ||||
| 
 | ||||
| func NewWebhook(option *DeployerOption) (Deployer, error) { | ||||
| 
 | ||||
| 	return &webhook{ | ||||
| 		option: option, | ||||
| 		infos:  make([]string, 0), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (w *webhook) GetInfo() []string { | ||||
| 	return w.infos | ||||
| } | ||||
| 
 | ||||
| func (w *webhook) Deploy(ctx context.Context) error { | ||||
| 	access := &webhookAccess{} | ||||
| 	if err := json.Unmarshal([]byte(w.option.Access), access); err != nil { | ||||
|  | @ -44,12 +50,14 @@ func (w *webhook) Deploy(ctx context.Context) error { | |||
| 
 | ||||
| 	body, _ := json.Marshal(data) | ||||
| 
 | ||||
| 	_, err := xhttp.Req(access.Url, http.MethodPost, bytes.NewReader(body), map[string]string{ | ||||
| 	resp, err := xhttp.Req(access.Url, http.MethodPost, bytes.NewReader(body), map[string]string{ | ||||
| 		"Content-Type": "application/json", | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to send hook request: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	w.infos = append(w.infos, toStr("webhook response", string(resp))) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
|  | @ -27,16 +27,17 @@ func deploy(ctx context.Context, record *models.Record) error { | |||
| 		} | ||||
| 	}() | ||||
| 	var certificate *applicant.Certificate | ||||
| 	currRecord, err := app.GetApp().Dao().FindRecordById("domains", record.Id) | ||||
| 
 | ||||
| 	history := NewHistory(record) | ||||
| 	defer history.commit() | ||||
| 
 | ||||
| 	// ############1.检查域名配置
 | ||||
| 	history.record(checkPhase, "开始检查", nil) | ||||
| 
 | ||||
| 	currRecord, err := app.GetApp().Dao().FindRecordById("domains", record.Id) | ||||
| 	if err != nil { | ||||
| 		app.GetApp().Logger().Error("获取记录失败", "err", err) | ||||
| 		history.record(checkPhase, "获取域名配置失败", err) | ||||
| 		history.record(checkPhase, "获取域名配置失败", &RecordInfo{Err: err}) | ||||
| 		return err | ||||
| 	} | ||||
| 	history.record(checkPhase, "获取记录成功", nil) | ||||
|  | @ -48,7 +49,7 @@ func deploy(ctx context.Context, record *models.Record) error { | |||
| 		} | ||||
| 		err = errors.Join(errList...) | ||||
| 		app.GetApp().Logger().Error("展开记录失败", "err", err) | ||||
| 		history.record(checkPhase, "获取授权信息失败", err) | ||||
| 		history.record(checkPhase, "获取授权信息失败", &RecordInfo{Err: err}) | ||||
| 		return err | ||||
| 	} | ||||
| 	history.record(checkPhase, "获取授权信息成功", nil) | ||||
|  | @ -56,9 +57,11 @@ func deploy(ctx context.Context, record *models.Record) error { | |||
| 	cert := currRecord.GetString("certificate") | ||||
| 	expiredAt := currRecord.GetDateTime("expiredAt").Time() | ||||
| 
 | ||||
| 	if cert != "" && time.Until(expiredAt) > time.Hour*24 && currRecord.GetBool("deployed") { | ||||
| 	if cert != "" && time.Until(expiredAt) > time.Hour*24*10 && currRecord.GetBool("deployed") { | ||||
| 		app.GetApp().Logger().Info("证书在有效期内") | ||||
| 		history.record(checkPhase, "证书在有效期内且已部署,跳过", nil, true) | ||||
| 		history.record(checkPhase, "证书在有效期内且已部署,跳过", &RecordInfo{ | ||||
| 			Info: []string{fmt.Sprintf("证书有效期至 %s", expiredAt.Format("2006-01-02"))}, | ||||
| 		}, true) | ||||
| 		return err | ||||
| 	} | ||||
| 	history.record(checkPhase, "检查通过", nil, true) | ||||
|  | @ -67,21 +70,25 @@ func deploy(ctx context.Context, record *models.Record) error { | |||
| 	history.record(applyPhase, "开始申请", nil) | ||||
| 
 | ||||
| 	if cert != "" && time.Until(expiredAt) > time.Hour*24 { | ||||
| 		history.record(applyPhase, "证书在有效期内,跳过", nil) | ||||
| 		history.record(applyPhase, "证书在有效期内,跳过", &RecordInfo{ | ||||
| 			Info: []string{fmt.Sprintf("证书有效期至 %s", expiredAt.Format("2006-01-02"))}, | ||||
| 		}) | ||||
| 	} else { | ||||
| 		applicant, err := applicant.Get(currRecord) | ||||
| 		if err != nil { | ||||
| 			history.record(applyPhase, "获取applicant失败", err) | ||||
| 			history.record(applyPhase, "获取applicant失败", &RecordInfo{Err: err}) | ||||
| 			app.GetApp().Logger().Error("获取applicant失败", "err", err) | ||||
| 			return err | ||||
| 		} | ||||
| 		certificate, err = applicant.Apply() | ||||
| 		if err != nil { | ||||
| 			history.record(applyPhase, "申请证书失败", err) | ||||
| 			history.record(applyPhase, "申请证书失败", &RecordInfo{Err: err}) | ||||
| 			app.GetApp().Logger().Error("申请证书失败", "err", err) | ||||
| 			return err | ||||
| 		} | ||||
| 		history.record(applyPhase, "申请证书成功", nil) | ||||
| 		history.record(applyPhase, "申请证书成功", &RecordInfo{ | ||||
| 			Info: []string{fmt.Sprintf("证书地址: %s", certificate.CertUrl)}, | ||||
| 		}) | ||||
| 		history.setCert(certificate) | ||||
| 	} | ||||
| 
 | ||||
|  | @ -91,7 +98,7 @@ func deploy(ctx context.Context, record *models.Record) error { | |||
| 	history.record(deployPhase, "开始部署", nil, false) | ||||
| 	deployer, err := deployer.Get(currRecord, certificate) | ||||
| 	if err != nil { | ||||
| 		history.record(deployPhase, "获取deployer失败", err) | ||||
| 		history.record(deployPhase, "获取deployer失败", &RecordInfo{Err: err}) | ||||
| 		app.GetApp().Logger().Error("获取deployer失败", "err", err) | ||||
| 		return err | ||||
| 	} | ||||
|  | @ -99,12 +106,14 @@ func deploy(ctx context.Context, record *models.Record) error { | |||
| 	if err = deployer.Deploy(ctx); err != nil { | ||||
| 
 | ||||
| 		app.GetApp().Logger().Error("部署失败", "err", err) | ||||
| 		history.record(deployPhase, "部署失败", err) | ||||
| 		history.record(deployPhase, "部署失败", &RecordInfo{Err: err, Info: deployer.GetInfo()}) | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	app.GetApp().Logger().Info("部署成功") | ||||
| 	history.record(deployPhase, "部署成功", nil, true) | ||||
| 	history.record(deployPhase, "部署成功", &RecordInfo{ | ||||
| 		Info: deployer.GetInfo(), | ||||
| 	}, true) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
|  | @ -10,9 +10,15 @@ import ( | |||
| ) | ||||
| 
 | ||||
| type historyItem struct { | ||||
| 	Time    string `json:"time"` | ||||
| 	Message string `json:"message"` | ||||
| 	Error   string `json:"error"` | ||||
| 	Time    string   `json:"time"` | ||||
| 	Message string   `json:"message"` | ||||
| 	Error   string   `json:"error"` | ||||
| 	Info    []string `json:"info"` | ||||
| } | ||||
| 
 | ||||
| type RecordInfo struct { | ||||
| 	Err  error    `json:"err"` | ||||
| 	Info []string `json:"info"` | ||||
| } | ||||
| 
 | ||||
| type history struct { | ||||
|  | @ -34,21 +40,25 @@ func NewHistory(record *models.Record) *history { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (a *history) record(phase Phase, msg string, err error, pass ...bool) { | ||||
| func (a *history) record(phase Phase, msg string, info *RecordInfo, pass ...bool) { | ||||
| 	if info == nil { | ||||
| 		info = &RecordInfo{} | ||||
| 	} | ||||
| 	a.Phase = phase | ||||
| 	if len(pass) > 0 { | ||||
| 		a.PhaseSuccess = pass[0] | ||||
| 	} | ||||
| 
 | ||||
| 	errMsg := "" | ||||
| 	if err != nil { | ||||
| 		errMsg = err.Error() | ||||
| 	if info.Err != nil { | ||||
| 		errMsg = info.Err.Error() | ||||
| 		a.PhaseSuccess = false | ||||
| 	} | ||||
| 
 | ||||
| 	a.Log[phase] = append(a.Log[phase], historyItem{ | ||||
| 		Message: msg, | ||||
| 		Error:   errMsg, | ||||
| 		Info:    info.Info, | ||||
| 		Time:    xtime.BeijingTimeStr(), | ||||
| 	}) | ||||
| 
 | ||||
|  |  | |||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -5,7 +5,7 @@ | |||
|     <link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>Certimate - Your Trusted SSL Automation Partner</title> | ||||
|     <script type="module" crossorigin src="/assets/index-B6xIlnQB.js"></script> | ||||
|     <script type="module" crossorigin src="/assets/index-D3t3IJ9L.js"></script> | ||||
|     <link rel="stylesheet" crossorigin href="/assets/index-VYJgHfoP.css"> | ||||
|   </head> | ||||
|   <body class="bg-background"> | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ export type Log = { | |||
|   time: string; | ||||
|   message: string; | ||||
|   error: string; | ||||
|   info?: string[]; | ||||
| }; | ||||
| 
 | ||||
| export type DeploymentListReq = { | ||||
|  |  | |||
|  | @ -187,7 +187,7 @@ export default function Dashboard() { | |||
|                     href="https://github.com/usual2970/certimate/releases" | ||||
|                     target="_blank" | ||||
|                   > | ||||
|                     Certimate v0.0.7 | ||||
|                     Certimate v0.0.8 | ||||
|                   </a> | ||||
|                 </div> | ||||
|               </div> | ||||
|  |  | |||
|  | @ -147,6 +147,14 @@ const History = () => { | |||
|                                   <div>[{item.time}]</div> | ||||
|                                   <div className="ml-2">{item.message}</div> | ||||
|                                 </div> | ||||
|                                 {item.info && | ||||
|                                   item.info.map((info: string) => { | ||||
|                                     return ( | ||||
|                                       <div className="mt-1 text-green-600"> | ||||
|                                         {info} | ||||
|                                       </div> | ||||
|                                     ); | ||||
|                                   })} | ||||
|                                 {item.error && ( | ||||
|                                   <div className="mt-1 text-red-600"> | ||||
|                                     {item.error} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 yoan
						yoan