package main import ( "bufio" "flag" "fmt" "io" "os" "strings" paneldb "git.lattuga.net/boyska/feedpanel/db" "github.com/go-pg/pg" "github.com/howeyc/gopass" ) type commandFunc func([]string) error var globalOptions struct { verbose bool pgOptions pg.Options } var cmdMap map[string]commandFunc func init() { cmdMap = map[string]commandFunc{ "batch": cmdBatch, "setup": cmdSetup, "adduser": cmdAddUser, "listusers": cmdListUsers, "showuser": cmdShowUser, } } func getDB() paneldb.DB { pgdb := pg.Connect(&globalOptions.pgOptions) db := paneldb.DB{PgDB: pgdb} return db } func cmdSetup(args []string) error { db := getDB() err := db.Setup() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if globalOptions.verbose { fmt.Println("Tables created") } os.Exit(0) return nil } func askPassword() (string, error) { fmt.Print("New password: ") pass, err := gopass.GetPasswd() if err != nil { return "", err } fmt.Print("Retype new password: ") pass2, err := gopass.GetPasswd() if err != nil { return "", err } if string(pass) != string(pass2) { return "", fmt.Errorf("Passwords differ") } return string(pass), nil } func cmdAddUser(args []string) error { flagset := flag.NewFlagSet(args[0], flag.ExitOnError) password := flagset.String("password", "", "set password from command line; INSECURE!") flagset.Parse(args[1:]) if len(flagset.Args()) < 2 { fmt.Fprintf(os.Stderr, "Usage: %s [options] USER EMAIL\n", args[0]) flagset.PrintDefaults() os.Exit(2) } user := flagset.Args()[0] email := flagset.Args()[1] if *password == "" { pass, err := askPassword() if err != nil { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } *password = string(pass) } db := getDB() err := db.UserAdd(user, email, *password) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if globalOptions.verbose { fmt.Printf("Success: user %s added\n", user) } os.Exit(0) return nil } func composeListUserOutput(user paneldb.User, usernameFlag, emailFlag, dateCreatedFlag bool) { var row string if usernameFlag { row += user.Name if emailFlag || dateCreatedFlag { row += "\t" } } if emailFlag { row += user.Email if dateCreatedFlag { row += "\t" } } if dateCreatedFlag { row += user.DateCreated } fmt.Fprintln(os.Stdout, row) } func cmdListUsers(args []string) error { flagset := flag.NewFlagSet(args[0], flag.ExitOnError) usernameFlag := flagset.Bool("no-nick", true, "skip printing usernames") emailFlag := flagset.Bool("no-email", true, "skip printing emails") dateCreatedFlag := flagset.Bool("created", false, "print date of user creation") headerFlag := flagset.Bool("no-header", false, "skip printing the header") flagset.Parse(args[1:]) db := getDB() users, err := db.GetUsers() if err != nil { fmt.Fprintln(os.Stderr, "Error:", err) os.Exit(1) } // Printing the header if !*headerFlag { header := paneldb.User{ Name: "username", Email: "email", DateCreated: "date_created", } separator := paneldb.User{ Name: "--------", Email: "-----", DateCreated: "------------", } composeListUserOutput(header, *usernameFlag, *emailFlag, *dateCreatedFlag) composeListUserOutput(separator, *usernameFlag, *emailFlag, *dateCreatedFlag) } // Printing the user list for _, user := range users { composeListUserOutput(user, *usernameFlag, *emailFlag, *dateCreatedFlag) } return nil } func cmdShowUser(args []string) error { flagset := flag.NewFlagSet(args[0], flag.ExitOnError) byEmail := flagset.String("email", "", "Search by email") byName := flagset.String("username", "", "Search by username") flagset.Parse(args[1:]) if *byEmail != "" && *byName != "" { fmt.Fprintln(os.Stderr, "-email and -username conflict") os.Exit(2) } if *byEmail == "" && *byName == "" { fmt.Fprintln(os.Stderr, "at least one of -email and -username must be specified") os.Exit(2) } db := getDB() var user paneldb.User var err error switch { case *byName != "": user, err = db.GetUserByName(*byName) case *byEmail != "": user, err = db.GetUserByEmail(*byEmail) } if err != nil { return err } fmt.Println(user.Details()) return nil } func usage(w io.Writer) { fmt.Fprintf(w, "Usage: %s SUBCOMMAND [command args...]\n", os.Args[0]) fmt.Fprintln(w, "\nSubcommands:") for cmdName := range cmdMap { fmt.Fprintln(w, " -", cmdName) } } func runLine(args []string) { cmdName := args[0] cmdFunc, ok := cmdMap[cmdName] if !ok { fmt.Fprintf(os.Stderr, "Invalid command `%s`\n", cmdName) usage(os.Stderr) os.Exit(2) } err := cmdFunc(args) if err != nil { fmt.Fprintln(os.Stderr, "Error") fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func cmdBatch(args []string) error { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { line := scanner.Text() runLine(strings.Split(line, " ")) } if scanner.Err() != nil { fmt.Fprintln(os.Stderr, "Error reading stdin:", scanner.Err()) os.Exit(1) } os.Exit(0) return nil } func main() { flag.StringVar(&globalOptions.pgOptions.User, "db-user", "panel", "The user to connect to database to") flag.StringVar(&globalOptions.pgOptions.Password, "db-pass", "", "database password") flag.StringVar(&globalOptions.pgOptions.Database, "db-name", "feeds", "database name") flag.StringVar(&globalOptions.pgOptions.Addr, "db-addr", "127.0.0.1:5432", "database network address") flag.BoolVar(&globalOptions.verbose, "verbose", false, "verbose output") flag.Parse() if len(flag.Args()) < 1 { fmt.Fprintln(os.Stderr, "Not enough arguments!") usage(os.Stderr) os.Exit(2) } runLine(flag.Args()) os.Exit(0) }