Compare commits
6 Commits
55d241b873
...
8d063e1330
Author | SHA1 | Date | |
---|---|---|---|
8d063e1330 | |||
a742f096f0 | |||
d505213d2c | |||
510af680c4 | |||
4f9086650e | |||
e6d412c114 |
|
@ -1,174 +0,0 @@
|
||||||
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
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"acme-mana/src/cmd/cmd_handle"
|
||||||
"acme-mana/src/common"
|
"acme-mana/src/common"
|
||||||
"acme-mana/src/conf"
|
"acme-mana/src/conf"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -31,7 +32,8 @@ func InitCmd() (*cobra.Command, error) {
|
||||||
//rootCmd.GenPowerShellCompletion(os.Stdout)
|
//rootCmd.GenPowerShellCompletion(os.Stdout)
|
||||||
//rootCmd.GenZshCompletion(os.Stdout)
|
//rootCmd.GenZshCompletion(os.Stdout)
|
||||||
|
|
||||||
conf.InitAppConfig()
|
conf.LoadAppConfig()
|
||||||
|
|
||||||
err := rootCmd.Execute()
|
err := rootCmd.Execute()
|
||||||
|
|
||||||
return rootCmd, err
|
return rootCmd, err
|
||||||
|
@ -51,7 +53,7 @@ func initConfCmd() *cobra.Command {
|
||||||
Short: "查看配置列表",
|
Short: "查看配置列表",
|
||||||
Long: "查看系统配置列表",
|
Long: "查看系统配置列表",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
confShow(cmd, args)
|
cmd_handle.ConfShow(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
confCmd.AddCommand(confShow)
|
confCmd.AddCommand(confShow)
|
||||||
|
@ -61,10 +63,13 @@ func initConfCmd() *cobra.Command {
|
||||||
func initServerCmd() *cobra.Command {
|
func initServerCmd() *cobra.Command {
|
||||||
serverCmd := &cobra.Command{
|
serverCmd := &cobra.Command{
|
||||||
Use: "server",
|
Use: "server",
|
||||||
Short: "服务相关命令",
|
Short: "服务端命令",
|
||||||
Long: "配置服务相关参数, 如监听端口,监听地址等",
|
Long: "acme-mana服务端相关命令, 配置服务相关参数, 如监听端口,监听地址等",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
cmd.Help()
|
err := cmd.Help()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
editCmd := &cobra.Command{
|
editCmd := &cobra.Command{
|
||||||
|
@ -72,7 +77,7 @@ func initServerCmd() *cobra.Command {
|
||||||
Short: "编辑服务配置",
|
Short: "编辑服务配置",
|
||||||
Long: "编辑服务配置",
|
Long: "编辑服务配置",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
editServer(cmd, args)
|
cmd_handle.ServerEdit(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
stateCmd := &cobra.Command{
|
stateCmd := &cobra.Command{
|
||||||
|
@ -80,7 +85,7 @@ func initServerCmd() *cobra.Command {
|
||||||
Short: "查看服务状态",
|
Short: "查看服务状态",
|
||||||
Long: "查看服务状态",
|
Long: "查看服务状态",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
serverState(cmd, args)
|
cmd_handle.ServerState(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
startCmd := &cobra.Command{
|
startCmd := &cobra.Command{
|
||||||
|
@ -88,7 +93,7 @@ func initServerCmd() *cobra.Command {
|
||||||
Short: "启动服务",
|
Short: "启动服务",
|
||||||
Long: "启动服务",
|
Long: "启动服务",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
startServer(cmd, args)
|
cmd_handle.ServerStart(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
stopCmd := &cobra.Command{
|
stopCmd := &cobra.Command{
|
||||||
|
@ -96,7 +101,7 @@ func initServerCmd() *cobra.Command {
|
||||||
Short: "停止服务",
|
Short: "停止服务",
|
||||||
Long: "停止服务",
|
Long: "停止服务",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
stopServer(cmd, args)
|
cmd_handle.ServerStop(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +126,7 @@ func initTaskCmd() *cobra.Command {
|
||||||
Short: "编辑定时任务配置",
|
Short: "编辑定时任务配置",
|
||||||
Long: "编辑定时任务配置",
|
Long: "编辑定时任务配置",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
editTask(cmd, args)
|
cmd_handle.TaskEdit(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
startTask := &cobra.Command{
|
startTask := &cobra.Command{
|
||||||
|
@ -129,7 +134,7 @@ func initTaskCmd() *cobra.Command {
|
||||||
Short: "启动定时任务",
|
Short: "启动定时任务",
|
||||||
Long: "启动定时任务",
|
Long: "启动定时任务",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
startTask(cmd, args)
|
cmd_handle.TaskStart(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
stopTask := &cobra.Command{
|
stopTask := &cobra.Command{
|
||||||
|
@ -137,7 +142,7 @@ func initTaskCmd() *cobra.Command {
|
||||||
Short: "停止定时任务",
|
Short: "停止定时任务",
|
||||||
Long: "停止定时任务",
|
Long: "停止定时任务",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
stopTask(cmd, args)
|
cmd_handle.TaskStop(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
statusTask := &cobra.Command{
|
statusTask := &cobra.Command{
|
||||||
|
@ -145,7 +150,7 @@ func initTaskCmd() *cobra.Command {
|
||||||
Short: "查看定时任务状态",
|
Short: "查看定时任务状态",
|
||||||
Long: "查看定时任务状态",
|
Long: "查看定时任务状态",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
statusTask(cmd, args)
|
cmd_handle.TaskStatus(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
runTask := &cobra.Command{
|
runTask := &cobra.Command{
|
||||||
|
@ -153,7 +158,7 @@ func initTaskCmd() *cobra.Command {
|
||||||
Short: "手动执行一次任务",
|
Short: "手动执行一次任务",
|
||||||
Long: "手动执行一次任务",
|
Long: "手动执行一次任务",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
runTask(cmd, args)
|
cmd_handle.TaskRun(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
taskCmd.AddCommand(editTask)
|
taskCmd.AddCommand(editTask)
|
||||||
|
@ -178,7 +183,7 @@ func initProviderCmd() *cobra.Command {
|
||||||
Short: "列出支持的DNS服务商",
|
Short: "列出支持的DNS服务商",
|
||||||
Long: "列出支持的DNS服务商",
|
Long: "列出支持的DNS服务商",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
providerSupport(cmd, args)
|
cmd_handle.ProviderSupport(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
providerList := &cobra.Command{
|
providerList := &cobra.Command{
|
||||||
|
@ -186,7 +191,7 @@ func initProviderCmd() *cobra.Command {
|
||||||
Short: "列出已配置的DNS服务商",
|
Short: "列出已配置的DNS服务商",
|
||||||
Long: "列出已配置的DNS服务商",
|
Long: "列出已配置的DNS服务商",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
providerList(cmd, args)
|
cmd_handle.ProviderList(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
providerAdd := &cobra.Command{
|
providerAdd := &cobra.Command{
|
||||||
|
@ -194,7 +199,7 @@ func initProviderCmd() *cobra.Command {
|
||||||
Short: "添加DNS服务商",
|
Short: "添加DNS服务商",
|
||||||
Long: "添加DNS服务商",
|
Long: "添加DNS服务商",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
providerAdd(cmd, args)
|
cmd_handle.ProviderAdd(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
providerEdit := &cobra.Command{
|
providerEdit := &cobra.Command{
|
||||||
|
@ -202,7 +207,7 @@ func initProviderCmd() *cobra.Command {
|
||||||
Short: "编辑DNS服务商",
|
Short: "编辑DNS服务商",
|
||||||
Long: "编辑DNS服务商",
|
Long: "编辑DNS服务商",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
providerEdit(cmd, args)
|
cmd_handle.ProviderEdit(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
providerDelete := &cobra.Command{
|
providerDelete := &cobra.Command{
|
||||||
|
@ -210,7 +215,7 @@ func initProviderCmd() *cobra.Command {
|
||||||
Short: "删除DNS服务商",
|
Short: "删除DNS服务商",
|
||||||
Long: "删除DNS服务商",
|
Long: "删除DNS服务商",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
providerDelete(cmd, args)
|
cmd_handle.ProviderDelete(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +241,7 @@ func certCmd() *cobra.Command {
|
||||||
Short: "列出已配置的证书",
|
Short: "列出已配置的证书",
|
||||||
Long: "列出已配置的证书",
|
Long: "列出已配置的证书",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
certList(cmd, args)
|
cmd_handle.CertList(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
certAdd := &cobra.Command{
|
certAdd := &cobra.Command{
|
||||||
|
@ -244,7 +249,7 @@ func certCmd() *cobra.Command {
|
||||||
Short: "添加证书",
|
Short: "添加证书",
|
||||||
Long: "添加证书",
|
Long: "添加证书",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
certAdd(cmd, args)
|
cmd_handle.CertAdd(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
certDelete := &cobra.Command{
|
certDelete := &cobra.Command{
|
||||||
|
@ -252,7 +257,7 @@ func certCmd() *cobra.Command {
|
||||||
Short: "删除证书",
|
Short: "删除证书",
|
||||||
Long: "删除证书",
|
Long: "删除证书",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
certDelete(cmd, args)
|
cmd_handle.CertDelete(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
editCert := &cobra.Command{
|
editCert := &cobra.Command{
|
||||||
|
@ -260,7 +265,7 @@ func certCmd() *cobra.Command {
|
||||||
Short: "编辑证书",
|
Short: "编辑证书",
|
||||||
Long: "编辑证书",
|
Long: "编辑证书",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
editCert(cmd, args)
|
cmd_handle.CertEdit(cmd, args)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
certCmd.AddCommand(certList)
|
certCmd.AddCommand(certList)
|
||||||
|
@ -274,7 +279,7 @@ func acmeCmd() *cobra.Command {
|
||||||
acmeCmd := &cobra.Command{
|
acmeCmd := &cobra.Command{
|
||||||
Use: "acme",
|
Use: "acme",
|
||||||
Short: "ACME相关命令",
|
Short: "ACME相关命令",
|
||||||
Long: "ACME相关命令",
|
Long: "acme.sh原生命令",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
cmd.Help()
|
cmd.Help()
|
||||||
},
|
},
|
||||||
|
|
22
src/cmd/cmd_handle/cert.go
Normal file
22
src/cmd/cmd_handle/cert.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package cmd_handle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CertEdit(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("edit cert")
|
||||||
|
}
|
||||||
|
|
||||||
|
func CertDelete(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("delete cert")
|
||||||
|
}
|
||||||
|
|
||||||
|
func CertAdd(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("add cert")
|
||||||
|
}
|
||||||
|
|
||||||
|
func CertList(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("list cert")
|
||||||
|
}
|
19
src/cmd/cmd_handle/conf.go
Normal file
19
src/cmd/cmd_handle/conf.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package cmd_handle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"acme-mana/src/conf"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfShow 打印 配置信息
|
||||||
|
func ConfShow(cmd *cobra.Command, args []string) {
|
||||||
|
tea.Println()
|
||||||
|
confJson, err := json.MarshalIndent(conf.Config(), "", " ")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("序列化配置信息失败:", err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(confJson))
|
||||||
|
}
|
26
src/cmd/cmd_handle/provider.go
Normal file
26
src/cmd/cmd_handle/provider.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package cmd_handle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProviderDelete(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("delete provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProviderEdit(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("edit provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProviderAdd(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("add provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProviderList(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("list provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProviderSupport(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("support provider")
|
||||||
|
}
|
148
src/cmd/cmd_handle/server.go
Normal file
148
src/cmd/cmd_handle/server.go
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package cmd_handle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"acme-mana/src/common"
|
||||||
|
"acme-mana/src/conf"
|
||||||
|
"acme-mana/src/server"
|
||||||
|
"acme-mana/src/util"
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var stdout *os.File
|
||||||
|
var stderr *os.File
|
||||||
|
|
||||||
|
func ServerStart(command *cobra.Command, args []string) {
|
||||||
|
initLog()
|
||||||
|
|
||||||
|
isDaemon := os.Getenv("GO_DAEMON")
|
||||||
|
log.Println("守护进程启动: " + isDaemon)
|
||||||
|
|
||||||
|
if isRunning() {
|
||||||
|
log.Println("守护进程已启动, 跳过执行")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDaemon == "1" {
|
||||||
|
daemonStart()
|
||||||
|
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 ServerEdit(cmd *cobra.Command, args []string) {
|
||||||
|
s := conf.Config().Server
|
||||||
|
host := util.ReadLine("请输入服务监听地址;", s.Host)
|
||||||
|
port := util.ReadInt("请输入服务监听端口;", strconv.Itoa(s.Port))
|
||||||
|
key := util.ReadLine("请输入服务通信认证秘钥;", s.Key)
|
||||||
|
conf.EditServer(host, port, key)
|
||||||
|
fmt.Println("服务监听配置已完成")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerState(cmd *cobra.Command, args []string) {
|
||||||
|
server.HttpInstance.Status()
|
||||||
|
}
|
||||||
|
func ServerStop(cmd *cobra.Command, args []string) {
|
||||||
|
server.HttpInstance.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
26
src/cmd/cmd_handle/task.go
Normal file
26
src/cmd/cmd_handle/task.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package cmd_handle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TaskEdit(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("edit task")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TaskStatus(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("status task")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TaskStop(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("stop task")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TaskStart(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("start task")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TaskRun(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("run task")
|
||||||
|
}
|
|
@ -1,95 +0,0 @@
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"acme-mana/src/common"
|
|
||||||
"acme-mana/src/conf"
|
|
||||||
"acme-mana/src/http"
|
|
||||||
"acme-mana/src/util"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 打印 配置信息
|
|
||||||
func confShow(cmd *cobra.Command, args []string) {
|
|
||||||
tea.Println()
|
|
||||||
confJson, err := json.MarshalIndent(common.AppConf, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("序列化配置信息失败:", err)
|
|
||||||
}
|
|
||||||
fmt.Println(string(confJson))
|
|
||||||
}
|
|
||||||
|
|
||||||
func editServer(cmd *cobra.Command, args []string) {
|
|
||||||
server := common.AppConf.Server
|
|
||||||
server.Host = util.ReadLine("请输入服务监听地址;", server.Host)
|
|
||||||
server.Port = util.ReadInt("请输入服务监听端口;", strconv.Itoa(server.Port))
|
|
||||||
conf.WriteConfig()
|
|
||||||
fmt.Println("服务监听配置已完成")
|
|
||||||
}
|
|
||||||
|
|
||||||
func serverState(cmd *cobra.Command, args []string) {
|
|
||||||
http.Start()
|
|
||||||
}
|
|
||||||
func startServer(cmd *cobra.Command, args []string) {
|
|
||||||
http.Start()
|
|
||||||
}
|
|
||||||
func stopServer(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("stop server")
|
|
||||||
}
|
|
||||||
|
|
||||||
func editTask(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("edit task")
|
|
||||||
}
|
|
||||||
func runTask(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("run task")
|
|
||||||
}
|
|
||||||
|
|
||||||
func statusTask(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("status task")
|
|
||||||
}
|
|
||||||
|
|
||||||
func stopTask(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("stop task")
|
|
||||||
}
|
|
||||||
|
|
||||||
func startTask(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("start task")
|
|
||||||
}
|
|
||||||
func providerDelete(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("delete provider")
|
|
||||||
}
|
|
||||||
|
|
||||||
func providerEdit(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("edit provider")
|
|
||||||
}
|
|
||||||
|
|
||||||
func providerAdd(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("add provider")
|
|
||||||
}
|
|
||||||
|
|
||||||
func providerList(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("list provider")
|
|
||||||
}
|
|
||||||
|
|
||||||
func providerSupport(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("support provider")
|
|
||||||
}
|
|
||||||
|
|
||||||
func editCert(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("edit cert")
|
|
||||||
}
|
|
||||||
|
|
||||||
func certDelete(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("delete cert")
|
|
||||||
}
|
|
||||||
|
|
||||||
func certAdd(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("add cert")
|
|
||||||
}
|
|
||||||
|
|
||||||
func certList(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println("list cert")
|
|
||||||
}
|
|
161
src/command.go
161
src/command.go
|
@ -1,83 +1,82 @@
|
||||||
package src
|
package src
|
||||||
|
|
||||||
import (
|
//import (
|
||||||
"log"
|
// "log"
|
||||||
"net"
|
// "net"
|
||||||
"os"
|
// "os"
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
// InitSocket 初始化 socket 文件
|
//// InitSocket 初始化 socket 文件
|
||||||
func InitSocket() {
|
//func InitSocket() {
|
||||||
log.Println("Start listen command")
|
// log.Println("Start listen command")
|
||||||
// 删除旧的 socket 文件
|
// // 删除旧的 socket 文件
|
||||||
if _, err := os.Stat(SocketFile); err == nil {
|
// if _, err := os.Stat(SocketFile); err == nil {
|
||||||
os.Remove(SocketFile)
|
// os.Remove(SocketFile)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
listener, err := net.Listen("unix", SocketFile)
|
// listener, err := net.Listen("unix", SocketFile)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to listen on socket: %v", err)
|
// log.Fatalf("Failed to listen on socket: %v", err)
|
||||||
}
|
// }
|
||||||
defer listener.Close()
|
// defer listener.Close()
|
||||||
|
//
|
||||||
for {
|
// for {
|
||||||
log.Println("Waiting for connections...")
|
// log.Println("Waiting for connections...")
|
||||||
conn, err := listener.Accept()
|
// conn, err := listener.Accept()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Printf("Failed to accept connection: %v", err)
|
// log.Printf("Failed to accept connection: %v", err)
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
go handleConnection(conn)
|
// go handleConnection(conn)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
/*
|
///*
|
||||||
*
|
//*
|
||||||
处理连接
|
//处理连接
|
||||||
*/
|
//*/
|
||||||
func handleConnection(conn net.Conn) {
|
//func handleConnection(conn net.Conn) {
|
||||||
defer conn.Close()
|
// defer conn.Close()
|
||||||
|
//
|
||||||
buf := make([]byte, 1024)
|
// buf := make([]byte, 1024)
|
||||||
n, err := conn.Read(buf)
|
// n, err := conn.Read(buf)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Printf("Failed to read command: %v", err)
|
// log.Printf("Failed to read command: %v", err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
command := string(buf[:n])
|
// command := string(buf[:n])
|
||||||
log.Printf("Received command: %s", command)
|
// log.Printf("Received command: %s", command)
|
||||||
|
//
|
||||||
// 在这里处理接收到的命令
|
// // 在这里处理接收到的命令
|
||||||
switch command {
|
// switch command {
|
||||||
case "stop":
|
// case "stop":
|
||||||
onStop()
|
// onStop()
|
||||||
default:
|
// default:
|
||||||
onCommand(command)
|
// onCommand(command)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
/*
|
///*
|
||||||
*
|
//*
|
||||||
收到停止命令
|
//收到停止命令
|
||||||
*/
|
//*/
|
||||||
func onStop() {
|
//func onStop() {
|
||||||
log.Println("Stopping daemon...")
|
// log.Println("Stopping daemon...")// os.Remove(PidFile)
|
||||||
os.Remove(PidFile)
|
// log.Println("Remove PID File...")
|
||||||
log.Println("Remove PID File...")
|
// os.Remove(SocketFile)
|
||||||
os.Remove(SocketFile)
|
// log.Println("Remove Socket File...")
|
||||||
log.Println("Remove Socket File...")
|
// os.Exit(0)
|
||||||
os.Exit(0)
|
//}
|
||||||
}
|
//
|
||||||
|
///*
|
||||||
/*
|
//收到命令
|
||||||
收到命令
|
//*/
|
||||||
*/
|
//func onCommand(command string) {
|
||||||
func onCommand(command string) {
|
//
|
||||||
|
//}
|
||||||
}
|
//
|
||||||
|
//func onConfig() {
|
||||||
func onConfig() {
|
//
|
||||||
|
//}
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"acme-mana/src/model"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var RootCmd *cobra.Command
|
const PidFile = "acme-mana.pid"
|
||||||
|
const SocketFile = "acme-mana.sock"
|
||||||
|
|
||||||
var AppConf *model.AppConfig
|
const CertFileName = "fullchain.pem"
|
||||||
|
const KeyFileName = "privkey.pem"
|
||||||
|
const CertInfoFileName = "info.json"
|
||||||
|
|
||||||
|
var RootCmd *cobra.Command
|
||||||
|
|
63
src/conf/conf.go
Normal file
63
src/conf/conf.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package conf
|
||||||
|
|
||||||
|
// AppConfig
|
||||||
|
// 配置文件
|
||||||
|
type AppConfig struct {
|
||||||
|
// 服务器配置
|
||||||
|
Server *ServerConf `json:"server" yaml:"server"`
|
||||||
|
// 网页配置
|
||||||
|
Web *WebConf `json:"web" yaml:"web"`
|
||||||
|
// 任务配置
|
||||||
|
Task *TaskConf `json:"task" yaml:"task"`
|
||||||
|
// 认证配置
|
||||||
|
Providers *[]ProviderConf `json:"provider" yaml:"provider"`
|
||||||
|
// 证书配置
|
||||||
|
Certs *[]CertConf `json:"cert" yaml:"cert"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConf 服务端配置
|
||||||
|
type ServerConf struct {
|
||||||
|
// 监听地址
|
||||||
|
Host string `json:"host" yaml:"host"`
|
||||||
|
// 监听端口
|
||||||
|
Port int `json:"port" yaml:"port"`
|
||||||
|
// 通信密钥, DES 加密
|
||||||
|
Key string `json:"key" yaml:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebConf 网页服务配置
|
||||||
|
type WebConf struct {
|
||||||
|
// 是否启用
|
||||||
|
Enable bool `json:"enable" yaml:"enable"`
|
||||||
|
// 监听地址
|
||||||
|
Host string `json:"host" yaml:"host"`
|
||||||
|
// 监听端口
|
||||||
|
Port int `json:"port" yaml:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskConf 定时任务配置
|
||||||
|
type TaskConf struct {
|
||||||
|
// 启动延迟时间, 单位: 毫秒, 默认: 0
|
||||||
|
Delay int `json:"delay" yaml:"delay"`
|
||||||
|
// 间隔时间
|
||||||
|
Interval int `json:"interval" yaml:"interval"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProviderConf 三方认证配置
|
||||||
|
type ProviderConf struct {
|
||||||
|
// 认证名称
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
// 认证类型
|
||||||
|
Type string `json:"type" yaml:"type"`
|
||||||
|
// 认证配置
|
||||||
|
Conf map[string]string `json:"conf" yaml:"conf"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertConf 证书配置
|
||||||
|
type CertConf struct {
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
Provider string `json:"use" yaml:"provider"`
|
||||||
|
Dir string `json:"dir" yaml:"dir"`
|
||||||
|
Email string `json:"email" yaml:"email"`
|
||||||
|
Host []string `json:"host" yaml:"host"`
|
||||||
|
}
|
129
src/conf/func.go
Normal file
129
src/conf/func.go
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
package conf
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
func Config() *AppConfig {
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditServer(host string, port int, key string) *AppConfig {
|
||||||
|
appConf.Server.Host = host
|
||||||
|
appConf.Server.Port = port
|
||||||
|
appConf.Server.Key = key
|
||||||
|
WriteConfig()
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditWeb(host string, port int) *AppConfig {
|
||||||
|
appConf.Web.Host = host
|
||||||
|
appConf.Web.Port = port
|
||||||
|
WriteConfig()
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecWeb(run bool) *AppConfig {
|
||||||
|
appConf.Web.Enable = run
|
||||||
|
WriteConfig()
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditTask(delay int, interval int) *AppConfig {
|
||||||
|
appConf.Task.Delay = delay
|
||||||
|
appConf.Task.Interval = interval
|
||||||
|
WriteConfig()
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindProvider(name string) (*ProviderConf, int) {
|
||||||
|
for index, provider := range *appConf.Providers {
|
||||||
|
if provider.Name == name {
|
||||||
|
return &provider, index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddProvider(name string, typeName string, conf map[string]string) *AppConfig {
|
||||||
|
provider, _ := FindProvider(name)
|
||||||
|
if provider != nil {
|
||||||
|
log.Fatal("已存在相同名称的配置")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
provider = &ProviderConf{
|
||||||
|
Name: name,
|
||||||
|
Type: typeName,
|
||||||
|
Conf: conf,
|
||||||
|
}
|
||||||
|
providers := *appConf.Providers
|
||||||
|
newProviders := append(providers, *provider)
|
||||||
|
appConf.Providers = &newProviders
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditProvider(name string, typeName string, conf map[string]string) *AppConfig {
|
||||||
|
provider, _ := FindProvider(name)
|
||||||
|
if provider == nil {
|
||||||
|
log.Fatal("不存在该配置")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
provider.Type = typeName
|
||||||
|
provider.Conf = conf
|
||||||
|
WriteConfig()
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func RmProvider(name string) *AppConfig {
|
||||||
|
_, index := FindProvider(name)
|
||||||
|
if index == -1 {
|
||||||
|
log.Fatal("不存在该配置")
|
||||||
|
}
|
||||||
|
providers := *appConf.Providers
|
||||||
|
newProviders := append(providers[:index], providers[index+1:]...)
|
||||||
|
appConf.Providers = &newProviders
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindCert(name string) (*CertConf, int) {
|
||||||
|
for index, cert := range *appConf.Certs {
|
||||||
|
if cert.Name == name {
|
||||||
|
return &cert, index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, -1
|
||||||
|
}
|
||||||
|
func FindCertByProvider(provider string) *[]CertConf {
|
||||||
|
var result []CertConf
|
||||||
|
for _, cert := range *appConf.Certs {
|
||||||
|
if cert.Provider == provider {
|
||||||
|
result = append(result, cert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddCert(cert *CertConf) *AppConfig {
|
||||||
|
certs := *appConf.Certs
|
||||||
|
newCerts := append(certs, *cert)
|
||||||
|
appConf.Certs = &newCerts
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
func EditCert(name string, cert *CertConf) *AppConfig {
|
||||||
|
_, index := FindCert(name)
|
||||||
|
if index == -1 {
|
||||||
|
log.Fatal("不存在该配置")
|
||||||
|
}
|
||||||
|
certs := *appConf.Certs
|
||||||
|
certs[index] = *cert
|
||||||
|
appConf.Certs = &certs
|
||||||
|
return appConf
|
||||||
|
}
|
||||||
|
func RmCert(name string) *AppConfig {
|
||||||
|
_, index := FindCert(name)
|
||||||
|
if index == -1 {
|
||||||
|
log.Fatal("不存在该配置")
|
||||||
|
}
|
||||||
|
certs := *appConf.Certs
|
||||||
|
newCerts := append(certs[:index], certs[index+1:]...)
|
||||||
|
appConf.Certs = &newCerts
|
||||||
|
return appConf
|
||||||
|
}
|
|
@ -2,54 +2,43 @@ package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"acme-mana/src/common"
|
"acme-mana/src/common"
|
||||||
"acme-mana/src/model"
|
|
||||||
"acme-mana/src/util"
|
"acme-mana/src/util"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func readAppConfig(c *model.AppConfig) *model.AppConfig {
|
func LoadAppConfig() {
|
||||||
confFile, err := common.RootCmd.PersistentFlags().GetString("conf")
|
// 读取配置文件位置
|
||||||
if err != nil {
|
confFile := getConfFile()
|
||||||
log.Fatalln("读取配置文件参数失败")
|
// 判断配资文件是否存在
|
||||||
}
|
if _, err := os.Stat(confFile); os.IsNotExist(err) {
|
||||||
|
|
||||||
_, err = os.Stat(confFile)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
log.Println("配置文件不存在, 自动创建")
|
log.Println("配置文件不存在, 自动创建")
|
||||||
writeConf(c, confFile)
|
config := defaultAppConfig()
|
||||||
|
log.Println("默认配置文件创建成功")
|
||||||
|
log.Println("服务器通信密钥: " + config.Server.Key)
|
||||||
|
appConf = config
|
||||||
|
writeConf(appConf, confFile)
|
||||||
|
} else {
|
||||||
|
appConf = readAppConfig(appConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.ReadFile(confFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("读取配置文件失败")
|
|
||||||
}
|
|
||||||
//var conf = &model.AppConfig{}
|
|
||||||
err = yaml.Unmarshal(file, c)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("解析配置文件失败")
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitAppConfig() {
|
|
||||||
common.AppConf = DefaultAppConfig()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshConfig 刷新配置
|
||||||
func RefreshConfig() {
|
func RefreshConfig() {
|
||||||
readAppConfig(common.AppConf)
|
readAppConfig(appConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteConfig 写入配置文件
|
||||||
func WriteConfig() {
|
func WriteConfig() {
|
||||||
confFile, err := common.RootCmd.PersistentFlags().GetString("conf")
|
confFile, err := common.RootCmd.PersistentFlags().GetString("conf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("读取配置文件参数失败")
|
log.Fatalln("读取配置文件参数失败")
|
||||||
}
|
}
|
||||||
writeConf(common.AppConf, confFile)
|
writeConf(appConf, confFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeConf(c *model.AppConfig, file string) {
|
func writeConf(c *AppConfig, file string) {
|
||||||
out, err := yaml.Marshal(c)
|
out, err := yaml.Marshal(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("序列化配置文件失败")
|
log.Fatalln("序列化配置文件失败")
|
||||||
|
@ -61,13 +50,22 @@ func writeConf(c *model.AppConfig, file string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultAppConfig() *model.AppConfig {
|
// DefaultAppConfig 默认配置
|
||||||
return &model.AppConfig{
|
func defaultAppConfig() *AppConfig {
|
||||||
Server: &model.ServerConf{
|
serverKey := util.RandomStr(32)
|
||||||
|
|
||||||
|
return &AppConfig{
|
||||||
|
Server: &ServerConf{
|
||||||
Host: "0.0.0.0",
|
Host: "0.0.0.0",
|
||||||
Port: 36851,
|
Port: 36851,
|
||||||
|
Key: serverKey,
|
||||||
},
|
},
|
||||||
Task: &model.TaskConf{
|
Web: &WebConf{
|
||||||
|
Enable: false,
|
||||||
|
Host: "0.0.0.0",
|
||||||
|
Port: 36852,
|
||||||
|
},
|
||||||
|
Task: &TaskConf{
|
||||||
Delay: 0,
|
Delay: 0,
|
||||||
Interval: 0,
|
Interval: 0,
|
||||||
},
|
},
|
||||||
|
@ -75,3 +73,27 @@ func DefaultAppConfig() *model.AppConfig {
|
||||||
Providers: nil,
|
Providers: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getConfFile 获取配置文件位置
|
||||||
|
func getConfFile() string {
|
||||||
|
confFile, err := common.RootCmd.PersistentFlags().GetString("conf")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("读取配置文件参数失败")
|
||||||
|
}
|
||||||
|
return confFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// readAppConfig 读取配置文件
|
||||||
|
func readAppConfig(c *AppConfig) *AppConfig {
|
||||||
|
confFile := getConfFile()
|
||||||
|
file, err := os.ReadFile(confFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("读取配置文件失败")
|
||||||
|
}
|
||||||
|
//var conf = &model.AppConfig{}
|
||||||
|
err = yaml.Unmarshal(file, c)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("解析配置文件失败")
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
3
src/conf/variable.go
Normal file
3
src/conf/variable.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package conf
|
||||||
|
|
||||||
|
var appConf *AppConfig
|
530
src/config.go
530
src/config.go
|
@ -1,267 +1,267 @@
|
||||||
package src
|
package src
|
||||||
|
|
||||||
import (
|
//import (
|
||||||
"acme-mana/src/crypto"
|
// "acme-mana/src/crypto"
|
||||||
"bufio"
|
// "bufio"
|
||||||
"fmt"
|
// "fmt"
|
||||||
"github.com/go-acme/lego/v4/log"
|
// "github.com/go-acme/lego/v4/log"
|
||||||
"github.com/go-acme/lego/v4/platform/config/env"
|
// "github.com/go-acme/lego/v4/platform/config/env"
|
||||||
"gopkg.in/yaml.v3"
|
// "gopkg.in/yaml.v3"
|
||||||
"os"
|
// "os"
|
||||||
"strings"
|
// "strings"
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
func ReadConfig() *AppConfig {
|
//func ReadConfig() *AppConfig {
|
||||||
if DnsProviderSupports != nil {
|
// if DnsProviderSupports != nil {
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
InitConfig()
|
// InitConfig()
|
||||||
file, err := os.ReadFile(GetEnvConf().ConfFile)
|
// file, err := os.ReadFile(GetEnvConf().ConfFile)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err)
|
// log.Fatal(err)
|
||||||
}
|
// }
|
||||||
var conf *AppConfig
|
// var conf *AppConfig
|
||||||
err = yaml.Unmarshal(file, &conf)
|
// err = yaml.Unmarshal(file, &conf)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err)
|
// log.Fatal(err)
|
||||||
}
|
// }
|
||||||
return conf
|
// return conf
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func InitConfig() {
|
//func InitConfig() {
|
||||||
// 判断当前目录下是否存在配置文件 config.yml
|
// // 判断当前目录下是否存在配置文件 config.yml
|
||||||
_, err := os.Stat(GetEnvConf().ConfFile)
|
// _, err := os.Stat(GetEnvConf().ConfFile)
|
||||||
|
//
|
||||||
if os.IsNotExist(err) {
|
// if os.IsNotExist(err) {
|
||||||
// 配置文件不存在,则创建一个
|
// // 配置文件不存在,则创建一个
|
||||||
log.Infof("配置文件不存在,自动创建默认配置文件")
|
// log.Infof("配置文件不存在,自动创建默认配置文件")
|
||||||
// 生成默认配置
|
// // 生成默认配置
|
||||||
conf := readNewConf()
|
// conf := readNewConf()
|
||||||
// 创建一个默认的配置文件
|
// // 创建一个默认的配置文件
|
||||||
file, err := os.Create(GetEnvConf().ConfFile)
|
// file, err := os.Create(GetEnvConf().ConfFile)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err)
|
// log.Fatal(err)
|
||||||
}
|
// }
|
||||||
data, err := yaml.Marshal(conf)
|
// data, err := yaml.Marshal(conf)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err)
|
// log.Fatal(err)
|
||||||
}
|
// }
|
||||||
// 写入配置文件
|
// // 写入配置文件
|
||||||
_, err = file.Write(data)
|
// _, err = file.Write(data)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err)
|
// log.Fatal(err)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func readNewConf() *AppConfig {
|
//func readNewConf() *AppConfig {
|
||||||
conf := defaultConf()
|
// conf := defaultConf()
|
||||||
log.Println("无配置文件, 生成配置文件")
|
// log.Println("无配置文件, 生成配置文件")
|
||||||
|
//
|
||||||
conf.CertDir = scanConfDefault("请输入证书保存目录; 默认为 cert", "cert")
|
// conf.CertDir = scanConfDefault("请输入证书保存目录; 默认为 cert", "cert")
|
||||||
isGenMsg := "是否需要自动生成用于数据传输加密的RSA密钥对? \n请输入yes(Y)/no(N) 默认为 yes"
|
// isGenMsg := "是否需要自动生成用于数据传输加密的RSA密钥对? \n请输入yes(Y)/no(N) 默认为 yes"
|
||||||
isGenErrMsg := "请输入yes(Y)/no(N)"
|
// isGenErrMsg := "请输入yes(Y)/no(N)"
|
||||||
isGenValues := []string{"yes", "no", "Y", "N"}
|
// isGenValues := []string{"yes", "no", "Y", "N"}
|
||||||
isGenEncrypt := scanConfDefaultCheck(isGenMsg, "yes", isGenValues, isGenErrMsg)
|
// isGenEncrypt := scanConfDefaultCheck(isGenMsg, "yes", isGenValues, isGenErrMsg)
|
||||||
if isGenEncrypt == "no" || isGenEncrypt == "N" {
|
// if isGenEncrypt == "no" || isGenEncrypt == "N" {
|
||||||
conf.Encrypt.PriKey = scanConf("请输入RSA私钥", "请输入RSA私钥")
|
// conf.Encrypt.PriKey = scanConf("请输入RSA私钥", "请输入RSA私钥")
|
||||||
conf.Encrypt.PubKey = scanConf("请输入RSA公钥", "请输入RSA公钥")
|
// conf.Encrypt.PubKey = scanConf("请输入RSA公钥", "请输入RSA公钥")
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
msg := fmt.Sprintf("请输入DNS提供商; 当前支持的: %s", strings.Join(DnsProviderSupports, ","))
|
// msg := fmt.Sprintf("请输入DNS提供商; 当前支持的: %s", strings.Join(DnsProviderSupports, ","))
|
||||||
errMsg := fmt.Sprintf("不支持的DNS提供商; 当前支持的: %s", strings.Join(DnsProviderSupports, ","))
|
// errMsg := fmt.Sprintf("不支持的DNS提供商; 当前支持的: %s", strings.Join(DnsProviderSupports, ","))
|
||||||
conf.Use = scanConfDefaultCheck(msg, "", DnsProviderSupports, errMsg)
|
// conf.Use = scanConfDefaultCheck(msg, "", DnsProviderSupports, errMsg)
|
||||||
switch conf.Use {
|
// switch conf.Use {
|
||||||
case "alidns":
|
// case "alidns":
|
||||||
fmt.Printf("阿里云DNS配置帮助页: \n%s\n", "https://go-acme.github.io/lego/dns/alidns/index.html")
|
// 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("阿里云令牌获取方式:\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")
|
// 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.RegionID = scanConf("请输入阿里云Region ID", "请输入阿里云Region ID")
|
||||||
conf.Provider.Ali.APIKey = scanConf("请输入阿里云API Key", "请输入阿里云API Key")
|
// conf.Provider.Ali.APIKey = scanConf("请输入阿里云API Key", "请输入阿里云API Key")
|
||||||
conf.Provider.Ali.SecretKey = scanConf("请输入阿里云Secret Key", "请输入阿里云Secret Key")
|
// conf.Provider.Ali.SecretKey = scanConf("请输入阿里云Secret Key", "请输入阿里云Secret Key")
|
||||||
case "tencentcloud":
|
// case "tencentcloud":
|
||||||
fmt.Printf("腾讯云DNS配置帮助页: \n%s\n", "https://go-acme.github.io/lego/dns/tencentcloud/index.html")
|
// 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("腾讯云令牌获取方式: \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")
|
// 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.SecretId = scanConf("请输入腾讯云Secret Id", "请输入腾讯云Secret Id")
|
||||||
conf.Provider.Tencent.SecretKey = scanConf("请输入腾讯云Secret Key", "请输入腾讯云Secret Key")
|
// conf.Provider.Tencent.SecretKey = scanConf("请输入腾讯云Secret Key", "请输入腾讯云Secret Key")
|
||||||
case "cloudflare":
|
// case "cloudflare":
|
||||||
fmt.Printf("Cloudflare DNS配置帮助页: \n%s\n", "https://go-acme.github.io/lego/dns/tencentcloud/index.html")
|
// 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 令牌获取方式: \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")
|
// fmt.Printf("Cloudflare SDK客户端项目地址: \n%s\n", "https://github.com/cloudflare/cloudflare-go")
|
||||||
conf.Provider.CloudFlare.Token = scanConf("请输入CloudFlare DNS API Token", "请输入CloudFlare Token")
|
// conf.Provider.CloudFlare.Token = scanConf("请输入CloudFlare DNS API Token", "请输入CloudFlare Token")
|
||||||
}
|
// }
|
||||||
isAddDomainMsg := "是否需要添加证书获取配置?\n您可以在此处通过控制台交互添加;也可以在配置文件创建后,直接修改配置文件.\n请输入yes(Y)/no(N) 默认为 yes"
|
// isAddDomainMsg := "是否需要添加证书获取配置?\n您可以在此处通过控制台交互添加;也可以在配置文件创建后,直接修改配置文件.\n请输入yes(Y)/no(N) 默认为 yes"
|
||||||
isAddDomain := scanConfDefaultCheck(isAddDomainMsg, "yes", isGenValues, isGenErrMsg)
|
// isAddDomain := scanConfDefaultCheck(isAddDomainMsg, "yes", isGenValues, isGenErrMsg)
|
||||||
if isAddDomain == "no" || isAddDomain == "N" {
|
// if isAddDomain == "no" || isAddDomain == "N" {
|
||||||
fmt.Printf("您可以通过手动修改%s文件, 调整证书获取配置.", GetEnvConf().ConfFile)
|
// fmt.Printf("您可以通过手动修改%s文件, 调整证书获取配置.", GetEnvConf().ConfFile)
|
||||||
conf.Domains = []Domain{}
|
// conf.Domains = []Domain{}
|
||||||
return conf
|
// return conf
|
||||||
}
|
// }
|
||||||
conf.Domains = []Domain{}
|
// conf.Domains = []Domain{}
|
||||||
for {
|
// for {
|
||||||
name := scanConf("请输入配置名称", "配置名称不能为空")
|
// name := scanConf("请输入配置名称", "配置名称不能为空")
|
||||||
email := scanConf("请输入邮箱", "邮箱不能为空")
|
// email := scanConf("请输入邮箱", "邮箱不能为空")
|
||||||
host := scanConf("请输入主机名;支持泛解析;\n支持多个域名,多个域名用,(英文逗号)分割;\n如: example.com,*.example.com\n", "主机名不能为空")
|
// host := scanConf("请输入主机名;支持泛解析;\n支持多个域名,多个域名用,(英文逗号)分割;\n如: example.com,*.example.com\n", "主机名不能为空")
|
||||||
// 将host通过,分割为数组
|
// // 将host通过,分割为数组
|
||||||
hosts := strings.Split(host, ",")
|
// hosts := strings.Split(host, ",")
|
||||||
conf.Domains = append(conf.Domains, Domain{
|
// conf.Domains = append(conf.Domains, Domain{
|
||||||
Name: name,
|
// Name: name,
|
||||||
Email: email,
|
// Email: email,
|
||||||
Host: hosts,
|
// Host: hosts,
|
||||||
})
|
// })
|
||||||
isAddNextDomainMsg := "是否需要继续添加证书获取配置?\n请输入yes(Y)/no(N) 默认为 yes"
|
// isAddNextDomainMsg := "是否需要继续添加证书获取配置?\n请输入yes(Y)/no(N) 默认为 yes"
|
||||||
isAddNextDomain := scanConfDefaultCheck(isAddNextDomainMsg, "yes", isGenValues, isGenErrMsg)
|
// isAddNextDomain := scanConfDefaultCheck(isAddNextDomainMsg, "yes", isGenValues, isGenErrMsg)
|
||||||
if isAddNextDomain == "no" || isAddNextDomain == "N" {
|
// if isAddNextDomain == "no" || isAddNextDomain == "N" {
|
||||||
fmt.Printf("您后续可以通过手动修改%s文件, 调整证书获取配置.", GetEnvConf().ConfFile)
|
// fmt.Printf("您后续可以通过手动修改%s文件, 调整证书获取配置.", GetEnvConf().ConfFile)
|
||||||
return conf
|
// return conf
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 读取用户输入
|
//// 读取用户输入
|
||||||
func scanConf(msg string, errMsg string) string {
|
//func scanConf(msg string, errMsg string) string {
|
||||||
for {
|
// for {
|
||||||
log.Println(msg)
|
// log.Println(msg)
|
||||||
reader := bufio.NewReader(os.Stdin)
|
// reader := bufio.NewReader(os.Stdin)
|
||||||
name, err := reader.ReadString('\n')
|
// name, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
fmt.Println("读取失败;", err)
|
// fmt.Println("读取失败;", err)
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
name = strings.Trim(name, "\r\n")
|
// name = strings.Trim(name, "\r\n")
|
||||||
if name == "" {
|
// if name == "" {
|
||||||
fmt.Println(errMsg)
|
// fmt.Println(errMsg)
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
return name
|
// return name
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func scanConfDefault(msg string, defaultContent string) string {
|
//func scanConfDefault(msg string, defaultContent string) string {
|
||||||
for {
|
// for {
|
||||||
log.Println(msg)
|
// log.Println(msg)
|
||||||
reader := bufio.NewReader(os.Stdin)
|
// reader := bufio.NewReader(os.Stdin)
|
||||||
name, err := reader.ReadString('\n')
|
// name, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
fmt.Println("读取失败;", err)
|
// fmt.Println("读取失败;", err)
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
name = strings.Trim(name, "\r\n")
|
// name = strings.Trim(name, "\r\n")
|
||||||
if name == "" {
|
// if name == "" {
|
||||||
return defaultContent
|
// return defaultContent
|
||||||
}
|
// }
|
||||||
return name
|
// return name
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func scanConfDefaultCheck(msg string, defaultContent string, values []string, errMsg string) string {
|
//func scanConfDefaultCheck(msg string, defaultContent string, values []string, errMsg string) string {
|
||||||
for {
|
// for {
|
||||||
content := scanConfDefault(msg, defaultContent)
|
// content := scanConfDefault(msg, defaultContent)
|
||||||
// 判断内容是否在values中
|
// // 判断内容是否在values中
|
||||||
for _, value := range values {
|
// for _, value := range values {
|
||||||
if value == content {
|
// if value == content {
|
||||||
return content
|
// return content
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
log.Println(errMsg)
|
// log.Println(errMsg)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func defaultConf() *AppConfig {
|
//func defaultConf() *AppConfig {
|
||||||
//priKey, pubKey, err := GenRsa()
|
// //priKey, pubKey, err := GenRsa()
|
||||||
priKey, pubKey, err := crypto.GenRSA()
|
// priKey, pubKey, err := crypto.GenRSA()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err)
|
// log.Fatal(err)
|
||||||
}
|
// }
|
||||||
return &AppConfig{
|
// return &AppConfig{
|
||||||
Use: "Ali",
|
// Use: "Ali",
|
||||||
CertDir: "cert",
|
// CertDir: "cert",
|
||||||
Provider: AppProvider{
|
// Provider: AppProvider{
|
||||||
Ali: AliProvider{
|
// Ali: AliProvider{
|
||||||
RegionID: "cn-hangzhou",
|
// RegionID: "cn-hangzhou",
|
||||||
APIKey: "api_key",
|
// APIKey: "api_key",
|
||||||
SecretKey: "secret_key",
|
// SecretKey: "secret_key",
|
||||||
},
|
// },
|
||||||
Tencent: TencentProvider{
|
// Tencent: TencentProvider{
|
||||||
SecretId: "secret_id",
|
// SecretId: "secret_id",
|
||||||
SecretKey: "secret_key",
|
// SecretKey: "secret_key",
|
||||||
},
|
// },
|
||||||
CloudFlare: CloudFlareProvider{
|
// CloudFlare: CloudFlareProvider{
|
||||||
Token: "token",
|
// Token: "token",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
Domains: []Domain{
|
// Domains: []Domain{
|
||||||
{
|
// {
|
||||||
Name: "example.com",
|
// Name: "example.com",
|
||||||
Email: "email@example.com",
|
// Email: "email@example.com",
|
||||||
Host: []string{"www.example.com"},
|
// Host: []string{"www.example.com"},
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
Encrypt: Encrypt{
|
// Encrypt: Encrypt{
|
||||||
PriKey: priKey,
|
// PriKey: priKey,
|
||||||
PubKey: pubKey,
|
// PubKey: pubKey,
|
||||||
},
|
// },
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
type AppConfig struct {
|
//type AppConfig struct {
|
||||||
Use string
|
// Use string
|
||||||
|
//
|
||||||
CertDir string
|
// CertDir string
|
||||||
|
//
|
||||||
Provider AppProvider
|
// Provider AppProvider
|
||||||
|
//
|
||||||
Domains []Domain
|
// Domains []Domain
|
||||||
|
//
|
||||||
Encrypt Encrypt
|
// Encrypt Encrypt
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
type AppProvider struct {
|
//type AppProvider struct {
|
||||||
Ali AliProvider
|
// Ali AliProvider
|
||||||
Tencent TencentProvider
|
// Tencent TencentProvider
|
||||||
CloudFlare CloudFlareProvider
|
// CloudFlare CloudFlareProvider
|
||||||
}
|
//}
|
||||||
type AliProvider struct {
|
//type AliProvider struct {
|
||||||
RegionID string
|
// RegionID string
|
||||||
APIKey string
|
// APIKey string
|
||||||
SecretKey string
|
// SecretKey string
|
||||||
}
|
//}
|
||||||
type TencentProvider struct {
|
//type TencentProvider struct {
|
||||||
SecretId string
|
// SecretId string
|
||||||
SecretKey string
|
// SecretKey string
|
||||||
}
|
//}
|
||||||
type CloudFlareProvider struct {
|
//type CloudFlareProvider struct {
|
||||||
Token string
|
// Token string
|
||||||
}
|
//}
|
||||||
type Domain struct {
|
//type Domain struct {
|
||||||
Name string
|
// Name string
|
||||||
Email string
|
// Email string
|
||||||
Host []string
|
// Host []string
|
||||||
}
|
//}
|
||||||
type Encrypt struct {
|
//type Encrypt struct {
|
||||||
PriKey string
|
// PriKey string
|
||||||
PubKey string
|
// PubKey string
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func (conf AppConfig) FindDomain(name string) *Domain {
|
//func (conf AppConfig) FindDomain(name string) *Domain {
|
||||||
for _, domain := range conf.Domains {
|
// for _, domain := range conf.Domains {
|
||||||
if domain.Name == name {
|
// if domain.Name == name {
|
||||||
return &domain
|
// return &domain
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return nil
|
// return nil
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
const ENV_CONF_FILE = "ACME_MANA_CONF_FILE"
|
//const ENV_CONF_FILE = "ACME_MANA_CONF_FILE"
|
||||||
|
//
|
||||||
func InitRuntimeConf() *EnvConf {
|
//func InitRuntimeConf() *EnvConf {
|
||||||
return &EnvConf{
|
// return &EnvConf{
|
||||||
ConfFile: env.GetOrDefaultString(ENV_CONF_FILE, "config.yml"),
|
// ConfFile: env.GetOrDefaultString(ENV_CONF_FILE, "config.yml"),
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
type EnvConf struct {
|
//type EnvConf struct {
|
||||||
ConfFile string
|
// ConfFile string
|
||||||
}
|
//}
|
||||||
|
|
534
src/daemon.go
534
src/daemon.go
|
@ -1,269 +1,269 @@
|
||||||
package src
|
package src
|
||||||
|
|
||||||
import (
|
//import (
|
||||||
"encoding/json"
|
// "encoding/json"
|
||||||
"log"
|
// "log"
|
||||||
"net"
|
// "net"
|
||||||
"os"
|
// "os"
|
||||||
"os/exec"
|
// "os/exec"
|
||||||
"path"
|
// "path"
|
||||||
"path/filepath"
|
// "path/filepath"
|
||||||
"strconv"
|
// "strconv"
|
||||||
"syscall"
|
// "syscall"
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
var stdout *os.File
|
//var stdout *os.File
|
||||||
var stderr *os.File
|
//var stderr *os.File
|
||||||
|
//
|
||||||
// Start 启动/*
|
//// Start 启动/*
|
||||||
func Start() {
|
//func Start() {
|
||||||
initLog()
|
// initLog()
|
||||||
args := os.Args
|
// args := os.Args
|
||||||
if len(args) <= 1 {
|
// if len(args) <= 1 {
|
||||||
//daemonStart()
|
// //daemonStart()
|
||||||
doTask()
|
// doTask()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
command := args[1]
|
// command := args[1]
|
||||||
switch command {
|
// switch command {
|
||||||
case "help":
|
// case "help":
|
||||||
showHelp()
|
// showHelp()
|
||||||
case "block":
|
// case "block":
|
||||||
doTask()
|
// doTask()
|
||||||
case "start":
|
// case "start":
|
||||||
daemonStart()
|
// daemonStart()
|
||||||
case "stop":
|
// case "stop":
|
||||||
daemonStop()
|
// daemonStop()
|
||||||
case "status":
|
// case "status":
|
||||||
daemonStatus()
|
// daemonStatus()
|
||||||
case "dump":
|
// case "dump":
|
||||||
dumpConfig()
|
// dumpConfig()
|
||||||
case "domains":
|
// case "domains":
|
||||||
showDomains()
|
// showDomains()
|
||||||
case "pubkey":
|
// case "pubkey":
|
||||||
showPubkey()
|
// showPubkey()
|
||||||
case "apply":
|
// case "apply":
|
||||||
applyOnce()
|
// applyOnce()
|
||||||
case "-s":
|
// case "-s":
|
||||||
daemonCommand()
|
// daemonCommand()
|
||||||
default:
|
// default:
|
||||||
log.Fatalf("Unknown command: %s", command)
|
// log.Fatalf("Unknown command: %s", command)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 初始化日志文件
|
//// 初始化日志文件
|
||||||
func initLog() {
|
//func initLog() {
|
||||||
pwd, err := os.Getwd()
|
// pwd, err := os.Getwd()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to get current working directory: %v", err)
|
// log.Fatalf("Failed to get current working directory: %v", err)
|
||||||
}
|
// }
|
||||||
dir := path.Join(pwd, "log")
|
// dir := path.Join(pwd, "log")
|
||||||
_, err = os.Stat(dir)
|
// _, err = os.Stat(dir)
|
||||||
if os.IsNotExist(err) {
|
// if os.IsNotExist(err) {
|
||||||
err := os.Mkdir(dir, 0777)
|
// err := os.Mkdir(dir, 0777)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to create directory: %v", err)
|
// log.Fatalf("Failed to create directory: %v", err)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
outFile, err := os.OpenFile(path.Join(dir, "out.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
// outFile, err := os.OpenFile(path.Join(dir, "out.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
defer outFile.Close()
|
// defer outFile.Close()
|
||||||
stdout = outFile
|
// stdout = outFile
|
||||||
|
//
|
||||||
errFile, err := os.OpenFile(path.Join(dir, "err.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
// errFile, err := os.OpenFile(path.Join(dir, "err.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
defer stderr.Close()
|
// defer stderr.Close()
|
||||||
stderr = errFile
|
// stderr = errFile
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func showHelp() {
|
//func showHelp() {
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 守护进程启动
|
//// 守护进程启动
|
||||||
func daemonStart() {
|
//func daemonStart() {
|
||||||
GetAppConfig()
|
// GetAppConfig()
|
||||||
isDaemon := os.Getenv("GO_DAEMON")
|
// isDaemon := os.Getenv("GO_DAEMON")
|
||||||
log.Println("Run Daemon, DAEMON Is " + isDaemon)
|
// log.Println("Run Daemon, DAEMON Is " + isDaemon)
|
||||||
if isDaemon != "1" {
|
// if isDaemon != "1" {
|
||||||
// 直接启动
|
// // 直接启动
|
||||||
if isRunning() {
|
// if isRunning() {
|
||||||
log.Println("Daemon is already running.")
|
// log.Println("Daemon is already running.")
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
workPath, err := os.Executable()
|
// workPath, err := os.Executable()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to get executable path: %v", err)
|
// log.Fatalf("Failed to get executable path: %v", err)
|
||||||
}
|
// }
|
||||||
cmd := exec.Cmd{
|
// cmd := exec.Cmd{
|
||||||
Path: workPath,
|
// Path: workPath,
|
||||||
Args: os.Args,
|
// Args: os.Args,
|
||||||
Dir: filepath.Dir(workPath),
|
// Dir: filepath.Dir(workPath),
|
||||||
Env: append(os.Environ(), "GO_DAEMON=1"),
|
// Env: append(os.Environ(), "GO_DAEMON=1"),
|
||||||
//Stdin: os.Stdin,
|
// //Stdin: os.Stdin,
|
||||||
Stdout: stdout,
|
// Stdout: stdout,
|
||||||
Stderr: stderr,
|
// Stderr: stderr,
|
||||||
//Stdin: os.Stdin,
|
// //Stdin: os.Stdin,
|
||||||
//Stdout: os.Stdout,
|
// //Stdout: os.Stdout,
|
||||||
//Stderr: os.Stderr,
|
// //Stderr: os.Stderr,
|
||||||
SysProcAttr: &syscall.SysProcAttr{},
|
// SysProcAttr: &syscall.SysProcAttr{},
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
log.Println("Starting daemon...")
|
// log.Println("Starting daemon...")
|
||||||
err = cmd.Start()
|
// err = cmd.Start()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to start daemon: %v", err)
|
// log.Fatalf("Failed to start daemon: %v", err)
|
||||||
}
|
// }
|
||||||
err = os.WriteFile(PidFile, []byte(strconv.Itoa(cmd.Process.Pid)), 0644)
|
// err = os.WriteFile(PidFile, []byte(strconv.Itoa(cmd.Process.Pid)), 0644)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to write PID file: %v", err)
|
// log.Fatalf("Failed to write PID file: %v", err)
|
||||||
}
|
// }
|
||||||
log.Printf("Daemon started with PID: %d", cmd.Process.Pid)
|
// log.Printf("Daemon started with PID: %d", cmd.Process.Pid)
|
||||||
os.Exit(0)
|
// os.Exit(0)
|
||||||
|
//
|
||||||
} else {
|
// } else {
|
||||||
// 子进程
|
// // 子进程
|
||||||
doTask()
|
// doTask()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 守护进程停止
|
//// 守护进程停止
|
||||||
func daemonStop() {
|
//func daemonStop() {
|
||||||
sendCommand("stop")
|
// sendCommand("stop")
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 守护进程状态
|
//// 守护进程状态
|
||||||
func daemonStatus() {
|
//func daemonStatus() {
|
||||||
if isRunning() {
|
// if isRunning() {
|
||||||
log.Println("Daemon is running.")
|
// log.Println("Daemon is running.")
|
||||||
} else {
|
// } else {
|
||||||
log.Println("Daemon is not running.")
|
// log.Println("Daemon is not running.")
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 输出配置文件
|
//// 输出配置文件
|
||||||
func dumpConfig() {
|
//func dumpConfig() {
|
||||||
//config, err := json.Marshal(GetAppConfig())
|
// //config, err := json.Marshal(GetAppConfig())
|
||||||
config, err := json.MarshalIndent(GetAppConfig(), "", " ")
|
// config, err := json.MarshalIndent(GetAppConfig(), "", " ")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to marshal config: %v", err)
|
// log.Fatalf("Failed to marshal config: %v", err)
|
||||||
}
|
// }
|
||||||
log.Println(string(config))
|
// log.Println(string(config))
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 手动执行一次证书申请
|
//// 手动执行一次证书申请
|
||||||
func applyOnce() {
|
//func applyOnce() {
|
||||||
if len(os.Args) < 3 {
|
// if len(os.Args) < 3 {
|
||||||
log.Fatalf("Please enter domain name!")
|
// log.Fatalf("Please enter domain name!")
|
||||||
}
|
// }
|
||||||
name := os.Args[2]
|
// name := os.Args[2]
|
||||||
if name == "" {
|
// if name == "" {
|
||||||
log.Fatalf("No domain specified!")
|
// log.Fatalf("No domain specified!")
|
||||||
}
|
// }
|
||||||
domain := GetAppConfig().FindDomain(name)
|
// domain := GetAppConfig().FindDomain(name)
|
||||||
if domain == nil {
|
// if domain == nil {
|
||||||
log.Fatalf("Domain not found: %s", name)
|
// log.Fatalf("Domain not found: %s", name)
|
||||||
}
|
// }
|
||||||
Apply(*domain)
|
// Apply(*domain)
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 输出域名列表
|
//// 输出域名列表
|
||||||
func showDomains() {
|
//func showDomains() {
|
||||||
domains := GetAppConfig().Domains
|
// domains := GetAppConfig().Domains
|
||||||
// 格式化为json并打印
|
// // 格式化为json并打印
|
||||||
config, err := json.MarshalIndent(domains, "", " ")
|
// config, err := json.MarshalIndent(domains, "", " ")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to marshal config: %v", err)
|
// log.Fatalf("Failed to marshal config: %v", err)
|
||||||
}
|
// }
|
||||||
log.Println(string(config))
|
// log.Println(string(config))
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 输出公钥
|
//// 输出公钥
|
||||||
func showPubkey() {
|
//func showPubkey() {
|
||||||
key := GetAppConfig().Encrypt.PubKey
|
// key := GetAppConfig().Encrypt.PubKey
|
||||||
log.Println(key)
|
// log.Println(key)
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 守护进程接收命令
|
//// 守护进程接收命令
|
||||||
func daemonCommand() {
|
//func daemonCommand() {
|
||||||
log.Println("Sending command...")
|
// log.Println("Sending command...")
|
||||||
command := os.Args[2]
|
// command := os.Args[2]
|
||||||
sendCommand(command)
|
// sendCommand(command)
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 发送命令
|
//// 发送命令
|
||||||
func sendCommand(command string) {
|
//func sendCommand(command string) {
|
||||||
conn, err := net.Dial("unix", SocketFile)
|
// conn, err := net.Dial("unix", SocketFile)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to connect to daemon: %v", err)
|
// log.Fatalf("Failed to connect to daemon: %v", err)
|
||||||
}
|
// }
|
||||||
defer func(conn net.Conn) {
|
// defer func(conn net.Conn) {
|
||||||
err := conn.Close()
|
// err := conn.Close()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to close connection: %v", err)
|
// log.Fatalf("Failed to close connection: %v", err)
|
||||||
}
|
// }
|
||||||
}(conn)
|
// }(conn)
|
||||||
|
//
|
||||||
_, err = conn.Write([]byte(command))
|
// _, err = conn.Write([]byte(command))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatalf("Failed to send command: %v", err)
|
// log.Fatalf("Failed to send command: %v", err)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
log.Printf("Sending command '%s' to daemon with PID: %d", command, 0)
|
// log.Printf("Sending command '%s' to daemon with PID: %d", command, 0)
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 业务进程执行任务
|
//// 业务进程执行任务
|
||||||
func doTask() {
|
//func doTask() {
|
||||||
|
//
|
||||||
// 监听主进程下发的指令
|
// // 监听主进程下发的指令
|
||||||
go InitSocket()
|
// go InitSocket()
|
||||||
|
//
|
||||||
// 监听HTTP请求
|
// // 监听HTTP请求
|
||||||
go InitHttpServer("0.0.0.0", 10000)
|
// go InitHttpServer("0.0.0.0", 10000)
|
||||||
|
//
|
||||||
// 自动执行域名证书更新
|
// // 自动执行域名证书更新
|
||||||
go AutoRefreshCert()
|
// go AutoRefreshCert()
|
||||||
|
//
|
||||||
// 阻止退出
|
// // 阻止退出
|
||||||
select {}
|
// select {}
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 是否已启动
|
//// 是否已启动
|
||||||
func isRunning() bool {
|
//func isRunning() bool {
|
||||||
log.Println("Checking if daemon is running...")
|
// log.Println("Checking if daemon is running...")
|
||||||
pid, err := readPID()
|
// pid, err := readPID()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
process, err := os.FindProcess(pid)
|
// process, err := os.FindProcess(pid)
|
||||||
log.Println("Found process:", process)
|
// log.Println("Found process:", process)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Println("Failed to find process:", err)
|
// log.Println("Failed to find process:", err)
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
return true
|
// return true
|
||||||
//err = process.Signal(syscall.Signal(0))
|
// //err = process.Signal(syscall.Signal(0))
|
||||||
//log.Println("Signal result:", err)
|
// //log.Println("Signal result:", err)
|
||||||
//return err == nil
|
// //return err == nil
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
// 读取PID文件
|
//// 读取PID文件
|
||||||
func readPID() (int, error) {
|
//func readPID() (int, error) {
|
||||||
log.Println("Reading PID file...")
|
// log.Println("Reading PID file...")
|
||||||
data, err := os.ReadFile(PidFile)
|
// data, err := os.ReadFile(PidFile)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Println("Failed to read PID file:", err)
|
// log.Println("Failed to read PID file:", err)
|
||||||
return 0, err
|
// return 0, err
|
||||||
}
|
// }
|
||||||
log.Println("PID file content:", string(data))
|
// log.Println("PID file content:", string(data))
|
||||||
|
//
|
||||||
pid, err := strconv.Atoi(string(data))
|
// pid, err := strconv.Atoi(string(data))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Println("Failed to parse PID:", err)
|
// log.Println("Failed to parse PID:", err)
|
||||||
return 0, err
|
// return 0, err
|
||||||
}
|
// }
|
||||||
log.Println("PID:", pid)
|
// log.Println("PID:", pid)
|
||||||
|
//
|
||||||
return pid, nil
|
// return pid, nil
|
||||||
}
|
//}
|
||||||
|
|
286
src/http.go
286
src/http.go
|
@ -1,145 +1,145 @@
|
||||||
package src
|
package src
|
||||||
|
|
||||||
import (
|
//import (
|
||||||
"acme-mana/src/crypto"
|
// "acme-mana/src/crypto"
|
||||||
"encoding/base64"
|
// "encoding/base64"
|
||||||
"encoding/hex"
|
// "encoding/hex"
|
||||||
"encoding/json"
|
// "encoding/json"
|
||||||
"github.com/gin-gonic/gin"
|
// "github.com/gin-gonic/gin"
|
||||||
"log"
|
// "log"
|
||||||
"os"
|
// "os"
|
||||||
"path"
|
// "path"
|
||||||
"path/filepath"
|
// "path/filepath"
|
||||||
"strconv"
|
// "strconv"
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
func InitHttpServer(host string, port int) {
|
//func InitHttpServer(host string, port int) {
|
||||||
|
//
|
||||||
log.Println("Start http server, Listen " + strconv.Itoa(port))
|
// log.Println("Start http server, Listen " + strconv.Itoa(port))
|
||||||
h := gin.Default()
|
// h := gin.Default()
|
||||||
h.GET("/api/v1/refresh", refreshCert)
|
// h.GET("/api/v1/refresh", refreshCert)
|
||||||
h.GET("/api/v1/cert", getCert)
|
// h.GET("/api/v1/cert", getCert)
|
||||||
h.GET("/api/v1/domain/list", domainList)
|
// h.GET("/api/v1/domain/list", domainList)
|
||||||
err := h.Run(host + ":" + strconv.Itoa(port))
|
// err := h.Run(host + ":" + strconv.Itoa(port))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func domainList(c *gin.Context) {
|
//func domainList(c *gin.Context) {
|
||||||
token := getToken(c)
|
// token := getToken(c)
|
||||||
domains := GetAppConfig().Domains
|
// domains := GetAppConfig().Domains
|
||||||
data, err := json.Marshal(domains)
|
// data, err := json.Marshal(domains)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err)
|
// log.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
encryptData := encryptResult(string(data), token)
|
// encryptData := encryptResult(string(data), token)
|
||||||
c.JSON(200, gin.H{
|
// c.JSON(200, gin.H{
|
||||||
"code": 200,
|
// "code": 200,
|
||||||
"msg": "success",
|
// "msg": "success",
|
||||||
"data": encryptData,
|
// "data": encryptData,
|
||||||
})
|
// })
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func getCert(c *gin.Context) {
|
//func getCert(c *gin.Context) {
|
||||||
name := c.Query("name")
|
// name := c.Query("name")
|
||||||
token := getToken(c)
|
// token := getToken(c)
|
||||||
|
//
|
||||||
dir := GetAppConfig().CertDir
|
// dir := GetAppConfig().CertDir
|
||||||
dir = filepath.Join(dir, name)
|
// dir = filepath.Join(dir, name)
|
||||||
_, err := os.Stat(dir)
|
// _, err := os.Stat(dir)
|
||||||
if os.IsNotExist(err) {
|
// if os.IsNotExist(err) {
|
||||||
c.JSON(200, gin.H{
|
// c.JSON(200, gin.H{
|
||||||
"code": 500,
|
// "code": 500,
|
||||||
"msg": "Name does not exist.",
|
// "msg": "Name does not exist.",
|
||||||
})
|
// })
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
crtFilePath := path.Join(dir, CertFileName)
|
// crtFilePath := path.Join(dir, CertFileName)
|
||||||
crtContent, err := os.ReadFile(crtFilePath)
|
// crtContent, err := os.ReadFile(crtFilePath)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
c.JSON(200, gin.H{
|
// c.JSON(200, gin.H{
|
||||||
"code": 500,
|
// "code": 500,
|
||||||
"msg": "Failed to read crt file.",
|
// "msg": "Failed to read crt file.",
|
||||||
})
|
// })
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
crt := string(crtContent)
|
// crt := string(crtContent)
|
||||||
|
//
|
||||||
keyFilePath := path.Join(dir, KeyFileName)
|
// keyFilePath := path.Join(dir, KeyFileName)
|
||||||
keyContent, err := os.ReadFile(keyFilePath)
|
// keyContent, err := os.ReadFile(keyFilePath)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
c.JSON(200, gin.H{
|
// c.JSON(200, gin.H{
|
||||||
"code": 500,
|
// "code": 500,
|
||||||
"msg": "Failed to read key file.",
|
// "msg": "Failed to read key file.",
|
||||||
})
|
// })
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
key := string(keyContent)
|
// key := string(keyContent)
|
||||||
|
//
|
||||||
certInfoFilePath := path.Join(dir, CertInfoFileName)
|
// certInfoFilePath := path.Join(dir, CertInfoFileName)
|
||||||
certInfoContent, err := os.ReadFile(certInfoFilePath)
|
// certInfoContent, err := os.ReadFile(certInfoFilePath)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
c.JSON(200, gin.H{
|
// c.JSON(200, gin.H{
|
||||||
"code": 500,
|
// "code": 500,
|
||||||
"msg": "Failed to read cert info file.",
|
// "msg": "Failed to read cert info file.",
|
||||||
})
|
// })
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
certInfo := string(certInfoContent)
|
// certInfo := string(certInfoContent)
|
||||||
|
//
|
||||||
data, err := json.Marshal(&DomainData{
|
// data, err := json.Marshal(&DomainData{
|
||||||
Fullchain: crt,
|
// Fullchain: crt,
|
||||||
Key: key,
|
// Key: key,
|
||||||
Info: certInfo,
|
// Info: certInfo,
|
||||||
})
|
// })
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err)
|
// log.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
encryptData := encryptResult(string(data), token)
|
// encryptData := encryptResult(string(data), token)
|
||||||
c.JSON(200, gin.H{
|
// c.JSON(200, gin.H{
|
||||||
"code": 200,
|
// "code": 200,
|
||||||
"msg": "Success",
|
// "msg": "Success",
|
||||||
"data": encryptData,
|
// "data": encryptData,
|
||||||
})
|
// })
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func refreshCert(c *gin.Context) {
|
//func refreshCert(c *gin.Context) {
|
||||||
name := c.Param("name")
|
// name := c.Param("name")
|
||||||
domain := GetAppConfig().FindDomain(name)
|
// domain := GetAppConfig().FindDomain(name)
|
||||||
if domain == nil {
|
// if domain == nil {
|
||||||
c.JSON(200, gin.H{
|
// c.JSON(200, gin.H{
|
||||||
"code": 500,
|
// "code": 500,
|
||||||
"msg": "Name does not exist.",
|
// "msg": "Name does not exist.",
|
||||||
})
|
// })
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
Apply(*domain)
|
// Apply(*domain)
|
||||||
c.JSON(200, gin.H{
|
// c.JSON(200, gin.H{
|
||||||
"code": 200,
|
// "code": 200,
|
||||||
"msg": "Success",
|
// "msg": "Success",
|
||||||
})
|
// })
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func getToken(c *gin.Context) (token string) {
|
//func getToken(c *gin.Context) (token string) {
|
||||||
token = c.Query("token")
|
// token = c.Query("token")
|
||||||
token = decryptParam(token)
|
// token = decryptParam(token)
|
||||||
return
|
// return
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func decryptParam(param string) string {
|
//func decryptParam(param string) string {
|
||||||
priKey := GetAppConfig().Encrypt.PriKey
|
// priKey := GetAppConfig().Encrypt.PriKey
|
||||||
tokenBytes, err := hex.DecodeString(param)
|
// tokenBytes, err := hex.DecodeString(param)
|
||||||
tokenPlain, err := crypto.DecryptRSABase64(priKey, tokenBytes)
|
// tokenPlain, err := crypto.DecryptRSABase64(priKey, tokenBytes)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Fatal(err)
|
// log.Fatal(err)
|
||||||
}
|
// }
|
||||||
return string(tokenPlain)
|
// return string(tokenPlain)
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func encryptResult(content string, token string) string {
|
//func encryptResult(content string, token string) string {
|
||||||
result := crypto.EncryptAES([]byte(token), []byte(content))
|
// result := crypto.EncryptAES([]byte(token), []byte(content))
|
||||||
return base64.StdEncoding.EncodeToString(result)
|
// return base64.StdEncoding.EncodeToString(result)
|
||||||
}
|
//}
|
||||||
|
|
|
@ -1,18 +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,42 +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() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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,34 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
type AppConfig struct {
|
|
||||||
Server *ServerConf `json:"server" yaml:"server"`
|
|
||||||
Task *TaskConf `json:"task" yaml:"task"`
|
|
||||||
Certs *[]CertConf `json:"cert" yaml:"cert"`
|
|
||||||
Providers *[]ProviderConf `json:"provider" yaml:"provider"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerConf struct {
|
|
||||||
Host string `json:"host" yaml:"host"`
|
|
||||||
Port int `json:"port" yaml:"port"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TaskConf struct {
|
|
||||||
// 启动延迟时间, 单位: 毫秒, 默认: 0
|
|
||||||
Delay int `json:"delay" yaml:"delay"`
|
|
||||||
// 间隔时间
|
|
||||||
Interval int `json:"interval" yaml:"interval"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProviderConf struct {
|
|
||||||
Name string `json:"name" yaml:"name"`
|
|
||||||
Type string `json:"type" yaml:"type"`
|
|
||||||
Conf map[string]string `json:"conf" yaml:"conf"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type CertConf struct {
|
|
||||||
Name string `json:"name" yaml:"name"`
|
|
||||||
Provider string `json:"use" yaml:"provider"`
|
|
||||||
Dir string `json:"dir" yaml:"dir"`
|
|
||||||
Email string `json:"email" yaml:"email"`
|
|
||||||
Host []string `json:"host" yaml:"host"`
|
|
||||||
}
|
|
|
@ -1,6 +1,8 @@
|
||||||
package src
|
package src
|
||||||
|
|
||||||
import "acme-mana/src/cmd"
|
import (
|
||||||
|
"acme-mana/src/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
func StartProgram() {
|
func StartProgram() {
|
||||||
_, err := cmd.InitCmd()
|
_, err := cmd.InitCmd()
|
||||||
|
@ -8,4 +10,5 @@ func StartProgram() {
|
||||||
panic(err)
|
panic(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
79
src/server/http-server.go
Normal file
79
src/server/http-server.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"acme-mana/src/conf"
|
||||||
|
"acme-mana/src/server/http_handler"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HttpServer struct {
|
||||||
|
server *http.Server
|
||||||
|
status bool
|
||||||
|
engine *gin.Engine
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HttpServer) Init() {
|
||||||
|
config := conf.Config()
|
||||||
|
var serverConf = config.Server
|
||||||
|
s.initServer(serverConf.Host, serverConf.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initServer 初始化
|
||||||
|
func (s *HttpServer) initServer(host string, port int) {
|
||||||
|
s.engine = gin.Default()
|
||||||
|
s.register()
|
||||||
|
s.status = false
|
||||||
|
|
||||||
|
s.server = &http.Server{
|
||||||
|
Addr: host + ":" + strconv.Itoa(port),
|
||||||
|
Handler: s.engine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HttpServer) Start() {
|
||||||
|
if s.status {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := s.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
log.Printf("listen: %s\n\n", err)
|
||||||
|
s.status = true
|
||||||
|
} else {
|
||||||
|
s.status = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HttpServer) Stop() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
err := HttpInstance.server.Shutdown(ctx)
|
||||||
|
s.status = false
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("HttpInstance Shutdown:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HttpServer) Status() bool {
|
||||||
|
return s.status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HttpServer) register() {
|
||||||
|
service := s.engine
|
||||||
|
service.Use(gin.Logger())
|
||||||
|
service.Use(http_handler.GlobalErrorHandler())
|
||||||
|
|
||||||
|
certHandler := http_handler.CertHandlerInstance
|
||||||
|
certGroup := service.Group("/api/v1/cert", http_handler.AuthMiddleware())
|
||||||
|
certGroup.GET("/", certHandler.Get)
|
||||||
|
|
||||||
|
confHandler := http_handler.ConfHandlerInstance
|
||||||
|
confGroup := service.Group("/api/v1", http_handler.AuthMiddleware())
|
||||||
|
confGroup.GET("/conf", confHandler.Get)
|
||||||
|
}
|
149
src/server/http-server_test.go
Normal file
149
src/server/http-server_test.go
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHttpServer_InitServer(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
server *http.Server
|
||||||
|
status bool
|
||||||
|
engine *gin.Engine
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
}{
|
||||||
|
// TODO: Add test cases.
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := &HttpServer{
|
||||||
|
server: tt.fields.server,
|
||||||
|
status: tt.fields.status,
|
||||||
|
engine: tt.fields.engine,
|
||||||
|
}
|
||||||
|
s.Init()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHttpServer_Start(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
server *http.Server
|
||||||
|
status bool
|
||||||
|
engine *gin.Engine
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
}{
|
||||||
|
// TODO: Add test cases.
|
||||||
|
{
|
||||||
|
name: "0.0.0.0",
|
||||||
|
fields: fields{
|
||||||
|
server: nil,
|
||||||
|
status: false,
|
||||||
|
engine: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := &HttpServer{
|
||||||
|
server: tt.fields.server,
|
||||||
|
status: tt.fields.status,
|
||||||
|
engine: tt.fields.engine,
|
||||||
|
}
|
||||||
|
s.initServer("0.0.0.0", 35541)
|
||||||
|
s.Start()
|
||||||
|
quit := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
<-quit
|
||||||
|
fmt.Println("Shutting down server...")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHttpServer_Stop(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
server *http.Server
|
||||||
|
status bool
|
||||||
|
engine *gin.Engine
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
}{
|
||||||
|
// TODO: Add test cases.
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := &HttpServer{
|
||||||
|
server: tt.fields.server,
|
||||||
|
status: tt.fields.status,
|
||||||
|
engine: tt.fields.engine,
|
||||||
|
}
|
||||||
|
s.Stop()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHttpServer_register(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
server *http.Server
|
||||||
|
status bool
|
||||||
|
engine *gin.Engine
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
}{
|
||||||
|
// TODO: Add test cases.
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := &HttpServer{
|
||||||
|
server: tt.fields.server,
|
||||||
|
status: tt.fields.status,
|
||||||
|
engine: tt.fields.engine,
|
||||||
|
}
|
||||||
|
s.register()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHttpServer_initServer(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
server *http.Server
|
||||||
|
status bool
|
||||||
|
engine *gin.Engine
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
host string
|
||||||
|
port int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
}{
|
||||||
|
// TODO: Add test cases.
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
s := &HttpServer{
|
||||||
|
server: tt.fields.server,
|
||||||
|
status: tt.fields.status,
|
||||||
|
engine: tt.fields.engine,
|
||||||
|
}
|
||||||
|
s.initServer(tt.args.host, tt.args.port)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
13
src/server/http_handler/auth.go
Normal file
13
src/server/http_handler/auth.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package http_handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AuthMiddleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
|
||||||
|
// 进行身份认证
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
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())
|
||||||
|
}
|
17
src/server/http_handler/conf.go
Normal file
17
src/server/http_handler/conf.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package http_handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"acme-mana/src/conf"
|
||||||
|
"acme-mana/src/server/model"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ConfHandlerInstance = &ConfHandler{}
|
||||||
|
|
||||||
|
type ConfHandler struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ConfHandler) Get(c *gin.Context) {
|
||||||
|
config := conf.Config()
|
||||||
|
c.JSON(200, model.SuccessD(config))
|
||||||
|
}
|
34
src/server/http_handler/error.go
Normal file
34
src/server/http_handler/error.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package http_handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"acme-mana/src/server/model"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GlobalErrorHandler 是一个全局错误处理器中间件
|
||||||
|
func GlobalErrorHandler() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 继续执行后续中间件和处理函数
|
||||||
|
c.Next()
|
||||||
|
|
||||||
|
// 获取上下文中的错误
|
||||||
|
if err, ok := c.Get("error"); ok {
|
||||||
|
switch typedErr := err.(type) {
|
||||||
|
case *model.AppError:
|
||||||
|
c.JSON(200, &model.Result{
|
||||||
|
Code: typedErr.Code,
|
||||||
|
Message: typedErr.Message,
|
||||||
|
Data: nil,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{
|
||||||
|
"error": "Internal Server Error",
|
||||||
|
"success": false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
src/server/model/app-error.go
Normal file
19
src/server/model/app-error.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type AppError struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error 实现 error 接口
|
||||||
|
func (e *AppError) Error() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAppError 创建一个新的 AppError
|
||||||
|
func NewAppError(code int, message string) *AppError {
|
||||||
|
return &AppError{
|
||||||
|
Code: code,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
}
|
27
src/server/model/result.go
Normal file
27
src/server/model/result.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data any `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Result) SetData(data any) Result {
|
||||||
|
r.Data = data
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func Success() *Result {
|
||||||
|
return &Result{
|
||||||
|
Code: 200,
|
||||||
|
Message: "success",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SuccessD(data any) *Result {
|
||||||
|
return &Result{
|
||||||
|
Code: 200,
|
||||||
|
Message: "success",
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
}
|
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)
|
||||||
|
}
|
4
src/server/variable.go
Normal file
4
src/server/variable.go
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
var HttpInstance *HttpServer
|
||||||
|
var SocketInstance *SocketServer
|
168
src/task.go
168
src/task.go
|
@ -1,86 +1,86 @@
|
||||||
package src
|
package src
|
||||||
|
|
||||||
import (
|
//import (
|
||||||
"encoding/json"
|
// "encoding/json"
|
||||||
"log"
|
// "log"
|
||||||
"os"
|
// "os"
|
||||||
"path"
|
// "path"
|
||||||
"time"
|
// "time"
|
||||||
)
|
//)
|
||||||
|
//
|
||||||
var AutoRefreshCertTicker = time.NewTicker(time.Hour)
|
//var AutoRefreshCertTicker = time.NewTicker(time.Hour)
|
||||||
|
//
|
||||||
func AutoRefreshCert() {
|
//func AutoRefreshCert() {
|
||||||
log.Println("Start auto refresh cert")
|
// log.Println("Start auto refresh cert")
|
||||||
defer AutoRefreshCertTicker.Stop()
|
// defer AutoRefreshCertTicker.Stop()
|
||||||
for {
|
// for {
|
||||||
select {
|
// select {
|
||||||
case <-AutoRefreshCertTicker.C:
|
// case <-AutoRefreshCertTicker.C:
|
||||||
doRefreshCert()
|
// doRefreshCert()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func doRefreshCert() {
|
//func doRefreshCert() {
|
||||||
domains := GetAppConfig().Domains
|
// domains := GetAppConfig().Domains
|
||||||
for _, domain := range domains {
|
// for _, domain := range domains {
|
||||||
doRefreshCertOnce(domain)
|
// doRefreshCertOnce(domain)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func doRefreshCertOnce(domain Domain) {
|
//func doRefreshCertOnce(domain Domain) {
|
||||||
name := domain.Name
|
// name := domain.Name
|
||||||
dir := GetAppConfig().CertDir
|
// dir := GetAppConfig().CertDir
|
||||||
certDir := path.Join(dir, name)
|
// certDir := path.Join(dir, name)
|
||||||
if !ValidExist(certDir, domain) {
|
// if !ValidExist(certDir, domain) {
|
||||||
Apply(domain)
|
// Apply(domain)
|
||||||
}
|
// }
|
||||||
infoFile := path.Join(certDir, CertInfoFileName)
|
// infoFile := path.Join(certDir, CertInfoFileName)
|
||||||
certInfo := ParseCertInfo(infoFile, domain)
|
// certInfo := ParseCertInfo(infoFile, domain)
|
||||||
log.Println("Checking if the certificate is expired, Domain: {}", name)
|
// log.Println("Checking if the certificate is expired, Domain: {}", name)
|
||||||
if certInfo.Info.NotAfter.Sub(time.Now()) < 14*24*time.Hour {
|
// 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)
|
// log.Println("Apply for a certificate that is about to expire, domain name:", name)
|
||||||
Apply(domain)
|
// Apply(domain)
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func ValidExist(certDir string, domain Domain) bool {
|
//func ValidExist(certDir string, domain Domain) bool {
|
||||||
_, err := os.Stat(certDir)
|
// _, err := os.Stat(certDir)
|
||||||
if os.IsNotExist(err) {
|
// if os.IsNotExist(err) {
|
||||||
log.Printf("Applying for a certificate, Domain: %s certificate directory does not exist!", domain.Name)
|
// log.Printf("Applying for a certificate, Domain: %s certificate directory does not exist!", domain.Name)
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
if !ExistFile(certDir, CertFileName) {
|
// if !ExistFile(certDir, CertFileName) {
|
||||||
log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, CertFileName)
|
// log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, CertFileName)
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
if !ExistFile(certDir, KeyFileName) {
|
// if !ExistFile(certDir, KeyFileName) {
|
||||||
log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, KeyFileName)
|
// log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, KeyFileName)
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
if !ExistFile(certDir, CertInfoFileName) {
|
// if !ExistFile(certDir, CertInfoFileName) {
|
||||||
log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, CertInfoFileName)
|
// log.Printf("Applying for a certificate, Domain: %s %s does not exist!", domain.Name, CertInfoFileName)
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
return true
|
// return true
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func ParseCertInfo(infoFile string, domain Domain) CertInfo {
|
//func ParseCertInfo(infoFile string, domain Domain) CertInfo {
|
||||||
infoBytes, err := os.ReadFile(infoFile)
|
// infoBytes, err := os.ReadFile(infoFile)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Println("Failed to read cert info file, Domain: {}", domain.Name)
|
// log.Println("Failed to read cert info file, Domain: {}", domain.Name)
|
||||||
}
|
// }
|
||||||
var certInfo CertInfo
|
// var certInfo CertInfo
|
||||||
err = json.Unmarshal(infoBytes, &certInfo)
|
// err = json.Unmarshal(infoBytes, &certInfo)
|
||||||
//if err != nil {
|
// //if err != nil {
|
||||||
// log.Println("Failed to parse cert info file, Domain: {}", domain.Name)
|
// // log.Println("Failed to parse cert info file, Domain: {}", domain.Name)
|
||||||
//}
|
// //}
|
||||||
return certInfo
|
// return certInfo
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
func ExistFile(dir string, fileName string) bool {
|
//func ExistFile(dir string, fileName string) bool {
|
||||||
f := path.Join(dir, fileName)
|
// f := path.Join(dir, fileName)
|
||||||
_, err := os.Stat(f)
|
// _, err := os.Stat(f)
|
||||||
return !os.IsNotExist(err)
|
// 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)
|
||||||
|
|
||||||
|
}
|
17
src/util/random.go
Normal file
17
src/util/random.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
||||||
|
|
||||||
|
func RandomStr(size int) string {
|
||||||
|
rand.NewSource(time.Now().UnixNano())
|
||||||
|
b := make([]byte, size)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
|
@ -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