Compare commits

..

No commits in common. "8d063e13301ecfdef6e14f81480989c1caac975c" and "55d241b873b2e478fc4322e74aef51fe45c4cb67" have entirely different histories.

38 changed files with 1312 additions and 2245 deletions

174
src/acme-client.go Normal file
View File

@ -0,0 +1,174 @@
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
}

View File

@ -1,238 +0,0 @@
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
}

View File

@ -1,7 +1,6 @@
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"
@ -32,8 +31,7 @@ func InitCmd() (*cobra.Command, error) {
//rootCmd.GenPowerShellCompletion(os.Stdout) //rootCmd.GenPowerShellCompletion(os.Stdout)
//rootCmd.GenZshCompletion(os.Stdout) //rootCmd.GenZshCompletion(os.Stdout)
conf.LoadAppConfig() conf.InitAppConfig()
err := rootCmd.Execute() err := rootCmd.Execute()
return rootCmd, err return rootCmd, err
@ -53,7 +51,7 @@ func initConfCmd() *cobra.Command {
Short: "查看配置列表", Short: "查看配置列表",
Long: "查看系统配置列表", Long: "查看系统配置列表",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.ConfShow(cmd, args) confShow(cmd, args)
}, },
} }
confCmd.AddCommand(confShow) confCmd.AddCommand(confShow)
@ -63,13 +61,10 @@ 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: "acme-mana服务端相关命令, 配置服务相关参数, 如监听端口,监听地址等", Long: "配置服务相关参数, 如监听端口,监听地址等",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := cmd.Help() cmd.Help()
if err != nil {
return
}
}, },
} }
editCmd := &cobra.Command{ editCmd := &cobra.Command{
@ -77,7 +72,7 @@ func initServerCmd() *cobra.Command {
Short: "编辑服务配置", Short: "编辑服务配置",
Long: "编辑服务配置", Long: "编辑服务配置",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.ServerEdit(cmd, args) editServer(cmd, args)
}, },
} }
stateCmd := &cobra.Command{ stateCmd := &cobra.Command{
@ -85,7 +80,7 @@ func initServerCmd() *cobra.Command {
Short: "查看服务状态", Short: "查看服务状态",
Long: "查看服务状态", Long: "查看服务状态",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.ServerState(cmd, args) serverState(cmd, args)
}, },
} }
startCmd := &cobra.Command{ startCmd := &cobra.Command{
@ -93,7 +88,7 @@ func initServerCmd() *cobra.Command {
Short: "启动服务", Short: "启动服务",
Long: "启动服务", Long: "启动服务",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.ServerStart(cmd, args) startServer(cmd, args)
}, },
} }
stopCmd := &cobra.Command{ stopCmd := &cobra.Command{
@ -101,7 +96,7 @@ func initServerCmd() *cobra.Command {
Short: "停止服务", Short: "停止服务",
Long: "停止服务", Long: "停止服务",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.ServerStop(cmd, args) stopServer(cmd, args)
}, },
} }
@ -126,7 +121,7 @@ func initTaskCmd() *cobra.Command {
Short: "编辑定时任务配置", Short: "编辑定时任务配置",
Long: "编辑定时任务配置", Long: "编辑定时任务配置",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.TaskEdit(cmd, args) editTask(cmd, args)
}, },
} }
startTask := &cobra.Command{ startTask := &cobra.Command{
@ -134,7 +129,7 @@ func initTaskCmd() *cobra.Command {
Short: "启动定时任务", Short: "启动定时任务",
Long: "启动定时任务", Long: "启动定时任务",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.TaskStart(cmd, args) startTask(cmd, args)
}, },
} }
stopTask := &cobra.Command{ stopTask := &cobra.Command{
@ -142,7 +137,7 @@ func initTaskCmd() *cobra.Command {
Short: "停止定时任务", Short: "停止定时任务",
Long: "停止定时任务", Long: "停止定时任务",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.TaskStop(cmd, args) stopTask(cmd, args)
}, },
} }
statusTask := &cobra.Command{ statusTask := &cobra.Command{
@ -150,7 +145,7 @@ func initTaskCmd() *cobra.Command {
Short: "查看定时任务状态", Short: "查看定时任务状态",
Long: "查看定时任务状态", Long: "查看定时任务状态",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.TaskStatus(cmd, args) statusTask(cmd, args)
}, },
} }
runTask := &cobra.Command{ runTask := &cobra.Command{
@ -158,7 +153,7 @@ func initTaskCmd() *cobra.Command {
Short: "手动执行一次任务", Short: "手动执行一次任务",
Long: "手动执行一次任务", Long: "手动执行一次任务",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.TaskRun(cmd, args) runTask(cmd, args)
}, },
} }
taskCmd.AddCommand(editTask) taskCmd.AddCommand(editTask)
@ -183,7 +178,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) {
cmd_handle.ProviderSupport(cmd, args) providerSupport(cmd, args)
}, },
} }
providerList := &cobra.Command{ providerList := &cobra.Command{
@ -191,7 +186,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) {
cmd_handle.ProviderList(cmd, args) providerList(cmd, args)
}, },
} }
providerAdd := &cobra.Command{ providerAdd := &cobra.Command{
@ -199,7 +194,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) {
cmd_handle.ProviderAdd(cmd, args) providerAdd(cmd, args)
}, },
} }
providerEdit := &cobra.Command{ providerEdit := &cobra.Command{
@ -207,7 +202,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) {
cmd_handle.ProviderEdit(cmd, args) providerEdit(cmd, args)
}, },
} }
providerDelete := &cobra.Command{ providerDelete := &cobra.Command{
@ -215,7 +210,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) {
cmd_handle.ProviderDelete(cmd, args) providerDelete(cmd, args)
}, },
} }
@ -241,7 +236,7 @@ func certCmd() *cobra.Command {
Short: "列出已配置的证书", Short: "列出已配置的证书",
Long: "列出已配置的证书", Long: "列出已配置的证书",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.CertList(cmd, args) certList(cmd, args)
}, },
} }
certAdd := &cobra.Command{ certAdd := &cobra.Command{
@ -249,7 +244,7 @@ func certCmd() *cobra.Command {
Short: "添加证书", Short: "添加证书",
Long: "添加证书", Long: "添加证书",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.CertAdd(cmd, args) certAdd(cmd, args)
}, },
} }
certDelete := &cobra.Command{ certDelete := &cobra.Command{
@ -257,7 +252,7 @@ func certCmd() *cobra.Command {
Short: "删除证书", Short: "删除证书",
Long: "删除证书", Long: "删除证书",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.CertDelete(cmd, args) certDelete(cmd, args)
}, },
} }
editCert := &cobra.Command{ editCert := &cobra.Command{
@ -265,7 +260,7 @@ func certCmd() *cobra.Command {
Short: "编辑证书", Short: "编辑证书",
Long: "编辑证书", Long: "编辑证书",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd_handle.CertEdit(cmd, args) editCert(cmd, args)
}, },
} }
certCmd.AddCommand(certList) certCmd.AddCommand(certList)
@ -279,7 +274,7 @@ func acmeCmd() *cobra.Command {
acmeCmd := &cobra.Command{ acmeCmd := &cobra.Command{
Use: "acme", Use: "acme",
Short: "ACME相关命令", Short: "ACME相关命令",
Long: "acme.sh原生命令", Long: "ACME相关命令",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmd.Help() cmd.Help()
}, },

View File

@ -1,22 +0,0 @@
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")
}

