186 lines
4.9 KiB
Go
186 lines
4.9 KiB
Go
package http
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"datarush/internal/auth/domain"
|
|
"datarush/internal/auth/service"
|
|
)
|
|
|
|
type SignUpRequest struct {
|
|
Email string `json:"email" binding:"required,email"`
|
|
Username string `json:"username" binding:"required"`
|
|
Password string `json:"password" binding:"required"`
|
|
}
|
|
|
|
type SignInRequest struct {
|
|
Email string `json:"email" binding:"required,email"`
|
|
Password string `json:"password" binding:"required"`
|
|
}
|
|
|
|
type TokenResponse struct {
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
type UserResponse struct {
|
|
ID string `json:"id"`
|
|
Email string `json:"email"`
|
|
Username string `json:"username"`
|
|
CreatedAt string `json:"created_at"`
|
|
}
|
|
|
|
type ErrorResponse struct {
|
|
Detail string `json:"detail"`
|
|
}
|
|
|
|
type AuthHandler struct {
|
|
authService *service.AuthService
|
|
}
|
|
|
|
func NewAuthHandler(authService *service.AuthService) *AuthHandler {
|
|
return &AuthHandler{
|
|
authService: authService,
|
|
}
|
|
}
|
|
|
|
func (h *AuthHandler) SignUp(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req SignUpRequest
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
log.Printf("read body error: %v", err)
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"})
|
|
return
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
if err := json.Unmarshal(body, &req); err != nil {
|
|
log.Printf("signup validation error: %v", err)
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"})
|
|
return
|
|
}
|
|
|
|
if req.Email == "" || req.Username == "" || req.Password == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"})
|
|
return
|
|
}
|
|
|
|
token, err := h.authService.SignUp(r.Context(), req.Email, req.Username, req.Password)
|
|
if err != nil {
|
|
log.Printf("signup error: %v", err)
|
|
switch err {
|
|
case domain.ErrUserAlreadyExists:
|
|
w.WriteHeader(http.StatusConflict)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "user already exists"})
|
|
default:
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "internal error"})
|
|
}
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusCreated)
|
|
json.NewEncoder(w).Encode(TokenResponse{Token: token})
|
|
}
|
|
|
|
func (h *AuthHandler) SignIn(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req SignInRequest
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
log.Printf("read body error: %v", err)
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"})
|
|
return
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
if err := json.Unmarshal(body, &req); err != nil {
|
|
log.Printf("signin validation error: %v", err)
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"})
|
|
return
|
|
}
|
|
|
|
if req.Email == "" || req.Password == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid request"})
|
|
return
|
|
}
|
|
|
|
token, err := h.authService.SignIn(r.Context(), req.Email, req.Password)
|
|
if err != nil {
|
|
log.Printf("signin error: %v", err)
|
|
switch err {
|
|
case domain.ErrInvalidPassword, domain.ErrUserNotFound:
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "invalid credentials"})
|
|
default:
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "internal error"})
|
|
}
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(TokenResponse{Token: token})
|
|
}
|
|
|
|
func (h *AuthHandler) GetMe(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
// Extract token from Authorization header
|
|
authHeader := r.Header.Get("Authorization")
|
|
if authHeader == "" {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "unauthorized"})
|
|
return
|
|
}
|
|
|
|
parts := strings.Split(authHeader, " ")
|
|
if len(parts) != 2 || parts[0] != "Bearer" {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "unauthorized"})
|
|
return
|
|
}
|
|
|
|
token := parts[1]
|
|
|
|
user, err := h.authService.ValidateToken(r.Context(), token)
|
|
if err != nil {
|
|
log.Printf("validate token error: %v", err)
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
json.NewEncoder(w).Encode(ErrorResponse{Detail: "unauthorized"})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(UserResponse{
|
|
ID: user.ID.String(),
|
|
Email: user.Email,
|
|
Username: user.Username,
|
|
CreatedAt: user.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
|
})
|
|
}
|