feat: init

This commit is contained in:
micah 2026-03-28 19:25:12 +08:00
commit 6baa62dc06
26 changed files with 961 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@ -0,0 +1,28 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# ide
.idea
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
magic
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
go.work.sum
# env file
.env

0
README.md Normal file
View File

41
cmd/acme_sh/acme_sh.go Normal file
View File

@ -0,0 +1,41 @@
package acme_sh
import (
"context"
"fmt"
"os/exec"
"strings"
"gitea.micah.wiki/pandora/magic/config"
"gitea.micah.wiki/pandora/magic/pkg/env"
)
func Run(_ context.Context, cmdPath string, conf *config.Config, current *config.AcmeSh, isNew bool) error {
var currentCmd string
domains := strings.Split(current.Domain, ",")
domainValue := ""
for _, domain := range domains {
if strings.Contains(domain, "*") {
domainValue = fmt.Sprintf("%s -d \"%s\"", domainValue, domain)
} else {
domainValue = fmt.Sprintf("%s -d %s", domainValue, domain)
}
}
if isNew {
currentCmd = fmt.Sprintf("%s --issue %s --dns %s --key-file %s/key.pem --fullchain-file %s/cert.pem --yes-I-know-dns-manual-mode-enough-go-ahead-please --server letsencrypt",
cmdPath, domainValue, current.DNSType, current.KeyPath, current.KeyPath)
} else {
currentCmd = fmt.Sprintf("%s --renew %s --dns %s --key-file %s/key.pem --fullchain-file %s/cert.pem --yes-I-know-dns-manual-mode-enough-go-ahead-please --force --server letsencrypt",
cmdPath, domainValue, current.DNSType, current.KeyPath, current.KeyPath)
}
cmd := exec.Command(env.GetShellName(), "-c", fmt.Sprintf("source %s; %s", conf.ShellConfig, currentCmd))
output, err := cmd.Output()
// 如果返回是没有错误,则直接成功。
if err != nil {
return err
}
fmt.Println(fmt.Sprintf("acme.sh 创建成功\n%s", string(output)))
return nil
}

47
cmd/acme_sh/check.go Normal file
View File

@ -0,0 +1,47 @@
package acme_sh
import (
"context"
"fmt"
"os/exec"
"strings"
"gitea.micah.wiki/pandora/magic/config"
"gitea.micah.wiki/pandora/magic/pkg/env"
"gitea.micah.wiki/pandora/magic/pkg/loggerx"
)
// CheckAcmeSh 安装ache.sh 成功后返回安装路径,否则返回异常
func CheckAcmeSh(ctx context.Context, conf *config.Config, current *config.AcmeSh) (string, error) {
shellConf := conf.ShellConfig
shellName := env.GetShellName()
if len(shellConf) == 0 {
tempConf, err := env.GetShellConfig()
if err != nil {
return "", err
}
if len(tempConf) == 0 {
return "", fmt.Errorf("未找到sh配置文件地址")
}
shellConf = tempConf
conf.ShellConfig = tempConf
}
cmd := exec.Command(shellName, "-c", fmt.Sprintf("source %s; ls ~/.acme.sh/acme.sh", shellConf))
output, err := cmd.Output()
// 如果返回是没有错误,则直接成功。
if err == nil {
dir := strings.TrimSpace(string(output))
fmt.Println("acme.sh 路径为: " + dir)
loggerx.AcmeSh().Infof("acme.sh already installed. dir: %s", dir)
return dir, nil
}
cmd = exec.Command(shellName, "-c", fmt.Sprintf("source %s; curl https://get.acme.sh | sh -s email=%s", shellConf, current.DNSEmail))
output, err = cmd.Output()
// 如果返回是没有错误,则直接成功。
if err != nil {
return "", err
}
fmt.Println(fmt.Sprintf("acme.sh 安装成功\n%s", string(output)))
return CheckAcmeSh(ctx, conf, current)
}

44
cmd/acme_sh/command.go Normal file
View File

