package user import ( "database/sql" "errors" _ "github.com/mattn/go-sqlite3" "golang.org/x/crypto/bcrypt" ) type Manager struct { db *sql.DB } var ( ErrInvalidUsername = errors.New("invalid username") ErrInvalidPassword = errors.New("invalid password") ErrUserExists = errors.New("user already exists") ) func NewManager(dbPath string) (*Manager, error) { db, err := sql.Open("sqlite3", dbPath) if err != nil { return nil, err } _, err = db.Exec(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE, password TEXT ) `) if err != nil { return nil, err } return &Manager{db: db}, nil } func (m *Manager) Close() error { return m.db.Close() } func (m *Manager) Authenticate(username, password string) (bool, error) { var storedPassword string err := m.db.QueryRow("SELECT password FROM users WHERE username = ?", username).Scan(&storedPassword) if err != nil { if err == sql.ErrNoRows { return false, nil } return false, err } err = bcrypt.CompareHashAndPassword([]byte(storedPassword), []byte(password)) return err == nil, nil } func (m *Manager) CreateUser(username, password string) error { if err := validateUsername(username); err != nil { return err } if err := validatePassword(password); err != nil { return err } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return err } _, err = m.db.Exec("INSERT INTO users (username, password) VALUES (?, ?)", username, string(hashedPassword)) if err != nil { if err.Error() == "UNIQUE constraint failed: users.username" { return ErrUserExists } return err } return nil } func validateUsername(username string) error { if len(username) < 3 || len(username) > 20 { return ErrInvalidUsername } return nil } func validatePassword(password string) error { if len(password) < 8 { return ErrInvalidPassword } return nil }