You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

66 lines
1.6 KiB

package handlers
import (
"database/sql"
"encoding/json"
"log/slog"
"net/http"
"os"
"time"
"golang.org/x/crypto/bcrypt"
"person-home/internal/auth"
"person-home/internal/models"
)
type AuthHandler struct {
DB *sql.DB
JWTSecret []byte
}
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
return
}
var req models.LoginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
var id int64
var username, hash string
err := h.DB.QueryRow("SELECT id, username, password_hash FROM users WHERE username = ?", req.Username).Scan(&id, &username, &hash)
if err == sql.ErrNoRows {
writeError(w, http.StatusUnauthorized, "invalid credentials")
return
}
if err != nil {
slog.Error("login query", "error", err)
writeError(w, http.StatusInternalServerError, "internal error")
return
}
if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(req.Password)); err != nil {
writeError(w, http.StatusUnauthorized, "invalid credentials")
return
}
token, expiresAt, err := auth.GenerateToken(username, h.JWTSecret, 24*time.Hour)
if err != nil {
slog.Error("generate token", "error", err)
writeError(w, http.StatusInternalServerError, "internal error")
return
}
writeJSON(w, http.StatusOK, models.LoginResponse{Token: token, ExpiresAt: expiresAt.Format(time.RFC3339)})
}
func init() {
if os.Getenv("ADMIN_PASSWORD") == "" {
slog.Warn("ADMIN_PASSWORD not set; admin login will not work")
}
}