diff --git a/db/db.go b/db/db.go index 277efd8..833bbfa 100644 --- a/db/db.go +++ b/db/db.go @@ -63,7 +63,7 @@ func WithAppName(name string) func(*gorm.DB) *gorm.DB { } } -func WithAppStatus(statuses ...uint32) func(*gorm.DB) *gorm.DB { +func WithAppStatus(statuses ...uint8) func(*gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB { switch len(statuses) { case 0: @@ -87,8 +87,8 @@ func FindApp(opts ...func(*gorm.DB) *gorm.DB) (*App, error) { } // FindApps 查找多个 App -func FindApps(opts ...func(*gorm.DB) *gorm.DB) ([]App, error) { - var apps []App +func FindApps(opts ...func(*gorm.DB) *gorm.DB) ([]*App, error) { + var apps []*App err := DB.Model(&App{}).Scopes(opts...).Find(&apps).Error if err != nil { return nil, err diff --git a/go.mod b/go.mod index 7c0aed4..57c4f09 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/mattn/go-isatty v0.0.17 github.com/pelletier/go-toml/v2 v2.0.6 - github.com/tint/env v1.0.2 + github.com/rs/xid v1.4.0 gopkg.in/ini.v1 v1.67.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/sqlite v1.4.4 @@ -15,7 +15,6 @@ require ( require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/joho/godotenv v1.4.0 // indirect github.com/mattn/go-sqlite3 v1.14.15 // indirect golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect ) diff --git a/go.sum b/go.sum index 28a5e25..3c7d26d 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvI github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= 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/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/main.go b/main.go index aff6bf2..c70eaac 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,9 @@ package main import ( + "flag" "fmt" - "github.com/tint/env" + "github.com/rs/xid" "hupeh.vip/pm/cmd" "hupeh.vip/pm/db" "os" @@ -10,86 +11,100 @@ import ( "strconv" ) -//import ( -// "flag" -// "fmt" -// "log" -// "os" -// "os/exec" -// "time" -//) -// -//// 移除不需要的参数 -//func strip(slice []string, remove string) []string { -// for i := 0; i < len(slice); { -// if slice[i] == remove { -// if i != len(slice)-1 { -// slice = append(slice[:i], slice[i+1:]...) -// } else { -// slice = slice[:i] -// } -// } else { -// i++ -// } -// } -// return slice -//} -// -//func subProcess(args []string) *exec.Cmd { -// cmd := exec.Command(args[0], args[1:]...) -// cmd.Stdin = os.Stdin -// cmd.Stdout = os.Stdout -// cmd.Stderr = os.Stderr -// err := cmd.Start() -// if err != nil { -// fmt.Fprintf(os.Stderr, "[-] Error: %s\n", err) -// } -// return cmd -//} -// -//func main() { -// daemon := flag.Bool("daemon", false, "run in daemon") -// forever := flag.Bool("forever", false, "run forever") -// flag.Parse() -// -// // 启用守护模式 -// if *daemon { -// subProcess(strip(os.Arguments, "-daemon")) -// fmt.Printf("[*] Daemon running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid()) -// os.Exit(0) -// } -// -// if *forever { -// for { -// cmd := subProcess(strip(os.Arguments, "-forever")) -// fmt.Printf("[*] Forever running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid()) -// if err := cmd.Wait(); err != nil { -// fmt.Println(err) -// os.Exit(1) -// } -// } -// } -// -// fp, _ := os.OpenFile("./dosomething.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) -// log.SetOutput(fp) -// log.Printf("[*] Service running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid()) -// defer log.Println("exit ...") -// for { -// log.Printf("DoSomething running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid()) -// time.Sleep(time.Second * 5) -// } -//} +var allowPMTs = map[string]int{ + "start": 1, + "restart": 1, + //"list": 1, + //"shell": 1, +} func main() { - env.Setup() + var sign string // all + var pmt string // all + var id int // start,restart + var daemon bool // start,restart - switch env.String("PMT_CURRENT_FUNCTION") { - case "start": - case "stop": - case "shell": - case "list": - default: + flag.StringVar(&sign, "sign", "", "sign token") + flag.StringVar(&pmt, "pmt", "", "task name") + flag.IntVar(&id, "id", 0, "app id") + flag.BoolVar(&daemon, "daemon", false, "daemon mode") + flag.Parse() + + // 命令行模式 + if v, ok := allowPMTs[pmt]; !ok || v != 1 { runCli() + return + } + + // 验证签名 + if _, err := xid.FromString(sign); err != nil { + fmt.Println(cmd.Red("bad sign token")) + return + } + + switch pmt { + // 启动应用 + case "start": + // 开启守护模式 + if !daemon { + subProcess(append(os.Args, "-daemon")...) + fmt.Printf("[*] Daemon running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid()) + os.Exit(0) + } + if id <= 0 { + fmt.Println(cmd.Red("missing app id")) + return + } + // 查询 app + app, err := db.FindApp(db.WithAppID(uint(id))) + if err != nil { + fmt.Println(cmd.Red("failed to find app")) + fmt.Println(" " + cmd.Red(err.Error())) + return + } + // 创建进程处理器 + proc := db.NewProc(app) + // 忽略运行中的 + if proc.IsAlive() { + return + } + // 启动失败 + if err = proc.Start(); err != nil { + fmt.Println(cmd.Yellow(app.Name) + " - " + cmd.Red("start fail")) + fmt.Println(" " + cmd.Red(err.Error())) + return + } + // 启动成功 + fmt.Println(cmd.Yellow(app.Name) + " - " + cmd.Green("start success")) + // 重启脚本 + case "restart": + // 开启守护模式 + if !daemon { + subProcess(append(os.Args, "-daemon")...) + fmt.Printf("[*] Daemon running in pid: %d PPID: %d\n", os.Getpid(), os.Getppid()) + os.Exit(0) + } + if id <= 0 { + fmt.Println(cmd.Red("missing app id")) + return + } + // 查询 app + app, err := db.FindApp(db.WithAppID(uint(id))) + if err != nil { + fmt.Println(cmd.Red("failed to find app")) + fmt.Println(" " + cmd.Red(err.Error())) + return + } + // 创建进程处理器 + proc := db.NewProc(app) + // 重启失败 + if err = proc.Restart(); err != nil { + fmt.Println(cmd.Yellow(app.Name) + " - " + cmd.Red("restart fail")) + fmt.Println(" " + cmd.Red(err.Error())) + return + } + // 重启成功 + fmt.Println(cmd.Yellow(app.Name) + " - " + cmd.Green("restart success")) } } @@ -99,7 +114,7 @@ func runCli() { Description("description"). // start 命令 - Command("start", "Start a program in daemon"). + Command("start", "Start program(s) in daemon"). Argument("[names:...string]", "file/name/id"). Option("-w, --watch ", "Watches folder changes"). Option("-c, --cwd ", "Sets working directory "+cmd.Yellow("Sets working directory")+" Sets working directory "+cmd.Yellow("Sets working directory")+" Sets working directory "+cmd.Yellow(" Sets working directory")+" Sets working directory "+cmd.Yellow("Sets working directory")+" Sets working directory "+cmd.Yellow("Sets working directory")+" End ..."). @@ -107,42 +122,106 @@ func runCli() { Option("-e, --env", "Sets current environment name"). Option("-i, --interpreter", "Sets interpreter name"). Action(func(c *cmd.Command, m map[string]any, a ...any) error { + var apps []*db.App + var err error if len(a) != 1 { - return c.ShowHelp() + apps, err = db.FindApps(db.WithAppStatus(db.StatusStopped)) + if err != nil { + fmt.Println(cmd.Red("load all apps fail")) + fmt.Println(" " + cmd.Red(err.Error())) + return err + } + } else { + scripts := a[0].([]string) + for _, script := range scripts { + loads, err := db.ParseInput(script) + if err != nil { + fmt.Println(cmd.Red("load all apps fail")) + fmt.Println(" " + cmd.Red(err.Error())) + return err + } + apps = append(apps, loads...) + } + } + if len(apps) == 0 { + fmt.Println("No app found") + return nil + } + for _, app := range apps { + if app.Status == db.StatusRunning { + fmt.Println(cmd.Yellow(app.Name), cmd.Green("running")) + continue + } + if app.ID == 0 { + if err := db.SaveApp(app); err != nil { + fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed to saving")) + fmt.Println(" " + cmd.Red(err.Error())) + continue + } + } + // todo options + proc := subProcess( + "-id="+strconv.Itoa(int(app.ID)), + "-pmt=start", + "-sign="+xid.New().String(), + ) + if err := proc.Wait(); err != nil { + fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed")) + fmt.Println(" ", err.Error()) + } else { + fmt.Println(cmd.Yellow(app.Name), cmd.Green("success")) + } } + fmt.Println("over") + return nil + }). + // restart 命令 + Command("restart", "Restarts program(s)"). + Argument("", "file/name/id"). + Action(func(c *cmd.Command, m map[string]any, a ...any) error { var apps []*db.App + if len(a) != 0 { + c.ShowHelp() + return nil + } scripts := a[0].([]string) for _, script := range scripts { loads, err := db.ParseInput(script) if err != nil { + fmt.Println(cmd.Red("load all apps fail")) + fmt.Println(" " + cmd.Red(err.Error())) return err } apps = append(apps, loads...) } - if len(apps) == 0 { fmt.Println("No app found") return nil } - for _, app := range apps { - if app.Status == db.StatusRunning { - fmt.Println(cmd.Yellow(app.Name), cmd.Green("running")) - continue - } if app.ID == 0 { if err := db.SaveApp(app); err != nil { - fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed for save")) - return err + fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed to saving")) + fmt.Println(" " + cmd.Red(err.Error())) + continue } } - if err := runStart(app.ID); err != nil { + // todo options + proc := subProcess( + "-id="+strconv.Itoa(int(app.ID)), + "-pmt=restart", + "-sign="+xid.New().String(), + ) + if err := proc.Wait(); err != nil { fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed")) fmt.Println(" ", err.Error()) + } else { + fmt.Println(cmd.Yellow(app.Name), cmd.Green("success")) } } fmt.Println("over") + return nil }). @@ -150,8 +229,38 @@ func runCli() { Command("stop", "Stops a program"). Argument("", "file/name/id"). Action(func(c *cmd.Command, m map[string]any, a ...any) error { - fmt.Println("停止程序") - fmt.Println(a...) + if len(a) != 1 { + return c.ShowHelp() + } + var apps []*db.App + scripts := a[0].([]string) + for _, script := range scripts { + loads, err := db.ParseInput(script) + if err != nil { + return err + } + apps = append(apps, loads...) + } + if len(apps) == 0 { + fmt.Println("No app found") + return nil + } + for _, app := range apps { + if app.PID <= 0 { + fmt.Println(cmd.Yellow(app.Name), "not running") + return nil + } + proc := db.NewProc(app) + err := proc.Stop() + if err != nil { + fmt.Println(cmd.Yellow(app.Name), cmd.Red("failed to stop")) + fmt.Println(" " + cmd.Red(err.Error())) + return err + } + fmt.Println(cmd.Yellow(app.Name), cmd.Green("stopped")) + return nil + } + fmt.Println("over") return nil }). @@ -159,17 +268,8 @@ func runCli() { Run() } -func runStart(id uint) error { - proc := subProcess( - []string{os.Args[0], "-id", strconv.Itoa(int(id))}, - []string{"PMT_CURRENT_FUNCTION=start"}, - ) - return proc.Wait() -} - -func subProcess(args []string, env []string) *exec.Cmd { - cmd := exec.Command(args[0], args[1:]...) - cmd.Env = append(os.Environ(), env...) +func subProcess(args ...string) *exec.Cmd { + cmd := exec.Command(os.Args[0], args...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr