217 lines
7.4 KiB
Go
217 lines
7.4 KiB
Go
package contact
|
|
|
|
import (
|
|
"tm/pkg/hcaptcha"
|
|
"tm/pkg/response"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
// Handler handles HTTP requests for contact operations
|
|
type Handler struct {
|
|
service Service
|
|
hcaptchaVerifier hcaptcha.Verifier
|
|
}
|
|
|
|
// NewHandler creates a new contact handler
|
|
func NewHandler(service Service, hcaptchaVerifier hcaptcha.Verifier) *Handler {
|
|
return &Handler{
|
|
service: service,
|
|
hcaptchaVerifier: hcaptchaVerifier,
|
|
}
|
|
}
|
|
|
|
// Create handles creating a new contact message
|
|
// @Summary Create a new contact message
|
|
// @Description Create a new contact message from a user
|
|
// @Tags Contacts
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param contact body CreateContactForm true "Contact information"
|
|
// @Success 201 {object} response.APIResponse{data=ContactResponse}
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /api/v1/contacts [post]
|
|
func (h *Handler) Create(c echo.Context) error {
|
|
form, err := response.Parse[CreateContactForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
// Verify hCaptcha token
|
|
remoteIP := c.RealIP()
|
|
verificationResponse, err := h.hcaptchaVerifier.Verify(form.HCaptchaToken, remoteIP)
|
|
if err != nil {
|
|
return response.BadRequest(c, "Failed to verify hCaptcha token", "")
|
|
}
|
|
|
|
if !verificationResponse.Success {
|
|
return response.BadRequest(c, "hCaptcha verification failed", "")
|
|
}
|
|
|
|
// Create contact
|
|
contact, err := h.service.Create(c.Request().Context(), form)
|
|
if err != nil {
|
|
return response.InternalServerError(c, "Failed to create contact")
|
|
}
|
|
|
|
// Return public response (limited fields)
|
|
return response.Created(c, &PublicContactResponse{
|
|
ID: contact.ID,
|
|
Status: contact.Status,
|
|
CreatedAt: contact.CreatedAt,
|
|
}, "Contact message created successfully")
|
|
}
|
|
|
|
// GetByID handles retrieving a contact by ID (admin only)
|
|
// @Summary Get contact by ID
|
|
// @Description Retrieve a contact message by its ID (admin only)
|
|
// @Tags Admin-Contacts
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Contact ID"
|
|
// @Success 200 {object} response.APIResponse{data=ContactResponse}
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 404 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/contacts/{id} [get]
|
|
func (h *Handler) GetByID(c echo.Context) error {
|
|
id := c.Param("id")
|
|
|
|
contact, err := h.service.GetByID(c.Request().Context(), id)
|
|
if err != nil {
|
|
if err.Error() == "contact not found" {
|
|
return response.NotFound(c, "Contact not found")
|
|
}
|
|
return response.InternalServerError(c, "Failed to get contact")
|
|
}
|
|
|
|
return response.Success(c, contact, "Contact retrieved successfully")
|
|
}
|
|
|
|
// Search handles searching contacts with filters (admin only)
|
|
// @Summary Search contacts
|
|
// @Description Search contacts with filters and pagination (admin only)
|
|
// @Tags Admin-Contacts
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param q query string false "Search query"
|
|
// @Param status query string false "Contact status" Enums(pending, in_progress, resolved, closed)
|
|
// @Param date_from query int false "Date from (Unix timestamp)"
|
|
// @Param date_to query int false "Date to (Unix timestamp)"
|
|
// @Param limit query integer false "Number of contacts per page (1-100)" minimum(1) maximum(100) default(20)
|
|
// @Param offset query integer false "Number of contacts to skip for pagination" minimum(0) default(0)
|
|
// @Param sort_by query string false "Field to sort by" Enums(full_name, email, created_at, updated_at, status) default(created_at)
|
|
// @Param sort_order query string false "Sort order" Enums(asc, desc) default(desc)
|
|
// @Success 200 {object} response.APIResponse{data=ContactListResponse}
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/contacts [get]
|
|
func (h *Handler) Search(c echo.Context) error {
|
|
form, err := response.Parse[SearchContactsForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
pagination, err := response.NewPagination(c)
|
|
if err != nil {
|
|
return response.PaginationBadRequest(c, err)
|
|
}
|
|
|
|
result, err := h.service.Search(c.Request().Context(), form, pagination)
|
|
if err != nil {
|
|
if response.IsListPaginationError(err) {
|
|
return response.PaginationBadRequest(c, err)
|
|
}
|
|
return response.InternalServerError(c, "Failed to search contacts")
|
|
}
|
|
|
|
return response.SuccessWithMeta(c, result, result.Meta, "Contacts retrieved successfully")
|
|
}
|
|
|
|
// UpdateStatus handles updating contact status (admin only)
|
|
// @Summary Update contact status
|
|
// @Description Update the status of a contact message (admin only)
|
|
// @Tags Admin-Contacts
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Contact ID"
|
|
// @Param status body UpdateContactStatusForm true "Status update information"
|
|
// @Success 200 {object} response.APIResponse
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 404 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/contacts/{id}/status [put]
|
|
func (h *Handler) UpdateStatus(c echo.Context) error {
|
|
id := c.Param("id")
|
|
form, err := response.Parse[UpdateContactStatusForm](c)
|
|
if err != nil {
|
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
}
|
|
|
|
// Get admin ID from context (assuming it's set by auth middleware)
|
|
adminID := c.Get("user_id")
|
|
if adminID == nil {
|
|
return response.Unauthorized(c, "Admin authentication required")
|
|
}
|
|
|
|
// Update contact status
|
|
err = h.service.UpdateStatus(c.Request().Context(), id, form, adminID.(string))
|
|
if err != nil {
|
|
if err.Error() == "contact not found" {
|
|
return response.NotFound(c, "Contact not found")
|
|
}
|
|
return response.InternalServerError(c, "Failed to update contact status")
|
|
}
|
|
|
|
return response.Success(c, map[string]interface{}{
|
|
"message": "Contact status updated successfully",
|
|
}, "Contact status updated successfully")
|
|
}
|
|
|
|
// GetStats handles retrieving contact statistics (admin only)
|
|
// @Summary Get contact statistics
|
|
// @Description Get contact statistics by status (admin only)
|
|
// @Tags Admin-Contacts
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} response.APIResponse{data=ContactStats}
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/contacts/stats [get]
|
|
func (h *Handler) GetStats(c echo.Context) error {
|
|
stats, err := h.service.GetStats(c.Request().Context())
|
|
if err != nil {
|
|
return response.InternalServerError(c, "Failed to get contact statistics")
|
|
}
|
|
|
|
return response.Success(c, stats, "Contact statistics retrieved successfully")
|
|
}
|
|
|
|
// Delete handles deleting a contact (admin only)
|
|
// @Summary Delete contact
|
|
// @Description Delete a contact message by ID (admin only)
|
|
// @Tags Admin-Contacts
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "Contact ID"
|
|
// @Success 200 {object} response.APIResponse
|
|
// @Failure 400 {object} response.APIResponse
|
|
// @Failure 404 {object} response.APIResponse
|
|
// @Failure 500 {object} response.APIResponse
|
|
// @Router /admin/v1/contacts/{id} [delete]
|
|
func (h *Handler) Delete(c echo.Context) error {
|
|
id := c.Param("id")
|
|
|
|
err := h.service.Delete(c.Request().Context(), id)
|
|
if err != nil {
|
|
if err.Error() == "contact not found" {
|
|
return response.NotFound(c, "Contact not found")
|
|
}
|
|
return response.InternalServerError(c, "Failed to delete contact")
|
|
}
|
|
|
|
return response.Success(c, map[string]interface{}{
|
|
"message": "Contact deleted successfully",
|
|
}, "Contact deleted successfully")
|
|
}
|