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>
135 lines
4.2 KiB
Go
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})
|
|
}
|