@ -0,0 +1,44 @@
package acme_sh
import (
"fmt"
"github.com/urfave/cli/v2"
"gitea.micah.wiki/pandora/magic/config"
)
func AcmeSH() *cli.Command {
return &cli.Command{
Name: "acme.sh",
Action: func(c *cli.Context) error {
conf := config.Get()
if len(conf.AcmeShs) == 0 {
return fmt.Errorf("未找到acme.sh的配置")
}
current, err := ReadCurrentAcmeSh(c.Context, conf)
if err != nil {
return err
}
cmdPath, err := CheckAcmeSh(c.Context, conf, current)
if err != nil {
return err
}
newFlag, err := ReadIsNew(c.Context)
if err != nil {
return err
}
err = Run(c.Context, cmdPath, conf, current, newFlag)
if err != nil {
return err
}
_ = config.Save(conf)
return nil
},
}
}

38
cmd/acme_sh/read.go Normal file
View File

@ -0,0 +1,38 @@
package acme_sh
import (
"context"
"fmt"
"strings"
"gitea.micah.wiki/pandora/naive/pkg/stdinx"
"gitea.micah.wiki/pandora/magic/config"
)
func ReadIsNew(ctx context.Context) (bool, error) {
isNewFlag, err := stdinx.ReadStr(ctx, fmt.Sprintf("*** 新增 or 更新:(新增: Y; 更新: N;"))
if err != nil {
return false, err
}
if strings.EqualFold(isNewFlag, "y") {
return true, nil
}
return false, nil
}
func ReadCurrentAcmeSh(ctx context.Context, conf *config.Config) (*config.AcmeSh, error) {
total := len(conf.AcmeShs)
for i, acmeSh := range conf.AcmeShs {
check, err := stdinx.ReadStr(ctx, fmt.Sprintf("*** 请确认是否需要生成当前域名`%s`(%d/%d):(是: Y; 否: N;", acmeSh.Domain, i+1, total))
if err != nil {
return nil, err
}
if strings.EqualFold(check, "y") {
return acmeSh, nil
}
}
return nil, fmt.Errorf("未选择任何需要生成的域名,请编辑配置后,再运行")
}

19
cmd/cmd.go Normal file
View File

@ -0,0 +1,19 @@
package cmd
import (
"github.com/urfave/cli/v2"
acmesh "gitea.micah.wiki/pandora/magic/cmd/acme_sh"
"gitea.micah.wiki/pandora/magic/cmd/initialize"
"gitea.micah.wiki/pandora/magic/cmd/nginx"
"gitea.micah.wiki/pandora/magic/cmd/test"
)
func GetList() []*cli.Command {
return []*cli.Command{
test.Test(),
nginx.Nginx(),
acmesh.AcmeSH(),
initialize.Init(),
}
}

25
cmd/initialize/check.go Normal file
View File

@ -0,0 +1,25 @@
package initialize
import (
"context"
"fmt"
"os/exec"
)
func CheckHomebrew(ctx context.Context) error {
dir, err := exec.LookPath("brew")
if len(dir) > 0 {
fmt.Println("Homebrew 路径为: " + dir)
return nil
}
install := ReadInstallHomebrew(ctx)
if install {
err = InstallHomebrew(ctx)
if err != nil {
return err
}
}
return CheckHomebrew(ctx)
}

18
cmd/initialize/command.go Normal file
View File

@ -0,0 +1,18 @@
package initialize
import (
"github.com/urfave/cli/v2"
)
func Init() *cli.Command {
return &cli.Command{
Name: "init",
Action: func(c *cli.Context) error {
err := CheckHomebrew(c.Context)
if err != nil {
return err
}
return nil
},
}
}

18
cmd/initialize/install.go Normal file
View File

