package main import ( "bufio" "database/sql" "flag" "fmt" "log" "os" "golang.org/x/crypto/ssh/terminal" _ "modernc.org/sqlite" "github.com/autobrr/autobrr/internal/database" "github.com/autobrr/autobrr/internal/domain" "github.com/autobrr/autobrr/pkg/argon2id" ) const usage = `usage: autobrrctl --config path create-user Create user help Show this help message ` func init() { flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), usage) } } func main() { var configPath string flag.StringVar(&configPath, "config", "", "path to configuration file") flag.Parse() if configPath == "" { log.Fatal("--config required") } // if configPath is set then put database inside that path, otherwise create wherever it's run var dataSource = database.DataSourceName(configPath, "autobrr.db") // open database connection db, err := sql.Open("sqlite", dataSource) if err != nil { log.Fatalf("failed to open database: %v", err) } defer db.Close() if err = database.Migrate(db); err != nil { log.Fatalf("could not migrate db: %v", err) } userRepo := database.NewUserRepo(db) switch cmd := flag.Arg(0); cmd { case "create-user": username := flag.Arg(1) if username == "" { flag.Usage() os.Exit(1) } password, err := readPassword() if err != nil { log.Fatalf("failed to read password: %v", err) } hashed, err := argon2id.CreateHash(string(password), argon2id.DefaultParams) if err != nil { log.Fatalf("failed to hash password: %v", err) } user := domain.User{ Username: username, Password: hashed, } if err := userRepo.Store(user); err != nil { log.Fatalf("failed to create user: %v", err) } default: flag.Usage() if cmd != "help" { os.Exit(1) } } } func readPassword() ([]byte, error) { var password []byte var err error fd := int(os.Stdin.Fd()) if terminal.IsTerminal(fd) { fmt.Printf("Password: ") password, err = terminal.ReadPassword(int(os.Stdin.Fd())) if err != nil { return nil, err } fmt.Printf("\n") } else { fmt.Fprintf(os.Stderr, "warning: Reading password from stdin.\n") scanner := bufio.NewScanner(os.Stdin) if !scanner.Scan() { if err := scanner.Err(); err != nil { log.Fatalf("failed to read password from stdin: %v", err) } log.Fatalf("failed to read password from stdin: stdin is empty %v", err) } password = scanner.Bytes() if len(password) == 0 { return nil, fmt.Errorf("zero length password") } } return password, nil }