重构
This commit is contained in:
parent
510af680c4
commit
d505213d2c
|
@ -1,174 +1 @@
|
|||
package src
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Apply 申请证书
|
||||
// domain: 申请的域名
|
||||
func Apply(domain Domain) {
|
||||
email, hosts, name := domain.Email, domain.Host, domain.Name
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
acmeUser := &AcmeUser{
|
||||
Email: email,
|
||||
key: privateKey,
|
||||
}
|
||||
|
||||
config := lego.NewConfig(acmeUser)
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ali := appConfig.Provider.Ali
|
||||
conf := alidns.NewDefaultConfig()
|
||||
conf.RegionID = ali.RegionID
|
||||
conf.APIKey = ali.APIKey
|
||||
conf.SecretKey = ali.SecretKey
|
||||
provider, err := alidns.NewDNSProviderConfig(conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
challenge := client.Challenge
|
||||
err = challenge.SetDNS01Provider(provider)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
registrar := client.Registration
|
||||
reg, err := registrar.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
acmeUser.Registration = reg
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: hosts,
|
||||
Bundle: true,
|
||||
}
|
||||
|
||||
cert, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
saveCertFile(cert, name)
|
||||
}
|
||||
|
||||
func getProvider() challenge.Provider {
|
||||
config := GetAppConfig()
|
||||
|
||||
switch config.Use {
|
||||
case "alidns":
|
||||
return getAliProvider()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func getAliProvider() challenge.Provider {
|
||||
ali := GetAppConfig().Provider.Ali
|
||||
conf := alidns.NewDefaultConfig()
|
||||
conf.RegionID = ali.RegionID
|
||||
conf.APIKey = ali.APIKey
|
||||
conf.SecretKey = ali.SecretKey
|
||||
provider, err := alidns.NewDNSProviderConfig(conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return provider
|
||||
}
|
||||
func getTencentProvider() challenge.Provider {
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveCertFile 保存证书文件
|
||||
func saveCertFile(cert *certificate.Resource, name string) {
|
||||
dir := GetAppConfig().CertDir
|
||||
dir = filepath.Join(dir, name)
|
||||
_, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
log.Infof("创建目录 %s 失败", dir)
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Infof("创建目录 %s", dir)
|
||||
}
|
||||
|
||||
certBytes := cert.Certificate
|
||||
err = os.WriteFile(path.Join(dir, CertFileName), certBytes, 0755)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to save certificate: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(path.Join(dir, KeyFileName), cert.PrivateKey, 0755)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to save private key: %v", err)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certBytes)
|
||||
if block == nil {
|
||||
log.Fatalf("Failed to decode PEM block")
|
||||
return
|
||||
}
|
||||
|
||||
certParse, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse certificate: %v", err)
|
||||
}
|
||||
certInfo := CertInfo{
|
||||
Cert: *cert,
|
||||
Info: *certParse,
|
||||
}
|
||||
// 将 cert 转换为json格式并保存到文件中
|
||||
certJson, err := json.Marshal(certInfo)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal certificate: %v", err)
|
||||
}
|
||||
err = os.WriteFile(path.Join(dir, CertInfoFileName), certJson, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to save certificate info: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type AcmeUser struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
key crypto.PrivateKey
|
||||
}
|
||||
|
||||
func (u AcmeUser) GetEmail() string {
|
||||
return u.Email
|
||||
}
|
||||
func (u AcmeUser) GetRegistration() *registration.Resource {
|
||||
return u.Registration
|
||||
}
|
||||
func (u AcmeUser) GetPrivateKey() crypto.PrivateKey {
|
||||
return u.key
|
||||
}
|
||||
|
||||
type CertInfo struct {
|
||||
Cert certificate.Resource
|
||||
Info x509.Certificate
|
||||
}
|
||||
|
|
238
src/acme/helper.go
Normal file
238
src/acme/helper.go
Normal file
|
@ -0,0 +1,238 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"acme-mana/src/common"
|
||||
"acme-mana/src/conf"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"github.com/go-acme/lego/v4/certificate"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func Apply(cert *conf.CertConf) {
|
||||
email := cert.Email
|
||||
host := cert.Host
|
||||
name := cert.Name
|
||||
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
acmeUser := &AcmeUser{
|
||||
Email: email,
|
||||
key: privateKey,
|
||||
}
|
||||
config := lego.NewConfig(acmeUser)
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
providerName := cert.Provider
|
||||
p, _ := conf.FindProvider(providerName)
|
||||
|
||||
provider := getProvider(p)
|
||||
|
||||
//var provider challenge.Provider
|
||||
//
|
||||
//switch p.Type {
|
||||
//case "ali":
|
||||
// conf := alidns.NewDefaultConfig()
|
||||
// conf.RegionID = p.Conf["RegionID"]
|
||||
// conf.APIKey = p.Conf["APIKey"]
|
||||
// conf.SecretKey = p.Conf["SecretKey"]
|
||||
// provider, err = alidns.NewDNSProviderConfig(conf)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
chall := client.Challenge
|
||||
err = chall.SetDNS01Provider(provider)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
registrar := client.Registration
|
||||
reg, err := registrar.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
acmeUser.Registration = reg
|
||||
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: host,
|
||||
Bundle: true,
|
||||
}
|
||||
|
||||
res, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
saveCertFile(res, cert, name)
|
||||
|
||||
}
|
||||
|
||||
//func Apply1(domain Domain) {
|
||||
// email, hosts, name := domain.Email, domain.Host, domain.Name
|
||||
// privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// acmeUser := &AcmeUser{
|
||||
// Email: email,
|
||||
// key: privateKey,
|
||||
// }
|
||||
//
|
||||
// config := lego.NewConfig(acmeUser)
|
||||
//
|
||||
// client, err := lego.NewClient(config)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// ali := appConfig.Provider.Ali
|
||||
// conf := alidns.NewDefaultConfig()
|
||||
// conf.RegionID = ali.RegionID
|
||||
// conf.APIKey = ali.APIKey
|
||||
// conf.SecretKey = ali.SecretKey
|
||||
// provider, err := alidns.NewDNSProviderConfig(conf)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// challenge := client.Challenge
|
||||
// err = challenge.SetDNS01Provider(provider)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// registrar := client.Registration
|
||||
// reg, err := registrar.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// acmeUser.Registration = reg
|
||||
//
|
||||
// request := certificate.ObtainRequest{
|
||||
// Domains: hosts,
|
||||
// Bundle: true,
|
||||
// }
|
||||
//
|
||||
// cert, err := client.Certificate.Obtain(request)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// saveCertFile(cert, name)
|
||||
//}
|
||||
|
||||
func getProvider(p *conf.ProviderConf) challenge.Provider {
|
||||
|
||||
switch p.Type {
|
||||
case "ali":
|
||||
return aliProvider(p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func aliProvider(p *conf.ProviderConf) challenge.Provider {
|
||||
aliConf := alidns.NewDefaultConfig()
|
||||
aliConf.RegionID = p.Conf["RegionID"]
|
||||
aliConf.APIKey = p.Conf["APIKey"]
|
||||
aliConf.SecretKey = p.Conf["SecretKey"]
|
||||
provider, err := alidns.NewDNSProviderConfig(aliConf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return provider
|
||||
}
|
||||
func tencentProvider() challenge.Provider {
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveCertFile 保存证书文件
|
||||
func saveCertFile(cert *certificate.Resource, cerfConf *conf.CertConf, name string) {
|
||||
dir := cerfConf.Dir
|
||||
dir = filepath.Join(dir, name)
|
||||
_, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
log.Infof("创建目录 %s 失败", dir)
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Infof("创建目录 %s", dir)
|
||||
}
|
||||
|
||||
certBytes := cert.Certificate
|
||||
err = os.WriteFile(path.Join(dir, common.CertFileName), certBytes, 0755)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to save certificate: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(path.Join(dir, common.KeyFileName), cert.PrivateKey, 0755)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to save private key: %v", err)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certBytes)
|
||||
if block == nil {
|
||||
log.Fatalf("Failed to decode PEM block")
|
||||
return
|
||||
}
|
||||
|
||||
certParse, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse certificate: %v", err)
|
||||
}
|
||||
certInfo := CertInfo{
|
||||
Cert: *cert,
|
||||
Info: *certParse,
|
||||
}
|
||||
// 将 cert 转换为json格式并保存到文件中
|
||||
certJson, err := json.Marshal(certInfo)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal certificate: %v", err)
|
||||
}
|
||||
err = os.WriteFile(path.Join(dir, common.CertInfoFileName), certJson, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to save certificate info: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type AcmeUser struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
key crypto.PrivateKey
|
||||
}
|
||||
|
||||
func (u AcmeUser) GetEmail() string {
|
||||
return u.Email
|
||||
}
|
||||
func (u AcmeUser) GetRegistration() *registration.Resource {
|
||||
return u.Registration
|
||||
}
|
||||
func (u AcmeUser) GetPrivateKey() crypto.PrivateKey {
|
||||
return u.key
|
||||
}
|
||||
|
||||
type CertInfo struct {
|
||||
Cert certificate.Resource
|
||||
Info x509.Certificate
|
||||
}
|
126
src/cmd/cmd_handle/server.go
Normal file
126
src/cmd/cmd_handle/server.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
package cmd_handle
|
||||
|
||||
import (
|
||||
"acme-mana/src/common"
|
||||
"acme-mana/src/server"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var stdout *os.File
|
||||
var stderr *os.File
|
||||
|
||||
func start() {
|
||||
initLog()
|
||||
|
||||
isDaemon := os.Getenv("GO_DAEMON")
|
||||
log.Println("守护进程启动: " + isDaemon)
|
||||
|
||||
if isDaemon != "1" {
|
||||
daemonStart()
|
||||
return
|
||||
}
|
||||
if isRunning() {
|
||||
log.Println("守护进程已启动, 跳过执行")
|
||||
return
|
||||
}
|
||||
workPath, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Fatalf("无法获取当前工作路径: %v", err)
|
||||
}
|
||||
cmd := exec.Cmd{
|
||||
Path: workPath,
|
||||
Args: os.Args,
|
||||
Dir: filepath.Dir(workPath),
|
||||
Env: append(os.Environ(), "GO_DAEMON=1"),
|
||||
//Stdin: os.Stdin,
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
SysProcAttr: &syscall.SysProcAttr{},
|
||||
}
|
||||
log.Println("启动守护进程中...")
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("无法启动守护进程: %v", err)
|
||||
}
|
||||
err = os.WriteFile(common.PidFile, []byte(strconv.Itoa(cmd.Process.Pid)), 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("无法写出PID文件: %v", err)
|
||||
}
|
||||
log.Printf("启动进程启动成功, PID: %d", cmd.Process.Pid)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func daemonStart() {
|
||||
// 启动 HttpServer
|
||||
server.HttpInstance.Init()
|
||||
server.HttpInstance.Start()
|
||||
|
||||
// 启动 SocketServer
|
||||
server.SocketInstance.Start()
|
||||
|
||||
// TODO 启动 AutoRefreshCert
|
||||
}
|
||||
|
||||
func isRunning() bool {
|
||||
log.Println("Checking if daemon is running...")
|
||||
pid, err := readPID()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(pid)
|
||||
log.Println("Found process:", process)
|
||||
if err != nil {
|
||||
log.Println("Failed to find process:", err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 读取PID文件
|
||||
func readPID() (int, error) {
|
||||
log.Println("Reading PID file...")
|
||||
data, err := os.ReadFile(common.PidFile)
|
||||
if err != nil {
|
||||
log.Println("Failed to read PID file:", err)
|
||||
return 0, err
|
||||
}
|
||||
log.Println("PID file content:", string(data))
|
||||
|
||||
pid, err := strconv.Atoi(string(data))
|
||||
if err != nil {
|
||||
log.Println("Failed to parse PID:", err)
|
||||
return 0, err
|
||||
}
|
||||
log.Println("PID:", pid)
|
||||
|
||||
return pid, nil
|
||||
}
|
||||
|
||||
func initLog() {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("无法获取当前工作路径: %v", err)
|
||||
}
|
||||
dir := path.Join(pwd, "log")
|
||||
_, err = os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
err := os.Mkdir(dir, 0777)
|
||||
if err != nil {
|
||||
log.Fatalf("无法创建日志目录: %v", err)
|
||||
}
|
||||
}
|
||||
outFile, err := os.OpenFile(path.Join(dir, "out.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
defer outFile.Close()
|
||||
stdout = outFile
|
||||
|
||||
errFile, err := os.OpenFile(path.Join(dir, "err.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
defer stderr.Close()
|
||||
stderr = errFile
|
||||
}
|
|
@ -30,10 +30,10 @@ func editServer(cmd *cobra.Command, args []string) {
|
|||
}
|
||||
|
||||
func serverState(cmd *cobra.Command, args []string) {
|
||||
server.Instance.Status()
|
||||
server.HttpInstance.Status()
|
||||
}
|
||||
func startServer(cmd *cobra.Command, args []string) {
|
||||
server.Instance.Start()
|
||||
server.HttpInstance.Start()
|
||||
}
|
||||
func stopServer(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("stop server")
|
||||
|
|
162
src/command.go
162
src/command.go
|
@ -1,83 +1,83 @@
|
|||
package src
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
// InitSocket 初始化 socket 文件
|
||||
func InitSocket() {
|
||||
log.Println("Start listen command")
|
||||
// 删除旧的 socket 文件
|
||||
if _, err := os.Stat(SocketFile); err == nil {
|
||||
os.Remove(SocketFile)
|
||||
}
|
||||
|
||||
listener, err := net.Listen("unix", SocketFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to listen on socket: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
for {
|
||||
log.Println("Waiting for connections...")
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Printf("Failed to accept connection: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go handleConnection(conn)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
处理连接
|
||||
*/
|
||||
func handleConnection(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read command: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
command := string(buf[:n])
|
||||
log.Printf("Received command: %s", command)
|
||||
|
||||
// 在这里处理接收到的命令
|
||||
switch command {
|
||||
case "stop":
|
||||
onStop()
|
||||
default:
|
||||
onCommand(command)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
收到停止命令
|
||||
*/
|
||||
func onStop() {
|
||||
log.Println("Stopping daemon...")
|
||||
os.Remove(PidFile)
|
||||
log.Println("Remove PID File...")
|
||||
os.Remove(SocketFile)
|
||||
log.Println("Remove Socket File...")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
/*
|
||||
收到命令
|
||||
*/
|
||||
func onCommand(command string) {
|
||||
|
||||
}
|
||||
|
||||
func onConfig() {
|
||||
|
||||
}
|
||||
//import (
|
||||
// "log"
|
||||
// "net"
|
||||
// "os"
|
||||
//)
|
||||
//
|
||||
//// InitSocket 初始化 socket 文件
|
||||
//func InitSocket() {
|
||||
// log.Println("Start listen command")
|
||||
// // 删除旧的 socket 文件
|
||||
// if _, err := os.Stat(SocketFile); err == nil {
|
||||
// os.Remove(SocketFile)
|
||||
// }
|
||||
//
|
||||
// listener, err := net.Listen("unix", SocketFile)
|
||||
// if err != nil {
|
||||
// log.Fatalf("Failed to listen on socket: %v", err)
|
||||
// }
|
||||
// defer listener.Close()
|
||||
//
|
||||
// for {
|
||||
// log.Println("Waiting for connections...")
|
||||
// conn, err := listener.Accept()
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to accept connection: %v", err)
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// go handleConnection(conn)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///*
|
||||
//*
|
||||
//处理连接
|
||||
//*/
|
||||
//func handleConnection(conn net.Conn) {
|
||||
// defer conn.Close()
|
||||
//
|
||||
// buf := make([]byte, 1024)
|
||||
// n, err := conn.Read(buf)
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to read command: %v", err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// command := string(buf[:n])
|
||||
// log.Printf("Received command: %s", command)
|
||||
//
|
||||
// // 在这里处理接收到的命令
|
||||
// switch command {
|
||||
// case "stop":
|
||||
// onStop()
|
||||
// default:
|
||||
// onCommand(command)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
///*
|
||||
//*
|
||||
//收到停止命令
|
||||
//*/
|
||||
//func onStop() {
|
||||
// log.Println("Stopping daemon...")
|
||||
// os.Remove(PidFile)
|
||||
// log.Println("Remove PID File...")
|
||||
// os.Remove(SocketFile)
|
||||
// log.Println("Remove Socket File...")
|
||||
// os.Exit(0)
|
||||
//}
|
||||
//
|
||||
///*
|
||||
//收到命令
|
||||
//*/
|
||||
//func onCommand(command string) {
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func onConfig() {
|
||||
//
|
||||
//}
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
package common
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const PidFile = "acme-mana.pid"
|
||||
const SocketFile = "acme-mana.sock"
|
||||
|
||||
const CertFileName = "fullchain.pem"
|
||||
const KeyFileName = "privkey.pem"
|
||||
const CertInfoFileName = "info.json"
|
||||
|
||||
var RootCmd *cobra.Command
|
||||
|
|
530
src/config.go
530
src/config.go
|
@ -1,267 +1,267 @@
|
|||
package src
|
||||
|
||||
import (
|
||||
"acme-mana/src/crypto"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/go-acme/lego/v4/log"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ReadConfig() *AppConfig {
|
||||
if DnsProviderSupports != nil {
|
||||
return nil
|
||||
}
|
||||
InitConfig()
|
||||
file, err := os.ReadFile(GetEnvConf().ConfFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var conf *AppConfig
|
||||
err = yaml.Unmarshal(file, &conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return conf
|
||||
}
|
||||
|
||||
func InitConfig() {
|
||||
// 判断当前目录下是否存在配置文件 config.yml
|
||||
_, err := os.Stat(GetEnvConf().ConfFile)
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
// 配置文件不存在,则创建一个
|
||||
log.Infof("配置文件不存在,自动创建默认配置文件")
|
||||
// 生成默认配置
|
||||
conf := readNewConf()
|
||||
// 创建一个默认的配置文件
|
||||
file, err := os.Create(GetEnvConf().ConfFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
data, err := yaml.Marshal(conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// 写入配置文件
|
||||
_, err = file.Write(data)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func readNewConf() *AppConfig {
|
||||
conf := defaultConf()
|
||||
log.Println("无配置文件, 生成配置文件")
|
||||
|
||||
conf.CertDir = scanConfDefault("请输入证书保存目录; 默认为 cert", "cert")
|
||||
isGenMsg := "是否需要自动生成用于数据传输加密的RSA密钥对? \n请输入yes(Y)/no(N) 默认为 yes"
|
||||
isGenErrMsg := "请输入yes(Y)/no(N)"
|
||||
isGenValues := []string{"yes", "no", "Y", "N"}
|
||||
isGenEncrypt := scanConfDefaultCheck(isGenMsg, "yes", isGenValues, isGenErrMsg)
|
||||
if isGenEncrypt == "no" || isGenEncrypt == "N" {
|
||||
conf.Encrypt.PriKey = scanConf("请输入RSA私钥", "请输入RSA私钥")
|
||||
conf.Encrypt.PubKey = scanConf("请输入RSA公钥", "请输入RSA公钥")
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("请输入DNS提供商; 当前支持的: %s", strings.Join(DnsProviderSupports, ","))
|
||||
errMsg := fmt.Sprintf("不支持的DNS提供商; 当前支持的: %s", strings.Join(DnsProviderSupports, ","))
|
||||
conf.Use = scanConfDefaultCheck(msg, "", DnsProviderSupports, errMsg)
|
||||
switch conf.Use {
|
||||
case "alidns":
|
||||
fmt.Printf("阿里云DNS配置帮助页: \n%s\n", "https://go-acme.github.io/lego/dns/alidns/index.html")
|
||||
fmt.Printf("阿里云令牌获取方式:\n%s\n", "https://usercenter.console.aliyun.com/#/manage/ak")
|
||||
fmt.Printf("阿里云SDK客户端项目地址:\n%s\n", "https://github.com/aliyun/alibaba-cloud-sdk-go?tab=readme-ov-file")
|
||||
conf.Provider.Ali.RegionID = scanConf("请输入阿里云Region ID", "请输入阿里云Region ID")
|
||||
conf.Provider.Ali.APIKey = scanConf("请输入阿里云API Key", "请输入阿里云API Key")
|
||||
conf.Provider.Ali.SecretKey = scanConf("请输入阿里云Secret Key", "请输入阿里云Secret Key")
|
||||
case "tencentcloud":
|
||||
fmt.Printf("腾讯云DNS配置帮助页: \n%s\n", "https://go-acme.github.io/lego/dns/tencentcloud/index.html")
|
||||
fmt.Printf("腾讯云令牌获取方式: \n%s\n", "https://console.cloud.tencent.com/cam/capi")
|
||||
fmt.Printf("腾讯云SDK客户端项目地址: \n%s\n", "https://github.com/tencentcloud/tencentcloud-sdk-go?tab=readme-ov-file")
|
||||
conf.Provider.Tencent.SecretId = scanConf("请输入腾讯云Secret Id", "请输入腾讯云Secret Id")
|
||||
conf.Provider.Tencent.SecretKey = scanConf("请输入腾讯云Secret Key", "请输入腾讯云Secret Key")
|
||||
case "cloudflare":
|
||||
fmt.Printf("Cloudflare DNS配置帮助页: \n%s\n", "https://go-acme.github.io/lego/dns/tencentcloud/index.html")
|
||||
fmt.Printf("Cloudflare 令牌获取方式: \n%s\n", "https://blog.cloudflare.com/zh-cn/api-tokens-general-availability/")
|
||||
fmt.Printf("Cloudflare SDK客户端项目地址: \n%s\n", "https://github.com/cloudflare/cloudflare-go")
|
||||
conf.Provider.CloudFlare.Token = scanConf("请输入CloudFlare DNS API Token", "请输入CloudFlare Token")
|
||||
}
|
||||
isAddDomainMsg := "是否需要添加证书获取配置?\n您可以在此处通过控制台交互添加;也可以在配置文件创建后,直接修改配置文件.\n请输入yes(Y)/no(N) 默认为 yes"
|
||||
isAddDomain := scanConfDefaultCheck(isAddDomainMsg, "yes", isGenValues, isGenErrMsg)
|
||||
if isAddDomain == "no" || isAddDomain == "N" {
|
||||
fmt.Printf("您可以通过手动修改%s文件, 调整证书获取配置.", GetEnvConf().ConfFile)
|
||||
conf.Domains = []Domain{}
|
||||
return conf
|
||||
}
|
||||
conf.Domains = []Domain{}
|
||||
for {
|
||||
name := scanConf("请输入配置名称", "配置名称不能为空")
|
||||
email := scanConf("请输入邮箱", "邮箱不能为空")
|
||||
host := scanConf("请输入主机名;支持泛解析;\n支持多个域名,多个域名用,(英文逗号)分割;\n如: example.com,*.example.com\n", "主机名不能为空")
|
||||
// 将host通过,分割为数组
|
||||
hosts := strings.Split(host, ",")
|
||||
conf.Domains = append(conf.Domains, Domain{
|
||||
Name: name,
|
||||
Email: email,
|
||||
Host: hosts,
|
||||
})
|
||||
isAddNextDomainMsg := "是否需要继续添加证书获取配置?\n请输入yes(Y)/no(N) 默认为 yes"
|
||||
isAddNextDomain := scanConfDefaultCheck(isAddNextDomainMsg, "yes", isGenValues, isGenErrMsg)
|
||||
if isAddNextDomain == "no" || isAddNextDomain == "N" {
|
||||
fmt.Printf("您后续可以通过手动修改%s文件, 调整证书获取配置.", GetEnvConf().ConfFile)
|
||||
return conf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 读取用户输入
|
||||
func scanConf(msg string, errMsg string) string {
|
||||
for {
|
||||
log.Println(msg)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
name, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
fmt.Println("读取失败;", err)
|
||||
continue
|
||||
}
|
||||
name = strings.Trim(name, "\r\n")
|
||||
if name == "" {
|
||||
fmt.Println(errMsg)
|
||||
continue
|
||||
}
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
func scanConfDefault(msg string, defaultContent string) string {
|
||||
for {
|
||||
log.Println(msg)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
name, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
fmt.Println("读取失败;", err)
|
||||
continue
|
||||
}
|
||||
name = strings.Trim(name, "\r\n")
|
||||
if name == "" {
|
||||
return defaultContent
|
||||
}
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
func scanConfDefaultCheck(msg string, defaultContent string, values []string, errMsg string) string {
|
||||
for {
|
||||
content := scanConfDefault(msg, defaultContent)
|
||||
// 判断内容是否在values中
|
||||
for _, value := range values {
|
||||
if value == content {
|
||||
return content
|
||||
}
|
||||
}
|
||||
log.Println(errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
func defaultConf() *AppConfig {
|
||||
//priKey, pubKey, err := GenRsa()
|
||||
priKey, pubKey, err := crypto.GenRSA()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return &AppConfig{
|
||||
Use: "Ali",
|
||||
CertDir: "cert",
|
||||
Provider: AppProvider{
|
||||
Ali: AliProvider{
|
||||
RegionID: "cn-hangzhou",
|
||||
APIKey: "api_key",
|
||||
SecretKey: "secret_key",
|
||||
},
|
||||
Tencent: TencentProvider{
|
||||
SecretId: "secret_id",
|
||||
SecretKey: "secret_key",
|
||||
},
|
||||
CloudFlare: CloudFlareProvider{
|
||||
Token: "token",
|
||||
},
|
||||
},
|
||||
Domains: []Domain{
|
||||
{
|
||||
Name: "example.com",
|
||||
Email: "email@example.com",
|
||||
Host: []string{"www.example.com"},
|
||||
},
|
||||
},
|
||||
Encrypt: Encrypt{
|
||||
PriKey: priKey,
|
||||
PubKey: pubKey,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Use string
|
||||
|
||||
CertDir string
|
||||
|
||||
Provider AppProvider
|
||||
|
||||
Domains []Domain
|
||||
|
||||
Encrypt Encrypt
|
||||
}
|
||||
|
||||
type AppProvider struct {
|
||||
Ali AliProvider
|
||||
Tencent TencentProvider
|
||||
CloudFlare CloudFlareProvider
|
||||
}
|
||||
type AliProvider struct {
|
||||
RegionID string
|
||||
APIKey string
|
||||
SecretKey string
|
||||
}
|
||||
type TencentProvider struct {
|
||||
SecretId string
|
||||
SecretKey string
|
||||
}
|
||||
type CloudFlareProvider struct {
|
||||
Token string
|
||||
}
|
||||
type Domain struct {
|
||||
Name string
|
||||
Email string
|
||||
Host []string
|
||||
}
|
||||
type Encrypt struct {
|
||||
PriKey string
|
||||
PubKey string
|
||||
}
|
||||
|
||||
func (conf AppConfig) FindDomain(name string) *Domain {
|
||||
for _, domain := range conf.Domains {
|
||||
if domain.Name == name {
|
||||
return &domain
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const ENV_CONF_FILE = "ACME_MANA_CONF_FILE"
|
||||
|
||||
func InitRuntimeConf() *EnvConf {
|
||||
return &EnvConf{
|
||||
ConfFile: env.GetOrDefaultString(ENV_CONF_FILE, "config.yml"),
|
||||
}
|
||||
}
|
||||
|
||||
type EnvConf struct {
|
||||
ConfFile string
|
||||
}
|
||||
//import (
|
||||
// "acme-mana/src/crypto"
|
||||
// "bufio"
|
||||
// "fmt"
|
||||
// "github.com/go-acme/lego/v4/log"
|
||||
// "github.com/go-acme/lego/v4/platform/config/env"
|
||||
// "gopkg.in/yaml.v3"
|
||||
// "os"
|
||||
// "strings"
|
||||
//)
|
||||
//
|
||||
//func ReadConfig() *AppConfig {
|
||||
// if DnsProviderSupports != nil {
|
||||
// return nil
|
||||
// }
|
||||
// InitConfig()
|
||||
// file, err := os.ReadFile(GetEnvConf().ConfFile)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// var conf *AppConfig
|
||||
// err = yaml.Unmarshal(file, &conf)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// return conf
|
||||
//}
|
||||
//
|
||||
//func InitConfig() {
|
||||
// // 判断当前目录下是否存在配置文件 config.yml
|
||||
// _, err := os.Stat(GetEnvConf().ConfFile)
|
||||
//
|
||||
// if os.IsNotExist(err) {
|
||||
// // 配置文件不存在,则创建一个
|
||||
// log.Infof("配置文件不存在,自动创建默认配置文件")
|
||||
// // 生成默认配置
|
||||
// conf := readNewConf()
|
||||
// // 创建一个默认的配置文件
|
||||
// file, err := os.Create(GetEnvConf().ConfFile)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// data, err := yaml.Marshal(conf)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// // 写入配置文件
|
||||
// _, err = file.Write(data)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func readNewConf() *AppConfig {
|
||||
// conf := defaultConf()
|
||||
// log.Println("无配置文件, 生成配置文件")
|
||||
//
|
||||
// conf.CertDir = scanConfDefault("请输入证书保存目录; 默认为 cert", "cert")
|
||||
// isGenMsg := "是否需要自动生成用于数据传输加密的RSA密钥对? \n请输入yes(Y)/no(N) 默认为 yes"
|
||||
// isGenErrMsg := "请输入yes(Y)/no(N)"
|
||||
// isGenValues := []string{"yes", "no", "Y", "N"}
|
||||
// isGenEncrypt := scanConfDefaultCheck(isGenMsg, "yes", isGenValues, isGenErrMsg)
|
||||
// if isGenEncrypt == "no" || isGenEncrypt == "N" {
|
||||
// conf.Encrypt.PriKey = scanConf("请输入RSA私钥", "请输入RSA私钥")
|
||||
// conf.Encrypt.PubKey = scanConf("请输入RSA公钥", "请输入RSA公钥")
|
||||
// }
|
||||
//
|
||||
// msg := fmt.Sprintf("请输入DNS提供商; 当前支持的: %s", strings.Join(DnsProviderSupports, ","))
|
||||
// errMsg := fmt.Sprintf("不支持的DNS提供商; 当前支持的: %s", strings.Join(DnsProviderSupports, ","))
|
||||
// conf.Use = scanConfDefaultCheck(msg, "", DnsProviderSupports, errMsg)
|
||||
// switch conf.Use {
|
||||
// case "alidns":
|
||||
// fmt.Printf("阿里云DNS配置帮助页: \n%s\n", "https://go-acme.github.io/lego/dns/alidns/index.html")
|
||||
// fmt.Printf("阿里云令牌获取方式:\n%s\n", "https://usercenter.console.aliyun.com/#/manage/ak")
|
||||
// fmt.Printf("阿里云SDK客户端项目地址:\n%s\n", "https://github.com/aliyun/alibaba-cloud-sdk-go?tab=readme-ov-file")
|
||||
// conf.Provider.Ali.RegionID = scanConf("请输入阿里云Region ID", "请输入阿里云Region ID")
|
||||
// conf.Provider.Ali.APIKey = scanConf("请输入阿里云API Key", "请输入阿里云API Key")
|
||||
// conf.Provider.Ali.SecretKey = scanConf("请输入阿里云Secret Key", "请输入阿里云Secret Key")
|
||||
// case "tencentcloud":
|
||||
// fmt.Printf("腾讯云DNS配置帮助页: \n%s\n", "https://go-acme.github.io/lego/dns/tencentcloud/index.html")
|
||||
// fmt.Printf("腾讯云令牌获取方式: \n%s\n", "https://console.cloud.tencent.com/cam/capi")
|
||||
// fmt.Printf("腾讯云SDK客户端项目地址: \n%s\n", "https://github.com/tencentcloud/tencentcloud-sdk-go?tab=readme-ov-file")
|
||||
// conf.Provider.Tencent.SecretId = scanConf("请输入腾讯云Secret Id", "请输入腾讯云Secret Id")
|
||||
// conf.Provider.Tencent.SecretKey = scanConf("请输入腾讯云Secret Key", "请输入腾讯云Secret Key")
|
||||
// case "cloudflare":
|
||||
// fmt.Printf("Cloudflare DNS配置帮助页: \n%s\n", "https://go-acme.github.io/lego/dns/tencentcloud/index.html")
|
||||
// fmt.Printf("Cloudflare 令牌获取方式: \n%s\n", "https://blog.cloudflare.com/zh-cn/api-tokens-general-availability/")
|
||||
// fmt.Printf("Cloudflare SDK客户端项目地址: \n%s\n", "https://github.com/cloudflare/cloudflare-go")
|
||||
// conf.Provider.CloudFlare.Token = scanConf("请输入CloudFlare DNS API Token", "请输入CloudFlare Token")
|
||||
// }
|
||||
// isAddDomainMsg := "是否需要添加证书获取配置?\n您可以在此处通过控制台交互添加;也可以在配置文件创建后,直接修改配置文件.\n请输入yes(Y)/no(N) 默认为 yes"
|
||||
// isAddDomain := scanConfDefaultCheck(isAddDomainMsg, "yes", isGenValues, isGenErrMsg)
|
||||
// if isAddDomain == "no" || isAddDomain == "N" {
|
||||
// fmt.Printf("您可以通过手动修改%s文件, 调整证书获取配置.", GetEnvConf().ConfFile)
|
||||
// conf.Domains = []Domain{}
|
||||
// return conf
|
||||
// }
|
||||
// conf.Domains = []Domain{}
|
||||
// for {
|
||||
// name := scanConf("请输入配置名称", "配置名称不能为空")
|
||||
// email := scanConf("请输入邮箱", "邮箱不能为空")
|
||||
// host := scanConf("请输入主机名;支持泛解析;\n支持多个域名,多个域名用,(英文逗号)分割;\n如: example.com,*.example.com\n", "主机名不能为空")
|
||||
// // 将host通过,分割为数组
|
||||
// hosts := strings.Split(host, ",")
|
||||
// conf.Domains = append(conf.Domains, Domain{
|
||||
// Name: name,
|
||||
// Email: email,
|
||||
// Host: hosts,
|
||||
// })
|
||||
// isAddNextDomainMsg := "是否需要继续添加证书获取配置?\n请输入yes(Y)/no(N) 默认为 yes"
|
||||
// isAddNextDomain := scanConfDefaultCheck(isAddNextDomainMsg, "yes", isGenValues, isGenErrMsg)
|
||||
// if isAddNextDomain == "no" || isAddNextDomain == "N" {
|
||||
// fmt.Printf("您后续可以通过手动修改%s文件, 调整证书获取配置.", GetEnvConf().ConfFile)
|
||||
// return conf
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 读取用户输入
|
||||
//func scanConf(msg string, errMsg string) string {
|
||||
// for {
|
||||
// log.Println(msg)
|
||||
// reader := bufio.NewReader(os.Stdin)
|
||||
// name, err := reader.ReadString('\n')
|
||||
// if err != nil {
|
||||
// fmt.Println("读取失败;", err)
|
||||
// continue
|
||||
// }
|
||||
// name = strings.Trim(name, "\r\n")
|
||||
// if name == "" {
|
||||
// fmt.Println(errMsg)
|
||||
// continue
|
||||
// }
|
||||
// return name
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func scanConfDefault(msg string, defaultContent string) string {
|
||||
// for {
|
||||
// log.Println(msg)
|
||||
// reader := bufio.NewReader(os.Stdin)
|
||||
// name, err := reader.ReadString('\n')
|
||||
// if err != nil {
|
||||
// fmt.Println("读取失败;", err)
|
||||
// continue
|
||||
// }
|
||||
// name = strings.Trim(name, "\r\n")
|
||||
// if name == "" {
|
||||
// return defaultContent
|
||||
// }
|
||||
// return name
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func scanConfDefaultCheck(msg string, defaultContent string, values []string, errMsg string) string {
|
||||
// for {
|
||||
// content := scanConfDefault(msg, defaultContent)
|
||||
// // 判断内容是否在values中
|
||||
// for _, value := range values {
|
||||
// if value == content {
|
||||
// return content
|
||||
// }
|
||||
// }
|
||||
// log.Println(errMsg)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func defaultConf() *AppConfig {
|
||||
// //priKey, pubKey, err := GenRsa()
|
||||
// priKey, pubKey, err := crypto.GenRSA()
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// return &AppConfig{
|
||||
// Use: "Ali",
|
||||
// CertDir: "cert",
|
||||
// Provider: AppProvider{
|
||||
// Ali: AliProvider{
|
||||
// RegionID: "cn-hangzhou",
|
||||
// APIKey: "api_key",
|
||||
// SecretKey: "secret_key",
|
||||
// },
|
||||
// Tencent: TencentProvider{
|
||||
// SecretId: "secret_id",
|
||||
// SecretKey: "secret_key",
|
||||
// },
|
||||
// CloudFlare: CloudFlareProvider{
|
||||
// Token: "token",
|
||||
// },
|
||||
// },
|
||||
// Domains: []Domain{
|
||||
// {
|
||||
// Name: "example.com",
|
||||
// Email: "email@example.com",
|
||||
// Host: []string{"www.example.com"},
|
||||
// },
|
||||
// },
|
||||
// Encrypt: Encrypt{
|
||||
// PriKey: priKey,
|
||||
// PubKey: pubKey,
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//type AppConfig struct {
|
||||
// Use string
|
||||
//
|
||||
// CertDir string
|
||||
//
|
||||
// Provider AppProvider
|
||||
//
|
||||
// Domains []Domain
|
||||
//
|
||||
// Encrypt Encrypt
|
||||
//}
|
||||
//
|
||||
//type AppProvider struct {
|
||||
// Ali AliProvider
|
||||
// Tencent TencentProvider
|
||||
// CloudFlare CloudFlareProvider
|
||||
//}
|
||||
//type AliProvider struct {
|
||||
// RegionID string
|
||||
// APIKey string
|
||||
// SecretKey string
|
||||
//}
|
||||
//type TencentProvider struct {
|
||||
// SecretId string
|
||||
// SecretKey string
|
||||
//}
|
||||
//type CloudFlareProvider struct {
|
||||
// Token string
|
||||
//}
|
||||
//type Domain struct {
|
||||
// Name string
|
||||
// Email string
|
||||
// Host []string
|
||||
//}
|
||||
//type Encrypt struct {
|
||||
// PriKey string
|
||||
// PubKey string
|
||||
//}
|
||||
//
|
||||
//func (conf AppConfig) FindDomain(name string) *Domain {
|
||||
// for _, domain := range conf.Domains {
|
||||
// if domain.Name == name {
|
||||
// return &domain
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//const ENV_CONF_FILE = "ACME_MANA_CONF_FILE"
|
||||
//
|
||||
//func InitRuntimeConf() *EnvConf {
|
||||
// return &EnvConf{
|
||||
// ConfFile: env.GetOrDefaultString(ENV_CONF_FILE, "config.yml"),
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//type EnvConf struct {
|
||||
// ConfFile string
|
||||
//}
|
||||
|
|
286
src/http.go
286
src/http.go
|
@ -1,145 +1,145 @@
|
|||
package src
|
||||
|
||||
import (
|
||||
"acme-mana/src/crypto"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func InitHttpServer(host string, port int) {
|
||||
|
||||
log.Println("Start http server, Listen " + strconv.Itoa(port))
|
||||
h := gin.Default()
|
||||
h.GET("/api/v1/refresh", refreshCert)
|
||||
h.GET("/api/v1/cert", getCert)
|
||||
h.GET("/api/v1/domain/list", domainList)
|
||||
err := h.Run(host + ":" + strconv.Itoa(port))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func domainList(c *gin.Context) {
|
||||
token := getToken(c)
|
||||
domains := GetAppConfig().Domains
|
||||
data, err := json.Marshal(domains)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
encryptData := encryptResult(string(data), token)
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": encryptData,
|
||||
})
|
||||
}
|
||||
|
||||
func getCert(c *gin.Context) {
|
||||
name := c.Query("name")
|
||||
token := getToken(c)
|
||||
|
||||
dir := GetAppConfig().CertDir
|
||||
dir = filepath.Join(dir, name)
|
||||
_, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 500,
|
||||
"msg": "Name does not exist.",
|
||||
})
|
||||
return
|
||||
}
|
||||
crtFilePath := path.Join(dir, CertFileName)
|
||||
crtContent, err := os.ReadFile(crtFilePath)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 500,
|
||||
"msg": "Failed to read crt file.",
|
||||
})
|
||||
return
|
||||
}
|
||||
crt := string(crtContent)
|
||||
|
||||
keyFilePath := path.Join(dir, KeyFileName)
|
||||
keyContent, err := os.ReadFile(keyFilePath)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 500,
|
||||
"msg": "Failed to read key file.",
|
||||
})
|
||||
return
|
||||
}
|
||||
key := string(keyContent)
|
||||
|
||||
certInfoFilePath := path.Join(dir, CertInfoFileName)
|
||||
certInfoContent, err := os.ReadFile(certInfoFilePath)
|
||||
if err != nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 500,
|
||||
"msg": "Failed to read cert info file.",
|
||||
})
|
||||
return
|
||||
}
|
||||
certInfo := string(certInfoContent)
|
||||
|
||||
data, err := json.Marshal(&DomainData{
|
||||
Fullchain: crt,
|
||||
Key: key,
|
||||
Info: certInfo,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
encryptData := encryptResult(string(data), token)
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "Success",
|
||||
"data": encryptData,
|
||||
})
|
||||
}
|
||||
|
||||
func refreshCert(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
domain := GetAppConfig().FindDomain(name)
|
||||
if domain == nil {
|
||||
c.JSON(200, gin.H{
|
||||
"code": 500,
|
||||
"msg": "Name does not exist.",
|
||||
})
|
||||
return
|
||||
}
|
||||
Apply(*domain)
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"msg": "Success",
|
||||
})
|
||||
}
|
||||
|
||||
func getToken(c *gin.Context) (token string) {
|
||||
token = c.Query("token")
|
||||
token = decryptParam(token)
|
||||
return
|
||||
}
|
||||
|
||||
func decryptParam(param string) string {
|
||||
priKey := GetAppConfig().Encrypt.PriKey
|
||||
tokenBytes, err := hex.DecodeString(param)
|
||||
tokenPlain, err := crypto.DecryptRSABase64(priKey, tokenBytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return string(tokenPlain)
|
||||
}
|
||||
|
||||
func encryptResult(content string, token string) string {
|
||||
result := crypto.EncryptAES([]byte(token), []byte(content))
|
||||
return base64.StdEncoding.EncodeToString(result)
|
||||
}
|
||||
//import (
|
||||
// "acme-mana/src/crypto"
|
||||
// "encoding/base64"
|
||||
// "encoding/hex"
|
||||
// "encoding/json"
|
||||
// "github.com/gin-gonic/gin"
|
||||
// "log"
|
||||
// "os"
|
||||
// "path"
|
||||
// "path/filepath"
|
||||
// "strconv"
|
||||
//)
|
||||
//
|
||||
//func InitHttpServer(host string, port int) {
|
||||
//
|
||||
// log.Println("Start http server, Listen " + strconv.Itoa(port))
|
||||
// h := gin.Default()
|
||||
// h.GET("/api/v1/refresh", refreshCert)
|
||||
// h.GET("/api/v1/cert", getCert)
|
||||
// h.GET("/api/v1/domain/list", domainList)
|
||||
// err := h.Run(host + ":" + strconv.Itoa(port))
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func domainList(c *gin.Context) {
|
||||
// token := getToken(c)
|
||||
// domains := GetAppConfig().Domains
|
||||
// data, err := json.Marshal(domains)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// encryptData := encryptResult(string(data), token)
|
||||
// c.JSON(200, gin.H{
|
||||
// "code": 200,
|
||||
// "msg": "success",
|
||||
// "data": encryptData,
|
||||
// })
|
||||
//}
|
||||
//
|
||||
//func getCert(c *gin.Context) {
|
||||
// name := c.Query("name")
|
||||
// token := getToken(c)
|
||||
//
|
||||
// dir := GetAppConfig().CertDir
|
||||
// dir = filepath.Join(dir, name)
|
||||
// _, err := os.Stat(dir)
|
||||
// if os.IsNotExist(err) {
|
||||
// c.JSON(200, gin.H{
|
||||
// "code": 500,
|
||||
// "msg": "Name does not exist.",
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
// crtFilePath := path.Join(dir, CertFileName)
|
||||
// crtContent, err := os.ReadFile(crtFilePath)
|
||||
// if err != nil {
|
||||
// c.JSON(200, gin.H{
|
||||
// "code": 500,
|
||||
// "msg": "Failed to read crt file.",
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
// crt := string(crtContent)
|
||||
//
|
||||
// keyFilePath := path.Join(dir, KeyFileName)
|
||||
// keyContent, err := os.ReadFile(keyFilePath)
|
||||
// if err != nil {
|
||||
// c.JSON(200, gin.H{
|
||||
// "code": 500,
|
||||
// "msg": "Failed to read key file.",
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
// key := string(keyContent)
|
||||
//
|
||||
// certInfoFilePath := path.Join(dir, CertInfoFileName)
|
||||
// certInfoContent, err := os.ReadFile(certInfoFilePath)
|
||||
// if err != nil {
|
||||
// c.JSON(200, gin.H{
|
||||
// "code": 500,
|
||||
// "msg": "Failed to read cert info file.",
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
// certInfo := string(certInfoContent)
|
||||
//
|
||||
// data, err := json.Marshal(&DomainData{
|
||||
// Fullchain: crt,
|
||||
// Key: key,
|
||||
// Info: certInfo,
|
||||
// })
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// encryptData := encryptResult(string(data), token)
|
||||
// c.JSON(200, gin.H{
|
||||
// "code": 200,
|
||||
// "msg": "Success",
|
||||
// "data": encryptData,
|
||||
// })
|
||||
//}
|
||||
//
|
||||
//func refreshCert(c *gin.Context) {
|
||||
// name := c.Param("name")
|
||||
// domain := GetAppConfig().FindDomain(name)
|
||||
// if domain == nil {
|
||||
// c.JSON(200, gin.H{
|
||||
// "code": 500,
|
||||
// "msg": "Name does not exist.",
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
// Apply(*domain)
|
||||
// c.JSON(200, gin.H{
|
||||
// "code": 200,
|
||||
// "msg": "Success",
|
||||
// })
|
||||
//}
|
||||
//
|
||||
//func getToken(c *gin.Context) (token string) {
|
||||
// token = c.Query("token")
|
||||
// token = decryptParam(token)
|
||||
// return
|
||||
//}
|
||||
//
|
||||
//func decryptParam(param string) string {
|
||||
// priKey := GetAppConfig().Encrypt.PriKey
|
||||
// tokenBytes, err := hex.DecodeString(param)
|
||||
// tokenPlain, err := crypto.DecryptRSABase64(priKey, tokenBytes)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// return string(tokenPlain)
|
||||
//}
|
||||
//
|
||||
//func encryptResult(content string, token string) string {
|
||||
// result := crypto.EncryptAES([]byte(token), []byte(content))
|
||||
// return base64.StdEncoding.EncodeToString(result)
|
||||
//}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package http
|
||||
|
||||
//
|
||||
//import (
|
||||
// "fmt"
|
||||
// "github.com/gin-gonic/gin"
|
||||
//)
|
||||
//
|
||||
//func domainList(context *gin.Context) {
|
||||
// fmt.Println("domainList")
|
||||
//}
|
||||
//
|
||||
//func getCert(context *gin.Context) {
|
||||
// fmt.Println("getCert")
|
||||
//}
|
||||
//
|
||||
//func refreshCert(context *gin.Context) {
|
||||
// fmt.Println("refreshCert")
|
||||
//}
|
|
@ -1,46 +0,0 @@
|
|||
package http
|
||||
|
||||
//
|
||||
//import (
|
||||
// "acme-mana/src/common"
|
||||
// "github.com/gin-gonic/gin"
|
||||
// "github.com/go-acme/lego/v4/log"
|
||||
// "strconv"
|
||||
//)
|
||||
//
|
||||
//var service *gin.Engine
|
||||
//var isRunning bool
|
||||
//
|
||||
//func Start() {
|
||||
// if isRunning {
|
||||
// return
|
||||
// }
|
||||
// go start()
|
||||
//}
|
||||
//
|
||||
//func Stop() {
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func Status() bool {
|
||||
// return isRunning
|
||||
//}
|
||||
//
|
||||
//func start() {
|
||||
// conf := common.AppConf
|
||||
// server := conf.Server
|
||||
// service := gin.Default()
|
||||
// register(service)
|
||||
// isRunning = true
|
||||
// err := service.Run(server.Host + ":" + strconv.Itoa(server.Port))
|
||||
// if err != nil {
|
||||
// log.Fatal("http server start error \n", err)
|
||||
// }
|
||||
// isRunning = false
|
||||
//}
|
||||
//
|
||||
//func register(service *gin.Engine) {
|
||||
// service.GET("/api/v1/refresh", refreshCert)
|
||||
// service.GET("/api/v1/cert", getCert)
|
||||
// service.GET("/api/v1/domain/list", domainList)
|
||||
//}
|
|
@ -1,25 +0,0 @@
|
|||
package handle
|
||||
|
||||
import (
|
||||
"acme-mana/src/conf"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
)
|
||||
|
||||
var CertHandlerInstance = &CertHandler{}
|
||||
|
||||
type CertHandler struct {
|
||||
}
|
||||
|
||||
func (h *CertHandler) Get(c *gin.Context) {
|
||||
log.Println("get cert")
|
||||
c.JSON(200, conf.Config())
|
||||
}
|
||||
|
||||
func (h *CertHandler) Refresh(context *gin.Context) {
|
||||
log.Fatalln("refresh cert")
|
||||
}
|
||||
|
||||
func (h *CertHandler) DomainList(context *gin.Context) {
|
||||
log.Fatalln("domain list")
|
||||
}
|
|
@ -2,7 +2,7 @@ package server
|
|||
|
||||
import (
|
||||
"acme-mana/src/conf"
|
||||
"acme-mana/src/server/handle"
|
||||
"acme-mana/src/server/http_handler"
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -13,12 +13,12 @@ import (
|
|||
)
|
||||
|
||||
type HttpServer struct {
|
||||
server *http.Server `json:"server"`
|
||||
server *http.Server
|
||||
status bool
|
||||
engine *gin.Engine
|
||||
}
|
||||
|
||||
func (s *HttpServer) InitServer() {
|
||||
func (s *HttpServer) Init() {
|
||||
config := conf.Config()
|
||||
var serverConf = config.Server
|
||||
s.initServer(serverConf.Host, serverConf.Port)
|
||||
|
@ -53,10 +53,10 @@ func (s *HttpServer) Start() {
|
|||
func (s *HttpServer) Stop() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
err := Instance.server.Shutdown(ctx)
|
||||
err := HttpInstance.server.Shutdown(ctx)
|
||||
s.status = false
|
||||
if err != nil {
|
||||
log.Fatalln("Instance Shutdown:", err)
|
||||
log.Fatalln("HttpInstance Shutdown:", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,13 +67,13 @@ func (s *HttpServer) Status() bool {
|
|||
func (s *HttpServer) register() {
|
||||
service := s.engine
|
||||
service.Use(gin.Logger())
|
||||
service.Use(handle.GlobalErrorHandler())
|
||||
service.Use(http_handler.GlobalErrorHandler())
|
||||
|
||||
certHandler := handle.CertHandlerInstance
|
||||
certGroup := service.Group("/api/v1/cert", handle.AuthMiddleware())
|
||||
certHandler := http_handler.CertHandlerInstance
|
||||
certGroup := service.Group("/api/v1/cert", http_handler.AuthMiddleware())
|
||||
certGroup.GET("/", certHandler.Get)
|
||||
|
||||
confHandler := handle.ConfHandlerInstance
|
||||
confGroup := service.Group("/api/v1", handle.AuthMiddleware())
|
||||
confHandler := http_handler.ConfHandlerInstance
|
||||
confGroup := service.Group("/api/v1", http_handler.AuthMiddleware())
|
||||
confGroup.GET("/conf", confHandler.Get)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ func TestHttpServer_InitServer(t *testing.T) {
|
|||
status: tt.fields.status,
|
||||
engine: tt.fields.engine,
|
||||
}
|
||||
s.InitServer()
|
||||
s.Init()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -119,22 +119,6 @@ func TestHttpServer_register(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := Status(); got != tt.want {
|
||||
t.Errorf("Status() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHttpServer_initServer(t *testing.T) {
|
||||
type fields struct {
|
||||
server *http.Server
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package handle
|
||||
package http_handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
43
src/server/http_handler/cert.go
Normal file
43
src/server/http_handler/cert.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package http_handler
|
||||
|
||||
import (
|
||||
"acme-mana/src/acme"
|
||||
"acme-mana/src/conf"
|
||||
"acme-mana/src/server/model"
|
||||
"github.com/gin-gonic/gin"
|
||||
"log"
|
||||
)
|
||||
|
||||
var CertHandlerInstance = &CertHandler{}
|
||||
|
||||
type CertHandler struct {
|
||||
}
|
||||
|
||||
func (h *CertHandler) List(c *gin.Context) {
|
||||
log.Println("list cert")
|
||||
certs := conf.Config().Certs
|
||||
c.JSON(200, model.SuccessD(certs))
|
||||
}
|
||||
|
||||
func (h *CertHandler) Get(c *gin.Context) {
|
||||
log.Println("get cert")
|
||||
name := c.Query("name")
|
||||
if name == "" {
|
||||
c.Error(model.NewAppError(500, "请输入名称"))
|
||||
return
|
||||
}
|
||||
cert, _ := conf.FindCert(name)
|
||||
c.JSON(200, model.SuccessD(cert))
|
||||
}
|
||||
|
||||
func (h *CertHandler) Refresh(c *gin.Context) {
|
||||
log.Println("refresh cert")
|
||||
name := c.Query("name")
|
||||
if name == "" {
|
||||
c.Error(model.NewAppError(500, "请输入名称"))
|
||||
return
|
||||
}
|
||||
cert, _ := conf.FindCert(name)
|
||||
acme.Apply(cert)
|
||||
c.JSON(200, model.Success())
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package handle
|
||||
package http_handler
|
||||
|
||||
import (
|
||||
"acme-mana/src/conf"
|
|
@ -1,4 +1,4 @@
|
|||
package handle
|
||||
package http_handler
|
||||
|
||||
import (
|
||||
"acme-mana/src/server/model"
|
53
src/server/socker-server.go
Normal file
53
src/server/socker-server.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"acme-mana/src/common"
|
||||
socker_handler "acme-mana/src/server/socket_handler"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
type SocketServer struct {
|
||||
}
|
||||
|
||||
func (s *SocketServer) InitSocketServer() {
|
||||
|
||||
}
|
||||
|
||||
func (s *SocketServer) Start() {
|
||||
go start()
|
||||
}
|
||||
|
||||
func start() {
|
||||
log.Println("启动指令监听服务...")
|
||||
// 删除旧的 socket 文件
|
||||
if _, err := os.Stat(common.SocketFile); err == nil {
|
||||
err := os.Remove(common.SocketFile)
|
||||
if err != nil {
|
||||
log.Fatalf("无法删除Socket文件: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
listener, err := net.Listen("unix", common.SocketFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to listen on socket: %v", err)
|
||||
}
|
||||
defer func(listener net.Listener) {
|
||||
err := listener.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("无法关闭Socket文件监听: %v", err)
|
||||
}
|
||||
}(listener)
|
||||
|
||||
for {
|
||||
log.Println("等待连接...")
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Printf("无法建立连接: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go socker_handler.HandleConnection(conn)
|
||||
}
|
||||
}
|
1
src/server/socket_handler/cert.go
Normal file
1
src/server/socket_handler/cert.go
Normal file
|
@ -0,0 +1 @@
|
|||
package socket_handler
|
47
src/server/socket_handler/handler.go
Normal file
47
src/server/socket_handler/handler.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package socket_handler
|
||||
|
||||
import (
|
||||
"acme-mana/src/common"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
func HandleConnection(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read command: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
command := string(buf[:n])
|
||||
log.Printf("Received command: %s", command)
|
||||
|
||||
// 在这里处理接收到的命令
|
||||
switch command {
|
||||
case "stop":
|
||||
onStop()
|
||||
default:
|
||||
onCommand(command)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
收到命令
|
||||
*/
|
||||
func onCommand(command string) {
|
||||
|
||||
}
|
||||
|
||||
// 收到停止命令
|
||||
func onStop() {
|
||||
log.Println("停止守护进程...")
|
||||
os.Remove(common.PidFile)
|
||||
log.Println("删除PID文件...")
|
||||
os.Remove(common.SocketFile)
|
||||
log.Println("删除Socket文件...")
|
||||
os.Exit(0)
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
package server
|
||||
|
||||
var Instance *HttpServer
|
||||
var HttpInstance *HttpServer
|
||||
var SocketInstance *SocketServer
|
||||
|
|
168
src/task.go
168
src/task.go
|
@ -1,86 +1,86 @@
|
|||
package src
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
var AutoRefreshCertTicker = time.NewTicker(time.Hour)
|
||||
|
||||
func AutoRefreshCert() {
|
||||
log.Println("Start auto refresh cert")
|
||||
defer AutoRefreshCertTicker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-AutoRefreshCertTicker.C:
|
||||
doRefreshCert()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doRefreshCert() {
|
||||
domains := GetAppConfig().Domains
|
||||
for _, domain := range domains {
|
||||
doRefreshCertOnce(domain)
|
||||
}
|
||||
}
|
||||
|
||||
func doRefreshCertOnce(domain Domain) {
|
||||
name := domain.Name
|
||||
dir := GetAppConfig().CertDir
|
||||
certDir := path.Join(dir, name)
|
||||
if !ValidExist(certDir, domain) {
|
||||
Apply(domain)
|
||||
}
|
||||
infoFile := path.Join(certDir, CertInfoFileName)
|
||||
certInfo := ParseCertInfo(infoFile, domain)
|
||||
log.Println("Checking if the certificate is expired, Domain: {}", name)
|
||||
if certInfo.Info.NotAfter.Sub(time.Now()) < 14*24*time.Hour {
|
||||
log.Println("Apply for a certificate that is about to expire, domain name:", name)
|
||||
Apply(domain)
|
||||
}
|
||||
}
|
||||
|
||||
func ValidExist(certDir string, domain Domain) bool {
|
||||
_, err := os.Stat(certDir)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("Applying for a certificate, Domain: %s certificate directory does not exist!", domain.Name)
|
||||
return false
|
||||
}
|
||||
if !ExistFile(certDir, CertFileName) {
|
||||
log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, CertFileName)
|
||||
return false
|
||||
}
|
||||
if !ExistFile(certDir, KeyFileName) {
|
||||
log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, KeyFileName)
|
||||
return false
|
||||
}
|
||||
if !ExistFile(certDir, CertInfoFileName) {
|
||||
log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, CertInfoFileName)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ParseCertInfo(infoFile string, domain Domain) CertInfo {
|
||||
infoBytes, err := os.ReadFile(infoFile)
|
||||
if err != nil {
|
||||
log.Println("Failed to read cert info file, Domain: {}", domain.Name)
|
||||
}
|
||||
var certInfo CertInfo
|
||||
err = json.Unmarshal(infoBytes, &certInfo)
|
||||
//if err != nil {
|
||||
// log.Println("Failed to parse cert info file, Domain: {}", domain.Name)
|
||||
//}
|
||||
return certInfo
|
||||
}
|
||||
|
||||
func ExistFile(dir string, fileName string) bool {
|
||||
f := path.Join(dir, fileName)
|
||||
_, err := os.Stat(f)
|
||||
return !os.IsNotExist(err)
|
||||
|
||||
}
|
||||
//import (
|
||||
// "encoding/json"
|
||||
// "log"
|
||||
// "os"
|
||||
// "path"
|
||||
// "time"
|
||||
//)
|
||||
//
|
||||
//var AutoRefreshCertTicker = time.NewTicker(time.Hour)
|
||||
//
|
||||
//func AutoRefreshCert() {
|
||||
// log.Println("Start auto refresh cert")
|
||||
// defer AutoRefreshCertTicker.Stop()
|
||||
// for {
|
||||
// select {
|
||||
// case <-AutoRefreshCertTicker.C:
|
||||
// doRefreshCert()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func doRefreshCert() {
|
||||
// domains := GetAppConfig().Domains
|
||||
// for _, domain := range domains {
|
||||
// doRefreshCertOnce(domain)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func doRefreshCertOnce(domain Domain) {
|
||||
// name := domain.Name
|
||||
// dir := GetAppConfig().CertDir
|
||||
// certDir := path.Join(dir, name)
|
||||
// if !ValidExist(certDir, domain) {
|
||||
// Apply(domain)
|
||||
// }
|
||||
// infoFile := path.Join(certDir, CertInfoFileName)
|
||||
// certInfo := ParseCertInfo(infoFile, domain)
|
||||
// log.Println("Checking if the certificate is expired, Domain: {}", name)
|
||||
// if certInfo.Info.NotAfter.Sub(time.Now()) < 14*24*time.Hour {
|
||||
// log.Println("Apply for a certificate that is about to expire, domain name:", name)
|
||||
// Apply(domain)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func ValidExist(certDir string, domain Domain) bool {
|
||||
// _, err := os.Stat(certDir)
|
||||
// if os.IsNotExist(err) {
|
||||
// log.Printf("Applying for a certificate, Domain: %s certificate directory does not exist!", domain.Name)
|
||||
// return false
|
||||
// }
|
||||
// if !ExistFile(certDir, CertFileName) {
|
||||
// log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, CertFileName)
|
||||
// return false
|
||||
// }
|
||||
// if !ExistFile(certDir, KeyFileName) {
|
||||
// log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, KeyFileName)
|
||||
// return false
|
||||
// }
|
||||
// if !ExistFile(certDir, CertInfoFileName) {
|
||||
// log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, CertInfoFileName)
|
||||
// return false
|
||||
// }
|
||||
// return true
|
||||
//}
|
||||
//
|
||||
//func ParseCertInfo(infoFile string, domain Domain) CertInfo {
|
||||
// infoBytes, err := os.ReadFile(infoFile)
|
||||
// if err != nil {
|
||||
// log.Println("Failed to read cert info file, Domain: {}", domain.Name)
|
||||
// }
|
||||
// var certInfo CertInfo
|
||||
// err = json.Unmarshal(infoBytes, &certInfo)
|
||||
// //if err != nil {
|
||||
// // log.Println("Failed to parse cert info file, Domain: {}", domain.Name)
|
||||
// //}
|
||||
// return certInfo
|
||||
//}
|
||||
//
|
||||
//func ExistFile(dir string, fileName string) bool {
|
||||
// f := path.Join(dir, fileName)
|
||||
// _, err := os.Stat(f)
|
||||
// return !os.IsNotExist(err)
|
||||
//
|
||||
//}
|
||||
|
|
114
src/task/task.go
Normal file
114
src/task/task.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"acme-mana/src/acme"
|
||||
"acme-mana/src/common"
|
||||
"acme-mana/src/conf"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
var AutoRefreshCertTicker = time.NewTicker(time.Hour)
|
||||
|
||||
func AutoRefreshCert() {
|
||||
log.Println("Start auto refresh cert")
|
||||
defer AutoRefreshCertTicker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-AutoRefreshCertTicker.C:
|
||||
doRefreshCert()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doRefreshCert() {
|
||||
config := conf.Config()
|
||||
certs := config.Certs
|
||||
|
||||
for _, cert := range *certs {
|
||||
doRefreshCertOne(&cert)
|
||||
}
|
||||
|
||||
//domains := GetAppConfig().Domains
|
||||
//for _, domain := range domains {
|
||||
// doRefreshCertOnce(domain)
|
||||
//}
|
||||
}
|
||||
|
||||
func doRefreshCertOne(cert *conf.CertConf) {
|
||||
name := cert.Name
|
||||
dir := cert.Dir
|
||||
certDir := path.Join(dir, name)
|
||||
if !ValidExist(certDir, cert) {
|
||||
acme.Apply(cert)
|
||||
return
|
||||
}
|
||||
infoFile := path.Join(certDir, common.CertInfoFileName)
|
||||
certInfo := ParseCertInfo(infoFile, cert)
|
||||
log.Println("校验当前证书有效时间, 证书: {}", name)
|
||||
if certInfo.Info.NotAfter.Sub(time.Now()) < 14*24*time.Hour {
|
||||
log.Println("申请即将过期的证书: {}", name)
|
||||
acme.Apply(cert)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//func doRefreshCertOnce(domain Domain) {
|
||||
// name := domain.Name
|
||||
// dir := GetAppConfig().CertDir
|
||||
// certDir := path.Join(dir, name)
|
||||
// if !ValidExist(certDir, domain) {
|
||||
// Apply(domain)
|
||||
// }
|
||||
// infoFile := path.Join(certDir, CertInfoFileName)
|
||||
// certInfo := ParseCertInfo(infoFile, domain)
|
||||
// log.Println("Checking if the certificate is expired, Domain: {}", name)
|
||||
// if certInfo.Info.NotAfter.Sub(time.Now()) < 14*24*time.Hour {
|
||||
// log.Println("Apply for a certificate that is about to expire, domain name:", name)
|
||||
// Apply(domain)
|
||||
// }
|
||||
//}
|
||||
|
||||
func ValidExist(certDir string, cert *conf.CertConf) bool {
|
||||
_, err := os.Stat(certDir)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("Applying for a certificate, Domain: %s certificate directory does not exist!", cert.Name)
|
||||
return false
|
||||
}
|
||||
if !ExistFile(certDir, common.CertFileName) {
|
||||
log.Printf("Applying for a certificate, Domain: %s %s does not exist!", cert.Name, common.CertFileName)
|
||||
return false
|
||||
}
|
||||
if !ExistFile(certDir, common.KeyFileName) {
|
||||
log.Printf("Applying for a certificate, Domain: %s %s does not exist!", cert.Name, common.KeyFileName)
|
||||
return false
|
||||
}
|
||||
if !ExistFile(certDir, common.CertInfoFileName) {
|
||||
log.Printf("Applying for a certificate, Domain: %s %s does not exist!", cert.Name, common.CertInfoFileName)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ParseCertInfo(infoFile string, cert *conf.CertConf) *acme.CertInfo {
|
||||
infoBytes, err := os.ReadFile(infoFile)
|
||||
if err != nil {
|
||||
log.Println("Failed to read cert info file, Domain: {}", cert.Name)
|
||||
}
|
||||
var certInfo acme.CertInfo
|
||||
err = json.Unmarshal(infoBytes, &certInfo)
|
||||
//if err != nil {
|
||||
// log.Println("Failed to parse cert info file, Domain: {}", domain.Name)
|
||||
//}
|
||||
return &certInfo
|
||||
}
|
||||
|
||||
func ExistFile(dir string, fileName string) bool {
|
||||
f := path.Join(dir, fileName)
|
||||
_, err := os.Stat(f)
|
||||
return !os.IsNotExist(err)
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package src
|
||||
|
||||
const PidFile = "acme-mana.pid"
|
||||
const SocketFile = "acme-mana.sock"
|
||||
|
||||
const CertFileName = "fullchain.pem"
|
||||
const KeyFileName = "privkey.pem"
|
||||
const CertInfoFileName = "info.json"
|
||||
|
||||
var DnsProviderSupports = []string{"alidns", "tencentcloud", "cloudflare"}
|
||||
|
||||
var appConfig *AppConfig = ReadConfig()
|
||||
|
||||
func GetAppConfig() *AppConfig {
|
||||
return appConfig
|
||||
}
|
||||
|
||||
var envConf *EnvConf = InitRuntimeConf()
|
||||
|
||||
func GetEnvConf() *EnvConf {
|
||||
return envConf
|
||||
}
|
||||
|
||||
type DomainData struct {
|
||||
Fullchain string `json:"fullchain"`
|
||||
Key string `json:"key"`
|
||||
Info string `json:"info"`
|
||||
}
|
Loading…
Reference in New Issue
Block a user