@ -0,0 +1,18 @@
package initialize
import (
"context"
"fmt"
"os/exec"
)
func InstallHomebrew(_ context.Context) error {
cmd := exec.Command("/bin/bash", "-c", "\"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"")
output, err := cmd.Output()
// 如果返回是没有错误,则直接成功。
fmt.Println(string(output))
if err != nil {
return err
}
return nil
}

16
cmd/initialize/read.go Normal file
View File

@ -0,0 +1,16 @@
package initialize
import (
"context"
"strings"
"gitea.micah.wiki/pandora/naive/pkg/stdinx"
)
func ReadInstallHomebrew(ctx context.Context) bool {
restartFlag, err := stdinx.ReadStr(ctx, "*** 是否需要安装Homebrew确认Y or Yes按其他任意键不重启")
if err != nil {
return false
}
return strings.EqualFold(restartFlag, "yes") || strings.EqualFold(restartFlag, "y")
}

53
cmd/nginx/check.go Normal file
View File

@ -0,0 +1,53 @@
package nginx
import (
"context"
"fmt"
"os/exec"
"strings"
"gitea.micah.wiki/pandora/naive/pkg/filex"
"gitea.micah.wiki/pandora/magic/config"
)
func Check(_ context.Context, nginx *config.Nginx) (bool, error) {
_, err := exec.LookPath("nginx")
if err != nil {
return false, err
}
if b, err := filex.Exists(nginx.RootPath); err != nil {
return false, err
} else if !b {
return false, fmt.Errorf("nginx.RootPath: %s, 不存在", nginx.RootPath)
}
if b, err := filex.Exists(nginx.LogPath); err != nil {
return false, err
} else if !b {
return false, fmt.Errorf("nginx.LogPath: %s, 不存在", nginx.LogPath)
}
if b, err := filex.Exists(nginx.SSLPath); err != nil {
return false, err
} else if !b {
return false, fmt.Errorf("nginx.SSLPath: %s, 不存在", nginx.SSLPath)
}
if b, err := filex.Exists(nginx.ConfigPath); err != nil {
return false, err
} else if !b {
return false, fmt.Errorf("nginx.ConfigPath: %s, 不存在", nginx.ConfigPath)
}
if len(nginx.Domain) == 0 {
return false, fmt.Errorf("nginx.Domain 为空")
}
if len(nginx.Proxy) == 0 || !strings.HasPrefix(nginx.Proxy, "http") {
return false, fmt.Errorf("nginx.Proxy 为空 或者 不是以http开头")
}
return true, nil
}

49
cmd/nginx/command.go Normal file
View File

@ -0,0 +1,49 @@
package nginx
import (
"fmt"
"github.com/urfave/cli/v2"
"gitea.micah.wiki/pandora/magic/config"
)
func Nginx() *cli.Command {
return &cli.Command{
Name: "nginx",
Action: func(c *cli.Context) error {
conf := config.Get()
if len(conf.Nginx) == 0 {
return fmt.Errorf("未找到nginx的配置")
}
current, err := ReadCurrent(c.Context, conf)
if err != nil {
return err
}
exist, err := Check(c.Context, current)
if err != nil {
return err
}
if !exist {
return fmt.Errorf("在 $PATH 中未找到nginx请自行安装并配置在 $PATH 中")
}
err = Create(c.Context, current)
if err != nil {
return err
}
restart := ReadRestart(c.Context)
if restart {
err = ReStart(c.Context)
if err != nil {
return err
}
}
return nil
},
}
}

100
cmd/nginx/nginx.go Normal file
View File

@ -0,0 +1,100 @@
package nginx
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"gitea.micah.wiki/pandora/naive/pkg/filex"
"gitea.micah.wiki/pandora/magic/config"
"gitea.micah.wiki/pandora/magic/pkg/loggerx"
)
func Create(ctx context.Context, current *config.Nginx) error {
file := fmt.Sprintf("%s/%s", current.ConfigPath, current.Domain)
isExist, err := filex.Exists(file)
if err != nil {
loggerx.Nginx().Errorf("config file Exists error, err: %+v", err)
return err
}
if isExist {
err = os.Remove(file)
if err != nil {
loggerx.Nginx().Errorf("config file Remove error, err: %+v", err)
return err
}
}
f, err := filex.CreateIfNotExists(file)
if err != nil {
loggerx.Nginx().Errorf("CreateIfNotExists error, err: %+v", err)
return err
}
defer func() { _ = f.Close() }()
_, err = f.Write([]byte(GetInfo(ctx, current)))
if err != nil {
loggerx.Nginx().Errorf("Write config error, err: %+v", err)
return err
}
if strings.Contains(current.ConfigPath, "sites-available") {
lnFile := fmt.Sprintf("%s/../sites-enabled/%s", current.ConfigPath, current.Domain)
isExist, err = filex.Exists(lnFile)
if err != nil {
loggerx.Nginx().Errorf("ln config file Exists error, err: %+v", err)
return err
}
if isExist {
err = os.Remove(lnFile)
if err != nil {
loggerx.Nginx().Errorf("ln config file Remove error, err: %+v", err)
return err
}
}
cmd := exec.Command("ln", "-s", file, lnFile)
// 执行命令并捕获输出
_, err = cmd.CombinedOutput()
if err != nil {
loggerx.Nginx().Errorf("ln config error, err: %+v", err)
// 若执行命令出错,打印错误信息和命令输出
return err
}
}
return nil
}
func ReStart(_ context.Context) error {
_, err := exec.LookPath("brew")
if err == nil {
// 如果存在brew 则按照 brew运行
cmd := exec.Command("brew", "services", "restart", "nginx")
// 执行命令并捕获输出
output, err := cmd.CombinedOutput()
if err != nil {
// 若执行命令出错,打印错误信息和命令输出
return err
}
fmt.Println(string(output))
return nil
}
cmd := exec.Command("systemctl", "restart", "nginx")
// 执行命令并捕获输出
output, err := cmd.CombinedOutput()
if err == nil {
fmt.Println(string(output))
// 若执行命令出错,打印错误信息和命令输出
return nil
}
cmd = exec.Command("nginx", "-s", "reload")
// 执行命令并捕获输出
output, err = cmd.CombinedOutput()
if err != nil {
// 若执行命令出错,打印错误信息和命令输出
return err
}
fmt.Println(string(output))
return nil
}

