acme-client/main.go
2024-10-31 11:51:00 +08:00

256 lines
6.6 KiB
Go

package main
import (
"acme-client/src"
"bufio"
"encoding/json"
"fmt"
"github.com/go-acme/lego/v4/log"
"io"
"net/http"
"os"
"strconv"
"strings"
)
func main() {
// 读取server参数,如果没有提示添加
config := src.GetClientConfig()
server := config.Server
if server == "" {
log.Println("请输入服务端地址; 如: http://acme.server.com:8080")
reader := bufio.NewReader(os.Stdin)
serverAddr, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
serverAddr = strings.Trim(serverAddr, "\r\n")
config.Server = serverAddr
src.WriteConfig()
}
// 读取服务器RSA公钥, 没有提示添加
rsaPublicKey := config.RsaPublicKey
if rsaPublicKey == "" {
log.Println("请输入服务端下发的RAS公钥;")
reader := bufio.NewReader(os.Stdin)
rsaPublicKeyContent, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
rsaPublicKeyContent = strings.Trim(rsaPublicKeyContent, "\r\n")
config.RsaPublicKey = rsaPublicKeyContent
src.WriteConfig()
}
// 获取指令, 如果没有则提示添加
args := os.Args
if len(args) < 2 {
log.Println("请输入命令; 如: add")
log.Println("或者输入 help 查看帮助")
return
}
onCommand()
}
func onCommand() {
command := os.Args[1]
switch command {
case "help":
showHelp()
case "list":
showList()
case "add":
addConf()
case "del":
delConf()
case "edit":
editConf()
case "get":
getCert()
case "-s":
onServerCommand()
default:
log.Fatalf("不支持的指令: %s\n您可以使用 help 查看帮助", command)
}
}
// 帮助
func showHelp() {
fmt.Printf("help\t查看帮助\n")
fmt.Printf("list\t查看配置列表\n")
fmt.Printf("add\t添加配置\n")
fmt.Printf("del\t删除配置\n")
fmt.Printf("edit\t修改配置\n")
fmt.Printf("get\t从获取端获取证书,会自动判断有效期\n")
fmt.Printf("\t[-f]\t从获取端获取证书,不判断有效期\n")
fmt.Printf("-s list\t查看服务端已配置的Domain名称\n")
}
// 显示配置列表
func showList() {
domains := src.GetClientConfig().Domains
if len(domains) == 0 {
fmt.Println("暂无配置; 您可以使用 add 命令添加配置")
return
}
for i := range domains {
domain := domains[i]
fmt.Printf("%d. %s\n", i, domain.Name)
fmt.Printf(" - 证书文件: %s\n", domain.CertFile)
fmt.Printf(" - 密钥文件: %s\n", domain.KeyFile)
fmt.Printf(" - 详情文件: %s\n", domain.InfoFile)
fmt.Println()
}
}
// 添加配置
func addConf() {
domain := &src.Domain{}
name := scanConf("请输入服务端已配置名称;可以通过 -s list 查看", "名称不能为空")
domain.Name = name
domain.CertFile = scanConf("请输入证书文件存放全路径;如: /data/cert/fullchain.pem", "证书文件路径不能为空")
domain.KeyFile = scanConf("请输入私钥文件存放全路径;如: /data/cert/privkey.pem", "私钥文件路径不能为空")
domain.InfoFile = scanConf("请输入详情文件存放全路径;如: /data/cert/info.json", "详情文件路径不能为空")
config := src.GetClientConfig()
config.Domains = append(config.Domains, *domain)
src.WriteConfig()
fmt.Println("添加成功")
showList()
}
// 删除配置
func delConf() {
fmt.Println("当前配置: ")
showList()
indexInt, _ := selectDomain("请输入要删除的序号")
config := src.GetClientConfig()
config.Domains = append(config.Domains[:indexInt], config.Domains[indexInt+1:]...)
src.WriteConfig()
fmt.Println("删除成功")
showList()
}
// 修改配置
func editConf() {
fmt.Println("当前配置")
showList()
_, domain := selectDomain("请输入要修改的序号")
msg := fmt.Sprintf("请输入服务端已配置名称;可以通过 -s list 查看\n当前配置: %s\n", domain.Name)
domain.Name = scanConf(msg, "名称不能为空")
certFile := fmt.Sprintf("请输入证书文件存放全路径;如: /data/cert/fullchain.pem\n当前配置: %s\n", domain.CertFile)
domain.CertFile = scanConf(certFile, "证书文件路径不能为空")
msg = fmt.Sprintf("请输入私钥文件存放全路径;如: /data/cert/privkey.pem\n当前配置: %s\n", domain.KeyFile)
domain.KeyFile = scanConf(msg, "私钥文件路径不能为空")
msg = fmt.Sprintf("请输入详情文件存放全路径;如: /data/cert/info.json\n当前配置: %s\n", domain.InfoFile)
domain.InfoFile = scanConf(msg, "详情文件路径不能为空")
src.WriteConfig()
fmt.Println("修改成功")
showList()
}
func getCert() {
_, domain := selectDomain("请选择要获取证书的配置编号")
args := os.Args
isDoGet := len(args) >= 3 && args[3] == "-f"
if !isDoGet {
infoFile := domain.InfoFile
os.Stat(infoFile)
//info, err := os.ReadFile(infoFile)
//keyFile := domain.KeyFile
}
//config := src.GetClientConfig()
//domains := config.Domains
//config.FindDomain(args[2])
}
// 服务端命令
func onServerCommand() {
args := os.Args
if len(args) < 3 {
log.Fatal("参数错误, 请检查")
}
command := args[2]
switch command {
case "list":
showServerList()
default:
log.Fatal("参数错误, 请检查")
}
}
// 从服务端获取域名列表
func showServerList() {
server := src.GetClientConfig().Server
token, encryptToken := src.GenToken()
url := server + "/api/v1/domain/list?token=" + encryptToken
resp, err := http.Get(url)
if err != nil {
log.Fatal("获取服务端数据失败, 请检查 server 地址是否正确")
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
result := src.Result{}
err = json.Unmarshal(body, &result)
if err != nil {
log.Fatal(err)
}
if result.Code != 200 {
log.Fatal("读取数据出错; ", result.Msg)
}
data := result.Data
text := src.DecryptByToken(token, data)
d := &[]src.SDomain{}
err = json.Unmarshal([]byte(text), d)
if err != nil {
log.Fatal(err)
}
for i := range *d {
domain := (*d)[i]
fmt.Printf("- %s\n", domain.Name)
fmt.Printf("\t认证域名: [ ")
for j := range domain.Host {
host := domain.Host[j]
fmt.Printf("%s ", host)
}
fmt.Printf("]\n")
}
}
// 读取用户输入
func scanConf(msg string, errMsg string) string {
for {
log.Println(msg)
reader := bufio.NewReader(os.Stdin)
name, err := reader.ReadString('\n')
if err != nil {
fmt.Println("读取失败;", err)
continue
}
name = strings.Trim(name, "\r\n")
if name == "" {
fmt.Println(errMsg)
continue
}
return name
}
}
// 选择域名
func selectDomain(msg string) (int, *src.Domain) {
index := scanConf(msg, "序号不能为空")
indexInt, err := strconv.Atoi(index)
if err != nil {
log.Fatal("序号错误")
}
config := src.GetClientConfig()
if indexInt >= len(config.Domains) || indexInt < 0 {
log.Fatal("序号超出范围")
}
return indexInt, &config.Domains[indexInt]
}