View File

@ -1,19 +0,0 @@
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))
}

View File

@ -1,26 +0,0 @@
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")
}

View File

@ -1,148 +0,0 @@
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
}

View File

@ -1,26 +0,0 @@
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")
}

95
src/cmd/handler.go Normal file
View File

@ -0,0 +1,95 @@
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")
}

View File

@ -1,82 +1,83 @@
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...")// os.Remove(PidFile) log.Println("Stopping daemon...")
// log.Println("Remove PID File...") os.Remove(PidFile)
// os.Remove(SocketFile) log.Println("Remove PID File...")
// log.Println("Remove Socket File...") os.Remove(SocketFile)
// os.Exit(0) log.Println("Remove Socket File...")
//} os.Exit(0)
// }
///*
//收到命令 /*
//*/ 收到命令
//func onCommand(command string) { */
// func onCommand(command string) {
//}
// }
//func onConfig() {
// func onConfig() {
//}
}

View File

@ -1,14 +1,10 @@
package common package common
import ( import (
"acme-mana/src/model"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const PidFile = "acme-mana.pid"
const SocketFile = "acme-mana.sock"
const CertFileName = "fullchain.pem"
const KeyFileName = "privkey.pem"
const CertInfoFileName = "info.json"
var RootCmd *cobra.Command var RootCmd *cobra.Command
var AppConf *model.AppConfig

View File

@ -1,63 +0,0 @@
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"`
}

View File

@ -1,129 +0,0 @@
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
}

View File