46
cmd/nginx/read.go Normal file
View File

@ -0,0 +1,46 @@
package nginx
import (
"context"
"fmt"
"strings"
"gitea.micah.wiki/pandora/naive/pkg/stdinx"
"gitea.micah.wiki/pandora/magic/config"
)
//func ReadIsNew(ctx context.Context) (bool, error) {
// isNewFlag, err := stdinx.ReadStr(ctx, fmt.Sprintf("*** 新增 or 更新:(新增: Y; 更新: N;"))
// if err != nil {
// return false, err
// }
//
// if strings.EqualFold(isNewFlag, "y") {
// return true, nil
// }
//
// return false, nil
//}
func ReadCurrent(ctx context.Context, conf *config.Config) (*config.Nginx, error) {
total := len(conf.Nginx)
for i, nginx := range conf.Nginx {
check, err := stdinx.ReadStr(ctx, fmt.Sprintf("*** 请确认是否需要生成当前域名`%s`(%d/%d):(是: Y; 否: N;", nginx.Domain, i+1, total))
if err != nil {
return nil, err
}
if strings.EqualFold(check, "y") {
return nginx, nil
}
}
return nil, fmt.Errorf("未选择任何需要生成的域名,请编辑配置后,再运行")
}
func ReadRestart(ctx context.Context) bool {
restartFlag, err := stdinx.ReadStr(ctx, "*** 是否需要重启nginx确认Y or Yes按其他任意键不重启")
if err != nil {
return false
}
return strings.EqualFold(restartFlag, "yes") || strings.EqualFold(restartFlag, "y")
}

97
cmd/nginx/template.go Normal file
View File

@ -0,0 +1,97 @@
package nginx
import (
"context"
"fmt"
"strings"
"gitea.micah.wiki/pandora/magic/config"
)
var (
domainKey = "${Domain}"
logPathKey = "${LogPath}"
sslPathKey = "${SSLPath}"
certPathKey = "${CertPath}"
rootPathKey = "${RootPath}"
proxyKey = "${Proxy}"
httpTemplate = `
server {
listen 80;
server_name ${Domain};
rewrite ^(.*)$ https://${server_name}$1 permanent;
}
`
httpsTemplate = `
server {
listen 443 ssl;
server_name ${Domain};
error_log ${LogPath}/${Domain}.error.log;
access_log ${LogPath}/${Domain}.access.log main;
ssl_certificate ${SSLPath}/${CertPath}cert.pem;
ssl_certificate_key ${SSLPath}/${CertPath}key.pem;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
root ${RootPath};
location / {
proxy_pass ${Proxy};
#开启websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Forwarded-Ssl on;
proxy_set_header Accept-Encoding "";
proxy_connect_timeout 86400;
proxy_send_timeout 86400;
proxy_read_timeout 86400;
send_timeout 86400;
client_max_body_size 100g;
client_body_buffer_size 128k;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
location = /robots.txt {}
}
`
)
func GetInfo(ctx context.Context, nginx *config.Nginx) string {
nginxConf := GetHTTPsInfo(ctx, nginx.Domain, nginx.RootPath, nginx.LogPath, nginx.SSLPath, nginx.CertPrefix, nginx.Proxy)
if nginx.HTTPDirect {
nginxConf = fmt.Sprintf("%s\n%s", nginxConf, GetHTTPInfo(ctx, nginx.Domain))
}
return nginxConf
}
func GetHTTPInfo(_ context.Context, domain string) string {
return strings.ReplaceAll(httpTemplate, domainKey, domain)
}
func GetHTTPsInfo(_ context.Context, domain, rootPath, logPath, sslPath, certPrefix, proxy string) string {
info := strings.ReplaceAll(httpsTemplate, domainKey, domain)
info = strings.ReplaceAll(info, rootPathKey, rootPath)
info = strings.ReplaceAll(info, logPathKey, logPath)
info = strings.ReplaceAll(info, sslPathKey, sslPath)
info = strings.ReplaceAll(info, certPathKey, certPrefix)
info = strings.ReplaceAll(info, proxyKey, proxy)
return info
}

