code.vegaprotocol.io/vega@v0.79.0/paths/file.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package paths
    17  
    18  import (
    19  	"bytes"
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"net/http"
    25  
    26  	vgcrypto "code.vegaprotocol.io/vega/libs/crypto"
    27  	vgfs "code.vegaprotocol.io/vega/libs/fs"
    28  
    29  	"github.com/BurntSushi/toml"
    30  )
    31  
    32  var (
    33  	ErrEmptyResponse        = errors.New("empty response")
    34  	ErrEmptyFile            = errors.New("empty file")
    35  	ErrContentLooksLikeHTML = errors.New("the content looks like it contains HTML, be sure your file has TOML formatting")
    36  	ErrContentLooksLikeJSON = errors.New("the content looks like it contains JSON, be sure your file has TOML formatting")
    37  )
    38  
    39  func FetchStructuredFile(url string, v interface{}) error {
    40  	resp, err := http.Get(url) //nolint:noctx
    41  	if err != nil {
    42  		return fmt.Errorf("couldn't load file: %w", err)
    43  	}
    44  	defer resp.Body.Close()
    45  
    46  	if resp.StatusCode != http.StatusOK {
    47  		return errors.New(http.StatusText(resp.StatusCode))
    48  	}
    49  
    50  	body, err := io.ReadAll(resp.Body)
    51  	if err != nil {
    52  		return fmt.Errorf("couldn't read HTTP response body: %w", err)
    53  	}
    54  
    55  	if len(body) == 0 {
    56  		return ErrEmptyResponse
    57  	}
    58  
    59  	if err := decodeTOMLFile(body, v); err != nil {
    60  		return err
    61  	}
    62  
    63  	return nil
    64  }
    65  
    66  func decodeTOMLFile(content []byte, v interface{}) error {
    67  	if content[0] == '<' {
    68  		return ErrContentLooksLikeHTML
    69  	}
    70  
    71  	if content[0] == '{' {
    72  		return ErrContentLooksLikeJSON
    73  	}
    74  
    75  	if _, err := toml.Decode(string(content), v); err != nil {
    76  		return fmt.Errorf("invalid TOML document: %w", err)
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  func ReadStructuredFile(path string, v interface{}) error {
    83  	buf, err := vgfs.ReadFile(path)
    84  	if err != nil {
    85  		return fmt.Errorf("couldn't read file: %w", err)
    86  	}
    87  
    88  	if len(buf) == 0 {
    89  		return ErrEmptyFile
    90  	}
    91  
    92  	if err := decodeTOMLFile(buf, v); err != nil {
    93  		return err
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func WriteStructuredFile(path string, v interface{}) error {
   100  	buf := new(bytes.Buffer)
   101  	if err := toml.NewEncoder(buf).Encode(v); err != nil {
   102  		return fmt.Errorf("couldn't encode to TOML: %w", err)
   103  	}
   104  
   105  	if err := vgfs.WriteFile(path, buf.Bytes()); err != nil {
   106  		return fmt.Errorf("couldn't write file: %w", err)
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  func ReadEncryptedFile(path string, passphrase string, v interface{}) error {
   113  	encryptedBuf, err := vgfs.ReadFile(path)
   114  	if err != nil {
   115  		return fmt.Errorf("couldn't read secure file: %w", err)
   116  	}
   117  
   118  	if len(encryptedBuf) == 0 {
   119  		return ErrEmptyFile
   120  	}
   121  
   122  	buf, err := vgcrypto.Decrypt(encryptedBuf, passphrase)
   123  	if err != nil {
   124  		return fmt.Errorf("couldn't decrypt content: %w", err)
   125  	}
   126  
   127  	err = json.Unmarshal(buf, v)
   128  	if err != nil {
   129  		return fmt.Errorf("couldn't unmarshal content: %w", err)
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  func WriteEncryptedFile(path string, passphrase string, v interface{}) error {
   136  	buf, err := json.Marshal(v)
   137  	if err != nil {
   138  		return fmt.Errorf("couldn't marshal content: %w", err)
   139  	}
   140  
   141  	encryptedBuf, err := vgcrypto.Encrypt(buf, passphrase)
   142  	if err != nil {
   143  		return fmt.Errorf("couldn't encrypt content: %w", err)
   144  	}
   145  
   146  	if err := vgfs.WriteFile(path, encryptedBuf); err != nil {
   147  		return fmt.Errorf("couldn't write secure file: %w", err)
   148  	}
   149  
   150  	return nil
   151  }