nginx-ui/internal/cert/sync.go
2024-10-24 18:22:45 +08:00

137 lines
3.4 KiB
Go

package cert
import (
"bytes"
"encoding/json"
"fmt"
"github.com/0xJacky/Nginx-UI/internal/helper"
"github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/0xJacky/Nginx-UI/internal/notification"
"github.com/0xJacky/Nginx-UI/internal/transport"
"github.com/0xJacky/Nginx-UI/model"
"github.com/0xJacky/Nginx-UI/query"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/uozi-tech/cosy/logger"
"io"
"net/http"
"os"
)
type SyncCertificatePayload struct {
Name string `json:"name"`
SSLCertificatePath string `json:"ssl_certificate_path"`
SSLCertificateKeyPath string `json:"ssl_certificate_key_path"`
SSLCertificate string `json:"ssl_certificate"`
SSLCertificateKey string `json:"ssl_certificate_key"`
KeyType certcrypto.KeyType `json:"key_type"`
}
func SyncToRemoteServer(c *model.Cert) (err error) {
if c.SSLCertificatePath == "" || c.SSLCertificateKeyPath == "" || len(c.SyncNodeIds) == 0 {
return
}
nginxConfPath := nginx.GetConfPath()
if !helper.IsUnderDirectory(c.SSLCertificatePath, nginxConfPath) {
return fmt.Errorf("ssl_certificate_path: %s is not under the nginx conf path: %s",
c.SSLCertificatePath, nginxConfPath)
}
if !helper.IsUnderDirectory(c.SSLCertificateKeyPath, nginxConfPath) {
return fmt.Errorf("ssl_certificate_key_path: %s is not under the nginx conf path: %s",
c.SSLCertificateKeyPath, nginxConfPath)
}
certBytes, err := os.ReadFile(c.SSLCertificatePath)
if err != nil {
return
}
keyBytes, err := os.ReadFile(c.SSLCertificateKeyPath)
if err != nil {
return
}
payload := &SyncCertificatePayload{
Name: c.Name,
SSLCertificatePath: c.SSLCertificatePath,
SSLCertificateKeyPath: c.SSLCertificateKeyPath,
SSLCertificate: string(certBytes),
SSLCertificateKey: string(keyBytes),
KeyType: c.KeyType,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
return
}
q := query.Environment
envs, _ := q.Where(q.ID.In(c.SyncNodeIds...)).Find()
for _, env := range envs {
go func() {
err := deploy(env, c, payloadBytes)
if err != nil {
logger.Error(err)
}
}()
}
return
}
type SyncNotificationPayload struct {
StatusCode int `json:"status_code"`
CertName string `json:"cert_name"`
EnvName string `json:"env_name"`
RespBody string `json:"resp_body"`
}
func deploy(env *model.Environment, c *model.Cert, payloadBytes []byte) (err error) {
t, err := transport.NewTransport()
if err != nil {
return
}
client := http.Client{
Transport: t,
}
url, err := env.GetUrl("/api/cert_sync")
if err != nil {
return
}
req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(payloadBytes))
if err != nil {
return
}
req.Header.Set("X-Node-Secret", env.Token)
resp, err := client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return
}
notificationPayload := &SyncNotificationPayload{
StatusCode: resp.StatusCode,
CertName: c.Name,
EnvName: env.Name,
RespBody: string(respBody),
}
notificationPayloadBytes, err := json.Marshal(notificationPayload)
if err != nil {
return
}
if resp.StatusCode != http.StatusOK {
notification.Error("Sync Certificate Error", string(notificationPayloadBytes))
return
}
notification.Success("Sync Certificate Success", string(notificationPayloadBytes))
return
}