32
cmd/test/test.go Normal file
View File

@ -0,0 +1,32 @@
package test
import (
"fmt"
"github.com/urfave/cli/v2"
"gitea.micah.wiki/pandora/naive/pkg/pointx"
"gitea.micah.wiki/pandora/naive/pkg/stdinx"
)
func Test() *cli.Command {
return &cli.Command{
Name: "test",
Action: func(c *cli.Context) error {
// 假设这是你的原始数据
data := []string{"apple", "banana", "cherry", "date", "elderberry", "fig"}
// 打印原始数据
tips := "原始数据:\n"
for _, item := range data {
tips = fmt.Sprintf("%s%s\n", tips, item)
}
value, err := stdinx.ReadStr(c.Context, tips)
if err != nil {
return err
}
fmt.Println(pointx.Prettify(value))
return nil
},
}
}

8
config/acme_sh.go Normal file
View File

@ -0,0 +1,8 @@
package config
type AcmeSh struct {
Domain string `yaml:"domain" toml:"domain" json:"domain"`
KeyPath string `yaml:"key_path" toml:"key_path" json:"key_path"`
DNSType string `yaml:"dns_type" toml:"dns_type" json:"dns_type"`
DNSEmail string `yaml:"dns_email" toml:"dns_email" json:"dns_email"`
}

94
config/config.go Normal file
View File

@ -0,0 +1,94 @@
package config
import (
"fmt"
"os"
"gitea.micah.wiki/pandora/naive/pkg/filex"
)
type Config struct {
ShellConfig string `yaml:"shell_config" toml:"shell_config" json:"shell_config"`
Nginx []*Nginx `yaml:"nginx" toml:"nginx" json:"nginx"`
AcmeShs []*AcmeSh `yaml:"acme_sh" toml:"acme_sh" json:"acme_sh"`
}
var (
config *Config
configTypes = []string{"yaml", "toml", "json"}
fileName = ""
configType = ""
)
func init() {
homeDir, _ := os.UserHomeDir()
for _, cType := range configTypes {
name := fmt.Sprintf("%s/.magic/config.%s", homeDir, cType)
exist, err := filex.Exists(name)
if err != nil {
panic(err)
}
if exist {
fileName = name
configType = cType
break
}
}
conf, err := parseByType()
if err != nil {
panic(err)
}
config = conf
}
func parseByType() (*Config, error) {
conf := &Config{}
switch configType {
case "yaml":
err := filex.ParseYAML(fileName, conf)
if err != nil {
return nil, err
}
case "toml":
err := filex.ParseTOML(fileName, conf)
if err != nil {
return nil, err
}
case "json":
err := filex.ParseJSON(fileName, conf)
if err != nil {
return nil, err
}
}
return conf, nil
}
func Save(conf *Config) error {
if configType == "" {
configType = "yaml"
homeDir, _ := os.UserHomeDir()
fileName = fmt.Sprintf("%s/.magic/config.%s", homeDir, configType)
}
switch configType {
case "yaml":
err := filex.GenerateYAML(fileName, conf)
if err != nil {
return err
}
case "toml":
err := filex.ParseTOML(fileName, conf)
if err != nil {
return err
}
case "json":
err := filex.ParseJSON(fileName, conf)
if err != nil {
return err
}
}
return nil
}
func Get() *Config {
return config
}

12
config/nginx.go Normal file
View File

