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
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")
|
|
}
|
|
}
|
|
|