317 lines
5.9 KiB
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
|
|
}
|