package credentials import ( "bufio" "encoding/json" "errors" "fmt" "os" "path/filepath" "strings" "golang.org/x/term" ) type ( credential struct { Username string `json:"username"` Password string `json:"password"` } credentialsStore struct { Store map[string]credential `json:"store"` } ) var ( datastorePath string ) func init() { roaming, err := os.UserConfigDir() if err != nil { panic("failed to get user config path: " + err.Error()) } datastorePath = filepath.Join(roaming, "cloudsave") } func Read(server string) (string, string, error) { var err error store, err := load() if err == nil { if c, ok := store[server]; ok { return c.Username, c.Password, nil } } fmt.Print("Enter username: ") reader := bufio.NewReader(os.Stdin) username, _ := reader.ReadString('\n') username = strings.TrimSpace(username) fmt.Printf("password for %s: ", username) password, err := term.ReadPassword(int(os.Stdin.Fd())) if err != nil { return "", "", err } fmt.Println() return username, string(password), nil } func Login(username, password, server string) error { store, err := load() if err != nil { return err } store[server] = credential{ Username: username, Password: password, } return save(store) } func Logout(server string) error { store, err := load() if err != nil { return err } delete(store, server) return save(store) } func save(store map[string]credential) error { c := credentialsStore{ Store: store, } f, err := os.OpenFile(filepath.Clean(filepath.Join(datastorePath, "credential.json")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { return fmt.Errorf("failed to open datastore: %w", err) } defer f.Close() e := json.NewEncoder(f) if err := e.Encode(c); err != nil { return fmt.Errorf("failed to encode data: %w", err) } return nil } func load() (map[string]credential, error) { f, err := os.OpenFile(filepath.Clean(filepath.Join(datastorePath, "credential.json")), os.O_RDONLY, 0) if err != nil { if errors.Is(err, os.ErrNotExist) { return make(map[string]credential), nil } return nil, fmt.Errorf("failed to open datastore: %w", err) } defer f.Close() var c credentialsStore d := json.NewDecoder(f) if err := d.Decode(&c); err != nil { return nil, fmt.Errorf("failed to decode data: %w", err) } return c.Store, nil }