gosimplenpm/config/conf.go

317 lines
5.9 KiB
Go

package config
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"path"
"golang.org/x/crypto/bcrypt"
)
var (
// File to store the app configuration, like username, password, token, repo dir, logging level
ConfigFilePath string
// Repo dir
NpmRepoDir string
// HTTPListen
HTTPListen string
// Set Verbose Logging
CanLog bool
// Logging Level
LoggingLvl string
// Username
RegUser string
// Password
RegPwd string
)
type Config struct {
Token string `json:"token"`
RepoDir string `json:"repoDir"`
IpAddress string `json:"ipAddress"`
LogLevel string `json:"logLevel"`
}
func checkIfCorrectIPPort(s string) bool {
host, port, _ := net.SplitHostPort(s)
if host == "" || port == "" {
return false
}
if net.ParseIP(host) != nil {
return true
}
_, err := net.ResolveIPAddr("ip", host)
return err == nil
}
func VerifyConfig() error {
if !checkIfCorrectIPPort(HTTPListen) {
return errors.New("ip address should be in the format of <ip>:<port>")
}
// Check if config file exists
dirname, err := os.UserHomeDir()
if err != nil {
return err
}
configDirPath, err := checkOrCreateConfigDir(dirname, true)
if err != nil {
return err
}
ConfigFilePath = path.Join(configDirPath, "config.json")
if NpmRepoDir == "" {
NpmRepoDir = path.Join(dirname, ".gosimplenpm", "registry")
err := checkOrCreateRepoDir(NpmRepoDir)
if err != nil {
return err
}
} else if NpmRepoDir != "" {
err := checkOrCreateRepoDir(NpmRepoDir)
if err != nil {
return err
}
}
if CanLog {
LoggingLvl = "DEBUG"
fmt.Println("\n Enabled debug logging")
} else {
LoggingLvl = "INFO"
}
return nil
}
func checkOrCreateConfigDir(fp string, canCreate bool) (string, error) {
configDirPath := path.Join(fp, ".gosimplenpm", "config")
ok := isDir(configDirPath)
if !ok && canCreate {
err := os.MkdirAll(configDirPath, os.ModePerm)
if err != nil {
return "", err
}
}
if !ok && !canCreate {
return "", nil
}
return configDirPath, nil
}
func checkOrCreateRepoDir(repoDirPath string) error {
ok := isDir(repoDirPath)
if !ok {
err := os.MkdirAll(repoDirPath, os.ModePerm)
if err != nil {
return err
}
}
return nil
}
func createConfig(cfg *Config, recreate bool) error {
var scanner *bufio.Scanner
if recreate {
fmt.Println("\nNew config variables. Saving...")
} else {
fmt.Println("\nConfig file is not found. Creating...")
}
configFile, err := os.Create(ConfigFilePath)
if err != nil {
return err
}
defer configFile.Close()
cfg.IpAddress = HTTPListen
cfg.LogLevel = LoggingLvl
// Get username
if cfg.Token == "" {
fmt.Println("Enter your username: ")
scanner = bufio.NewScanner(os.Stdin)
scanner.Scan()
err = scanner.Err()
if err != nil {
return err
}
RegUser = scanner.Text()
fmt.Println("Enter your password: ")
scanner = bufio.NewScanner(os.Stdin)
scanner.Scan()
err = scanner.Err()
if err != nil {
return err
}
RegPwd = scanner.Text()
token, err := generateAuthToken()
if err != nil {
return err
}
cfg.Token = token
}
fmt.Printf("The npm authToken is %s.\n", cfg.Token)
err = json.NewEncoder(configFile).Encode(cfg)
if err != nil {
return err
}
return nil
}
func loadConfig(cfg *Config) error {
configFile, err := os.Open(ConfigFilePath)
if err != nil {
return err
}
defer configFile.Close()
err = json.NewDecoder(configFile).Decode(cfg)
if err != nil {
return err
}
return nil
}
func LoadOrCreateConfig(cfg *Config) error {
var err error
ok := isFile(ConfigFilePath)
// If file is not found
if !ok {
err = createConfig(cfg, false)
if err != nil {
return err
}
}
if ok {
// File is found
err = loadConfig(cfg)
if err != nil {
return err
}
if cfg.Token == "" || cfg.IpAddress != HTTPListen || cfg.LogLevel != LoggingLvl || cfg.RepoDir != NpmRepoDir {
// recreate the config file
err = createConfig(cfg, true)
if err != nil {
return err
}
}
}
return err
}
func isFile(fp string) bool {
info, err := os.Stat(fp)
if os.IsNotExist(err) || !info.Mode().IsRegular() {
return false
}
return true
}
func isDir(fp string) bool {
info, err := os.Stat(fp)
if os.IsNotExist(err) || !info.IsDir() {
return false
}
return true
}
// Hash password
func hashPassword(password string) (string, error) {
// Convert password string to byte slice
var passwordBytes = []byte(password)
// Hash password with Bcrypt's min cost
hashedPasswordBytes, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.MinCost)
return string(hashedPasswordBytes), err
}
// Check if two passwords match using Bcrypt's CompareHashAndPassword
// which return nil on success and an error on failure.
// func doPasswordsMatch(hashedPassword, currPassword string) bool {
// err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(currPassword))
// return err == nil
// }
func generateAuthToken() (string, error) {
hashed, err := hashPassword(RegPwd)
if err != nil {
return "", err
}
token := fmt.Sprintf("%s::%s", RegUser, hashed)
return token, nil
}
func PrintConfigFile() error {
// Check if config file exists
dirname, err := os.UserHomeDir()
if err != nil {
return err
}
configDirPath, err := checkOrCreateConfigDir(dirname, false)
if err != nil {
return err
}
if configDirPath == "" {
return errors.New("config dir is not found")
}
ConfigFilePath = path.Join(configDirPath, "config.json")
ok := isFile(ConfigFilePath)
if !ok {
return errors.New("config file is not found")
}
configFile, err := os.Open(ConfigFilePath)
if err != nil {
return err
}
defer configFile.Close()
b, err := io.ReadAll(configFile)
if err != nil {
return err
}
var result map[string]interface{}
err = json.Unmarshal([]byte(b), &result)
if err != nil {
return err
}
// Pretty-print the result
marshaled, err := json.MarshalIndent(result, "", " ")
if err != nil {
return err
}
fmt.Printf("Printing config located at %s: \n %s\n", ConfigFilePath, (marshaled))
return nil
}