Files
honeyDueAPI/internal/admin/handlers/auth_handler.go
Trey t 4976eafc6c Rebrand from Casera/MyCrib to honeyDue
Total rebrand across all Go API source files:
- Go module path: casera-api -> honeydue-api
- All imports updated (130+ files)
- Docker: containers, images, networks renamed
- Email templates: support email, noreply, icon URL
- Domains: casera.app/mycrib.treytartt.com -> honeyDue.treytartt.com
- Bundle IDs: com.tt.casera -> com.tt.honeyDue
- IAP product IDs updated
- Landing page, admin panel, config defaults
- Seeds, CI workflows, Makefile, docs
- Database table names preserved (no migration needed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 06:33:38 -06:00

135 lines
4.2 KiB
Go

package handlers
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/treytartt/honeydue-api/internal/config"
"github.com/treytartt/honeydue-api/internal/middleware"
"github.com/treytartt/honeydue-api/internal/models"
"github.com/treytartt/honeydue-api/internal/repositories"
)
// AdminAuthHandler handles admin authentication endpoints
type AdminAuthHandler struct {
adminRepo *repositories.AdminRepository
cfg *config.Config
}
// NewAdminAuthHandler creates a new admin auth handler
func NewAdminAuthHandler(adminRepo *repositories.AdminRepository, cfg *config.Config) *AdminAuthHandler {
return &AdminAuthHandler{
adminRepo: adminRepo,
cfg: cfg,
}
}
// LoginRequest represents the admin login request
type LoginRequest struct {
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required"`
}
// LoginResponse represents the admin login response
type LoginResponse struct {
Token string `json:"token"`
Admin AdminUserResponse `json:"admin"`
}
// AdminUserResponse represents an admin user in API responses
type AdminUserResponse struct {
ID uint `json:"id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Role models.AdminRole `json:"role"`
IsActive bool `json:"is_active"`
LastLogin *string `json:"last_login,omitempty"`
CreatedAt string `json:"created_at"`
}
// NewAdminUserResponse creates an AdminUserResponse from an AdminUser model
func NewAdminUserResponse(admin *models.AdminUser) AdminUserResponse {
resp := AdminUserResponse{
ID: admin.ID,
Email: admin.Email,
FirstName: admin.FirstName,
LastName: admin.LastName,
Role: admin.Role,
IsActive: admin.IsActive,
CreatedAt: admin.CreatedAt.Format("2006-01-02T15:04:05Z"),
}
if admin.LastLogin != nil {
lastLogin := admin.LastLogin.Format("2006-01-02T15:04:05Z")
resp.LastLogin = &lastLogin
}
return resp
}
// Login handles POST /api/admin/auth/login
func (h *AdminAuthHandler) Login(c echo.Context) error {
var req LoginRequest
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request: " + err.Error()})
}
// Find admin by email
admin, err := h.adminRepo.FindByEmail(req.Email)
if err != nil {
return c.JSON(http.StatusUnauthorized, map[string]interface{}{"error": "Invalid email or password"})
}
// Check password
if !admin.CheckPassword(req.Password) {
return c.JSON(http.StatusUnauthorized, map[string]interface{}{"error": "Invalid email or password"})
}
// Check if admin is active
if !admin.IsActive {
return c.JSON(http.StatusUnauthorized, map[string]interface{}{"error": "Account is disabled"})
}
// Generate JWT token
token, err := middleware.GenerateAdminToken(admin, h.cfg)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to generate token"})
}
// Update last login
_ = h.adminRepo.UpdateLastLogin(admin.ID)
// Refresh admin data after updating last login
admin, _ = h.adminRepo.FindByID(admin.ID)
return c.JSON(http.StatusOK, LoginResponse{
Token: token,
Admin: NewAdminUserResponse(admin),
})
}
// Logout handles POST /api/admin/auth/logout
// Note: JWT tokens are stateless, so logout is handled client-side by removing the token
func (h *AdminAuthHandler) Logout(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]interface{}{"message": "Logged out successfully"})
}
// Me handles GET /api/admin/auth/me
func (h *AdminAuthHandler) Me(c echo.Context) error {
admin := c.Get(middleware.AdminUserKey).(*models.AdminUser)
return c.JSON(http.StatusOK, NewAdminUserResponse(admin))
}
// RefreshToken handles POST /api/admin/auth/refresh
func (h *AdminAuthHandler) RefreshToken(c echo.Context) error {
admin := c.Get(middleware.AdminUserKey).(*models.AdminUser)
// Generate new token
token, err := middleware.GenerateAdminToken(admin, h.cfg)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to generate token"})
}
return c.JSON(http.StatusOK, map[string]interface{}{"token": token})
}