@ -0,0 +1,12 @@
package config
type Nginx struct {
Domain string `yaml:"domain" toml:"domain" json:"domain"`
RootPath string `yaml:"root_path" toml:"root_path" json:"root_path"`
LogPath string `yaml:"log_path" toml:"log_path" json:"log_path"`
SSLPath string `yaml:"ssl_path" toml:"ssl_path" json:"ssl_path"`
CertPrefix string `yaml:"cert_prefix" toml:"cert_prefix" json:"cert_prefix"`
Proxy string `yaml:"proxy" toml:"proxy" json:"proxy"`
HTTPDirect bool `yaml:"http_direct" toml:"http_direct" json:"http_direct"`
ConfigPath string `yaml:"config_path" toml:"config_path" json:"config_path"`
}

21
go.mod Normal file
View File

@ -0,0 +1,21 @@
module gitea.micah.wiki/pandora/magic
go 1.22
toolchain go1.24.12
require (
gitea.micah.wiki/pandora/naive v1.0.2-0.20250310144841-5ab2bac644a3
github.com/urfave/cli/v2 v2.27.7
github.com/urfave/cli/v3 v3.6.2
go.uber.org/zap v1.27.1
)
require (
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.uber.org/multierr v1.11.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

30
go.sum Normal file
View File

@ -0,0 +1,30 @@
gitea.micah.wiki/pandora/naive v1.0.2-0.20250310144841-5ab2bac644a3 h1:9ea1+oQeE3R4krLsgqdJ2TfPM8ZK0+3kQ7CIIBdudDc=
gitea.micah.wiki/pandora/naive v1.0.2-0.20250310144841-5ab2bac644a3/go.mod h1:ebLisq9JH4+IP/qqSsozxr7vkIM82pyHyGcszNX51yg=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

38
main.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"fmt"
"os"
"github.com/urfave/cli/v3"
"gitea.micah.wiki/pandora/magic/cmd"
"gitea.micah.wiki/pandora/magic/config"
_ "gitea.micah.wiki/pandora/magic/config"
"gitea.micah.wiki/pandora/magic/pkg/loggerx"
)
var VERSION = "v1.0.0"
func main() {
// 刷新日志,当程序退出时,把在缓存中的内容刷到日志中
defer func() {
loggerx.Sync()
}()
app := cli.NewApp()
app.Name = "magic"
app.Version = VERSION
app.Usage = "A command line tool for tools"
app.Authors = append(app.Authors, &cli.Author{
Name: "micah",
Email: "micah.shi@gmail.com",
})
app.Commands = cmd.GetList()
if err := app.Run(os.Args); err != nil {
fmt.Println(err)
return
}
_ = config.Save(config.Get())
}

17
pkg/env/shell.go vendored Normal file
View File

@ -0,0 +1,17 @@
package env
import (
"os"
"gitea.micah.wiki/pandora/naive/pkg/env"
)
func GetShellName() string {
// 获取 SHELL 环境变量
shell := os.Getenv("SHELL")
return shell
}
func GetShellConfig() (string, error) {
return env.ShellConfig()
}

22
pkg/env/shell_test.go vendored Normal file
View File

@ -0,0 +1,22 @@
package env
import "testing"
func TestGetShellName(t *testing.T) {
tests := []struct {
name string
want string
}{
{
name: "",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetShellName(); got != tt.want {
t.Errorf("GetShellName() = %v, want %v", got, tt.want)
}
})
}
}

48
pkg/loggerx/logger.go Normal file
View File

@ -0,0 +1,48 @@
package loggerx
import (
"fmt"
"gitea.micah.wiki/pandora/naive/pkg/filex"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var (
nameNginx = "nginx"
nameAcmeSh = "acme.sh"
logger *zap.SugaredLogger
)
func Sync() {
if logger != nil {
_ = logger.Sync()
}
}
func Nginx() *zap.SugaredLogger {
return getLogger(nameNginx)
}
func AcmeSh() *zap.SugaredLogger {
return getLogger(nameAcmeSh)
}
func getLogger(name string) *zap.SugaredLogger {
if logger != nil {
return logger.Named(name)
}
homeDir, err := filex.HomeDir()
if err != nil {
panic(err)
}
loggerFile := fmt.Sprintf("%s/.magic/logs.log", homeDir)
file, err := filex.CreateIfNotExists(loggerFile)
if err != nil {
panic(err)
}
writeSyncer := zapcore.AddSync(file)
core := zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), writeSyncer, zapcore.DebugLevel)
logger = zap.New(core).Sugar()
return logger.Named(name)
}