diff --git a/cmd/srun/cli.go b/cmd/srun/cli.go new file mode 100644 index 0000000..f282ead --- /dev/null +++ b/cmd/srun/cli.go @@ -0,0 +1,208 @@ +package main + +import ( + "bufio" + "errors" + "fmt" + log "github.com/sirupsen/logrus" + "github.com/vouv/srun/core" + "github.com/vouv/srun/model" + "github.com/vouv/srun/pkg/term" + "github.com/vouv/srun/store" + "io" + "os" + "runtime" + "strings" +) + +var ErrReadAccount = errors.New("读取账号文件错误, 请执行`srun config`配置账号信息") + +type Func func(cmd string, params ...string) + +var DefaultClient = &Client{} + +type Client struct{} + +var serverTypes = map[string]string{ + "xyw": model.ServerTypeOrigin, + "yd": model.ServerTypeCMCC, + "lt": model.ServerTypeWCDMA, +} + +// 登录 +func (s *Client) Login(cmd string, params ...string) { + account, gErr := store.ReadAccount() + if gErr != nil { + log.Error(ErrReadAccount.Error()) + log.Debug(gErr) + os.Exit(1) + } + + if len(params) != 0 { + if t, ok := serverTypes[params[0]]; ok { + account.Server = t + } else { + s.CmdList() + return + } + } + log.Info("尝试登录: ", account.Server) + + //username = model.AddSuffix(username, server) + info, err := core.Login(&account) + if err != nil { + log.Error(err) + os.Exit(1) + } + log.Info("登录成功!") + log.Info("在线IP: ", info.ClientIp) + + err = store.SetInfo(info.AccessToken, info.ClientIp) + if err != nil { + log.Error(err) + os.Exit(1) + } +} + +func (s *Client) Logout(cmd string, params ...string) { + if len(params) == 0 { + var err error + account, err := store.ReadAccount() + if err != nil { + log.Error(ErrReadAccount.Error()) + log.Debug(err) + os.Exit(1) + } + if err = core.Logout(account.Username); err != nil { + log.Error(err) + os.Exit(1) + } + log.Info("注销成功!") + } else { + s.CmdList() + } +} + +func (s *Client) GetInfo(cmd string, params ...string) { + if len(params) == 0 { + var err error + account, err := store.ReadAccount() + if err != nil { + log.Error(ErrReadAccount.Error()) + log.Debug(err) + os.Exit(1) + } + log.Info("当前校园网登录账号:", account.Username) + if err = core.Info(account); err != nil { + log.Error(err) + os.Exit(1) + } + } else { + s.CmdList() + } +} + +func (Client) SetAccount(cmd string, params ...string) { + + in := os.Stdin + fmt.Print("设置校园网账号:\n>") + username := readInput(in) + + // 终端API + fd, _ := term.GetFdInfo(in) + oldState, err := term.SaveState(fd) + if err != nil { + log.Error(err) + os.Exit(1) + } + fmt.Print("设置校园网密码:\n>") + + // read in stdin + _ = term.DisableEcho(fd, oldState) + pwd := readInput(in) + _ = term.RestoreTerminal(fd, oldState) + + fmt.Println() + +setServer: + fmt.Print("设置默认登录模式( 校园网(默认): 1 | 移动: 2 | 联通: 3 )\n>") + server := readInput(in) + switch server { + case "", "1": + server = model.ServerTypeOrigin + case "2": + server = model.ServerTypeCMCC + case "3": + server = model.ServerTypeWCDMA + default: + goto setServer + } + + // trim + username = strings.TrimSpace(username) + pwd = strings.TrimSpace(pwd) + + if err := store.SetAccount(username, pwd, server); err != nil { + log.Error(err) + os.Exit(1) + } + log.Info("账号密码已被保存") +} + +func readInput(in io.Reader) string { + reader := bufio.NewReader(in) + line, _, err := reader.ReadLine() + if err != nil { + panic(err) + } + return string(line) +} + +func (Client) ShowVersion() { + fmt.Println("System:") + fmt.Printf("\tOS:%s ARCH:%s GOVERSION:%s\n", runtime.GOOS, runtime.GOARCH, runtime.Version()) + fmt.Println("About:") + fmt.Printf("\tVersion:%s\n", Version) + fmt.Println("\n\t with ❤ By vouv") +} + +// srun help [COMMAND] +func (s *Client) CmdHelp(cmd string, params ...string) { + if len(params) == 0 { + fmt.Println(s.CmdList()) + } else { + if c, ok := cmdDocs[params[0]]; ok { + fmt.Println("Usage: ", c[0]) + } else { + fmt.Println(s.CmdList()) + } + } +} + +func (Client) CmdList() string { + sb := &strings.Builder{} + sb.WriteString(fmt.Sprint("\r\nUsage: srun [OPTIONS] COMMAND \r\n\r\n")) + + sb.WriteString("A efficient client for BIT campus network\r\n\r\n") + + sb.WriteString("Options:\r\n") + for k, v := range optionDocs { + sb.WriteString(fmt.Sprintf(" %-10s%-20s\r\n", k, v)) + } + + sb.WriteString("\r\nCommands:\r\n") + for k, v := range cmdDocs { + sb.WriteString(fmt.Sprintf(" %-10s%-20s\r\n", k, v[1])) + } + return sb.String() +} + +func (Client) Update(cmd string, params ...string) { + ok, v, d := HasUpdate() + if !ok { + log.Info("当前已是最新版本:", Version) + return + } + log.Info("发现新版本: ", v, "当前版本: ", Version) + log.Info("打开链接下载: ", d) +} diff --git a/cmd/srun/main.go b/cmd/srun/main.go new file mode 100755 index 0000000..79a8ef5 --- /dev/null +++ b/cmd/srun/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "flag" + log "github.com/sirupsen/logrus" + "net/http" + "os" + "time" +) + +const ( + Version = "v0.1.14" + Timeout = 3 * time.Second +) + +var LogLevel = log.InfoLevel + +var CommandMap = map[string]Func{ + "config": DefaultClient.SetAccount, + "login": DefaultClient.Login, + "logout": DefaultClient.Logout, + "info": DefaultClient.GetInfo, + "update": DefaultClient.Update, + + "help": DefaultClient.CmdHelp, +} + +var optionDocs = map[string]string{ + "-d": "Show debug message", + "-v": "Print version information and quit", + "-h": "Show help", +} + +var cmdDocs = map[string][]string{ + "config": {"srun config", "Set Username and Password"}, + "login": {"srun [login] [xyw|yd|lt]", "Login Srun"}, + "logout": {"srun logout", "Logout Srun"}, + "info": {"srun info", "Get Srun Info"}, + "update": {"srun update", "Update srun"}, +} + +func main() { + var debugMode bool + var helpMode bool + var versionMode bool + + flag.BoolVar(&debugMode, "d", false, "debug mode") + flag.BoolVar(&helpMode, "h", false, "show help") + flag.BoolVar(&versionMode, "v", false, "show version") + + flag.Parse() + + var cmd string + var params []string + + args := flag.Args() + if len(args) > 0 { + cmd = args[0] + params = args[1:] + } else { + cmd = "login" + } + + switch { + case helpMode: + DefaultClient.CmdHelp(cmd, args...) + return + case versionMode: + DefaultClient.ShowVersion() + return + case debugMode: + LogLevel = log.DebugLevel + } + + // config + http.DefaultClient.Timeout = Timeout + log.SetOutput(os.Stdout) + log.SetLevel(LogLevel) + log.SetFormatter(&log.TextFormatter{ + //DisableTimestamp: true, + TimestampFormat: "2006-01-02 15:04:05", + FullTimestamp: true, + }) + + if handle, ok := CommandMap[cmd]; ok { + handle(cmd, params...) + } else { + DefaultClient.CmdHelp(cmd, params...) + } + + // has update + // todo 修改更新逻辑, 减少更新频率 + //if ok, repo := cli.HasUpdate(); ok { + // fmt.Print("更新: " + repo) + // fmt.Println(" 当前版本: " + config.Version) + //} + +} diff --git a/cmd/srun/update.go b/cmd/srun/update.go new file mode 100644 index 0000000..66515f8 --- /dev/null +++ b/cmd/srun/update.go @@ -0,0 +1,47 @@ +package main + +import ( + "context" + log "github.com/sirupsen/logrus" + "net" + "net/http" + "strings" + "time" +) + +const repo = "https://github.com/vouv/srun/releases/latest" +const updateTimeout = 3 * time.Second + +var client = http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, e error) { + conn, err := net.DialTimeout(network, addr, updateTimeout) + if err != nil { + return nil, err + } + _ = conn.SetDeadline(time.Now().Add(updateTimeout)) + return conn, nil + }, +} + +func HasUpdate() (ok bool, version string, dist string) { + req, err := http.NewRequest("GET", repo, nil) + + if err != nil { + log.Debug("请求错误", err) + return + } + res, err := client.RoundTrip(req) + if err != nil { + log.Debug("请求错误", err) + return + } + dist = res.Header.Get("Location") + arr := strings.Split(dist, "/") + version = arr[len(arr)-1] + + log.Debug("最新版本", version) + + ok = version != Version + return + +}