@ -2,90 +2,25 @@ 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 LoadAppConfig() { func readAppConfig(c *model.AppConfig) *model.AppConfig {
// 读取配置文件位置 confFile, err := common.RootCmd.PersistentFlags().GetString("conf")
confFile := getConfFile() if err != nil {
// 判断配资文件是否存在 log.Fatalln("读取配置文件参数失败")
if _, err := os.Stat(confFile); os.IsNotExist(err) { }
_, err = os.Stat(confFile)
if os.IsNotExist(err) {
log.Println("配置文件不存在, 自动创建") log.Println("配置文件不存在, 自动创建")
config := defaultAppConfig() writeConf(c, confFile)
log.Println("默认配置文件创建成功")
log.Println("服务器通信密钥: " + config.Server.Key)
appConf = config
writeConf(appConf, confFile)
} else {
appConf = readAppConfig(appConf)
} }
}
// RefreshConfig 刷新配置
func RefreshConfig() {
readAppConfig(appConf)
}
// WriteConfig 写入配置文件
func WriteConfig() {
confFile, err := common.RootCmd.PersistentFlags().GetString("conf")
if err != nil {
log.Fatalln("读取配置文件参数失败")
}
writeConf(appConf, confFile)
}
func writeConf(c *AppConfig, file string) {
out, err := yaml.Marshal(c)
if err != nil {
log.Fatalln("序列化配置文件失败")
}
util.MkFileDir(file)
err = os.WriteFile(file, out, 0644)
if err != nil {
log.Fatalln("写入配置文件失败")
}
}
// DefaultAppConfig 默认配置
func defaultAppConfig() *AppConfig {
serverKey := util.RandomStr(32)
return &AppConfig{
Server: &ServerConf{
Host: "0.0.0.0",
Port: 36851,
Key: serverKey,
},
Web: &WebConf{
Enable: false,
Host: "0.0.0.0",
Port: 36852,
},
Task: &TaskConf{
Delay: 0,
Interval: 0,
},
Certs: 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) file, err := os.ReadFile(confFile)
if err != nil { if err != nil {
log.Fatalln("读取配置文件失败") log.Fatalln("读取配置文件失败")
@ -97,3 +32,46 @@ func readAppConfig(c *AppConfig) *AppConfig {
} }
return c return c
} }
func InitAppConfig() {
common.AppConf = DefaultAppConfig()
}
func RefreshConfig() {
readAppConfig(common.AppConf)
}
func WriteConfig() {
confFile, err := common.RootCmd.PersistentFlags().GetString("conf")
if err != nil {
log.Fatalln("读取配置文件参数失败")
}
writeConf(common.AppConf, confFile)
}
func writeConf(c *model.AppConfig, file string) {
out, err := yaml.Marshal(c)
if err != nil {
log.Fatalln("序列化配置文件失败")
}
util.MkFileDir(file)
err = os.WriteFile(file, out, 0644)
if err != nil {
log.Fatalln("写入配置文件失败")
}
}
func DefaultAppConfig() *model.AppConfig {
return &model.AppConfig{
Server: &model.ServerConf{
Host: "0.0.0.0",
Port: 36851,
},
Task: &model.TaskConf{
Delay: 0,
Interval: 0,
},
Certs: nil,
Providers: nil,
}
}

View File

@ -1,3 +0,0 @@
package conf
var appConf *AppConfig

View File

@ -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
//} }

View File

@ -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
//} }

View File

@ -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)
//} }

18
src/http/handler.go Normal file
View File

@ -0,0 +1,18 @@
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")
}

42
src/http/server.go Normal file
View File

@ -0,0 +1,42 @@
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)
}

34
src/model/conf.go Normal file
View File

@ -0,0 +1,34 @@
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"`
}

View File

@ -1,8 +1,6 @@
package src package src
import ( import "acme-mana/src/cmd"
"acme-mana/src/cmd"
)
func StartProgram() { func StartProgram() {
_, err := cmd.InitCmd() _, err := cmd.InitCmd()
@ -10,5 +8,4 @@ func StartProgram() {
panic(err) panic(err)
return return
} }
} }

View File

@ -1,79 +0,0 @@
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)
}

View File

@ -1,149 +0,0 @@
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)
})
}
}

View File

@ -1,13 +0,0 @@
package http_handler
import (
"github.com/gin-gonic/gin"
)
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 进行身份认证
c.Next()
}
}

View File

@ -1,43 +0,0 @@
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())
}

View File

@ -1,17 +0,0 @@
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))
}

View File

@ -1,34 +0,0 @@
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
}
}
}

View File

@ -1,19 +0,0 @@
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,
}
}

View File

@ -1,27 +0,0 @@
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,
}
}

View File

@ -1,53 +0,0 @@
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)
}
}

View File

@ -1 +0,0 @@
package socket_handler

View File

@ -1,47 +0,0 @@
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)
}

View File

@ -1,4 +0,0 @@
package server
var HttpInstance *HttpServer
var SocketInstance *SocketServer

View File

@ -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)
//
//} }

View File

@ -1,114 +0,0 @@
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)
}

View File

@ -1,17 +0,0 @@
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)
}

28
src/variable.go Normal file
View File

@ -0,0 +1,28 @@
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"`
}