1
0
Fork 0
forked from goffee/goffee

New goffee command, set the password for the user account with the specified name.

user:password [username] [newpassword] [dev|prod]
  username   - the name of the user to update
  password   - the new password to set
  env        - "dev" to use .env-dev, "prod" to use .env
This commit is contained in:
Zeni Kim 2026-05-26 10:12:50 -05:00
parent 807875da43
commit 06778a5357
6 changed files with 227 additions and 5 deletions

163
cmd/user-password.go Normal file
View file

@ -0,0 +1,163 @@
// Copyright (c) 2026 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/joho/godotenv"
"github.com/spf13/cobra"
"golang.org/x/crypto/bcrypt"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var UserPasswordCmd = &cobra.Command{
Use: "user:password [username] [newpassword] [dev|prod]",
Short: "Change a user password",
Long: `Set the password for the user account with the specified name.
Requires three arguments:
username - the name of the user to update
password - the new password to set
env - "dev" to use .env-dev, "prod" to use .env
Example:
goffee user:password someuser 'strongpassword' dev
goffee user:password admin 'newpass123' prod
`,
Args: cobra.ExactArgs(3),
Run: func(cmd *cobra.Command, args []string) {
username := args[0]
newPassword := args[1]
envMode := args[2]
// Determine which env file to load
var envFile string
switch envMode {
case "dev":
envFile = ".env-dev"
case "prod":
envFile = ".env"
default:
fmt.Printf("Error: environment must be 'dev' or 'prod', got '%s'\n", envMode)
return
}
// Build full path to env file
envPath := filepath.Join(".", envFile)
if _, err := os.Stat(envPath); os.IsNotExist(err) {
fmt.Printf("Error: environment file '%s' not found in current directory\n", envFile)
return
}
// Load env file
err := godotenv.Load(envPath)
if err != nil {
fmt.Printf("Error loading environment file '%s': %v\n", envFile, err)
return
}
// Connect to database
db, err := connectDatabase()
if err != nil {
fmt.Printf("Error connecting to database: %v\n", err)
return
}
// Find user by name
type User struct {
ID uint
Name string
Password string
Email string
}
var user User
result := db.Where("name = ?", username).First(&user)
if result.Error != nil {
if strings.Contains(result.Error.Error(), "record not found") {
fmt.Printf("Error: user '%s' not found\n", username)
} else {
fmt.Printf("Error finding user: %v\n", result.Error)
}
return
}
// Hash the new password
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
if err != nil {
fmt.Printf("Error hashing password: %v\n", err)
return
}
// Update password in database
result = db.Model(&user).Update("password", string(hashedPassword))
if result.Error != nil {
fmt.Printf("Error updating password: %v\n", result.Error)
return
}
fmt.Printf("Password updated successfully for user '%s'\n", username)
},
}
func init() {
rootCmd.AddCommand(UserPasswordCmd)
}
// connectDatabase establishes a database connection based on the loaded environment variables.
func connectDatabase() (*gorm.DB, error) {
dbDriver := os.Getenv("DB_DRIVER")
var dialector gorm.Dialector
switch dbDriver {
case "sqlite":
sqlitePath := os.Getenv("SQLITE_DB_PATH")
if sqlitePath == "" {
return nil, fmt.Errorf("SQLITE_DB_PATH environment variable not set")
}
dialector = sqlite.Open(sqlitePath)
case "mysql":
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local",
os.Getenv("MYSQL_USERNAME"),
os.Getenv("MYSQL_PASSWORD"),
os.Getenv("MYSQL_HOST"),
os.Getenv("MYSQL_PORT"),
os.Getenv("MYSQL_DB_NAME"),
os.Getenv("MYSQL_CHARSET"),
)
dialector = mysql.Open(dsn)
case "postgres":
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=%s TimeZone=%s",
os.Getenv("POSTGRES_HOST"),
os.Getenv("POSTGRES_USER"),
os.Getenv("POSTGRES_PASSWORD"),
os.Getenv("POSTGRES_DB_NAME"),
os.Getenv("POSTGRES_PORT"),
os.Getenv("POSTGRES_SSL_MODE"),
os.Getenv("POSTGRES_TIMEZONE"),
)
dialector = postgres.Open(dsn)
default:
return nil, fmt.Errorf("unsupported database driver: '%s'", dbDriver)
}
db, err := gorm.Open(dialector, &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
return nil, err
}
return db, nil
}