package handler import ( "net/http" "strconv" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/warmbly/warmbly/internal/api/middleware" "github.com/warmbly/warmbly/internal/errx" "github.com/warmbly/warmbly/internal/models" ) // User Management Handlers // AdminSearchUsers searches for users with pagination func (h *Handler) AdminSearchUsers(c *gin.Context) { var search models.AdminUserSearch if err := c.ShouldBindQuery(&search); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid query parameters")) return } result, xerr := h.AdminService.SearchUsers(c.Request.Context(), &search) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminGetUser gets a user's details func (h *Handler) AdminGetUser(c *gin.Context) { userID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } user, xerr := h.AdminService.GetUserDetail(c.Request.Context(), userID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, user) } // AdminGetUserPreview gets a full preview of a user's account func (h *Handler) AdminGetUserPreview(c *gin.Context) { userID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } preview, xerr := h.AdminService.GetUserPreview(c.Request.Context(), userID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, preview) } // AdminBanUser bans a user func (h *Handler) AdminBanUser(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } userID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } var req models.BanUserRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } xerr := h.AdminService.BanUser(c.Request.Context(), *adminID, userID, req.Reason, req.Scope, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "user banned successfully"}) } // AdminUnbanUser unbans a user func (h *Handler) AdminUnbanUser(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } userID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } var req models.UnbanUserRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } xerr := h.AdminService.UnbanUser(c.Request.Context(), *adminID, userID, req.Reason, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "user unbanned successfully"}) } // AdminGetUserBans gets the ban history for a user func (h *Handler) AdminGetUserBans(c *gin.Context) { userID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } bans, xerr := h.AdminService.GetUserBans(c.Request.Context(), userID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"bans": bans}) } // AdminGetUserCampaigns gets a user's campaigns func (h *Handler) AdminGetUserCampaigns(c *gin.Context) { userID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } cursor := parseCursor(c.Query("cursor")) limit := parseLimit(c.Query("limit"), 50) result, xerr := h.AdminService.GetUserCampaigns(c.Request.Context(), userID, cursor, limit) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminGetUserEmails gets a user's email accounts func (h *Handler) AdminGetUserEmails(c *gin.Context) { userID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } cursor := parseCursor(c.Query("cursor")) limit := parseLimit(c.Query("limit"), 50) emails, pagination, xerr := h.AdminService.GetUserEmails(c.Request.Context(), userID, cursor, limit) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"data": emails, "pagination": pagination}) } // AdminGetUserRateLimits gets a user's rate limits func (h *Handler) AdminGetUserRateLimits(c *gin.Context) { userID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } limits, xerr := h.AdminService.GetUserRateLimits(c.Request.Context(), userID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, limits) } // AdminUpdateUserRateLimits updates a user's rate limits func (h *Handler) AdminUpdateUserRateLimits(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } userID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } var req models.UpdateUserRateLimitsRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } xerr := h.AdminService.UpdateUserRateLimits(c.Request.Context(), *adminID, userID, &req, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "rate limits updated successfully"}) } // Worker Management Handlers // AdminListWorkers lists all workers func (h *Handler) AdminListWorkers(c *gin.Context) { cursor := parseCursor(c.Query("cursor")) limit := parseLimit(c.Query("limit"), 50) result, xerr := h.AdminService.ListWorkers(c.Request.Context(), cursor, limit) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminGetWorker gets a worker's details func (h *Handler) AdminGetWorker(c *gin.Context) { workerID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid worker ID")) return } worker, xerr := h.AdminService.GetWorkerDetail(c.Request.Context(), workerID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, worker) } // AdminUpdateWorker updates a worker func (h *Handler) AdminUpdateWorker(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } workerID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid worker ID")) return } var req models.AdminUpdateWorker if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } xerr := h.AdminService.UpdateWorker(c.Request.Context(), *adminID, workerID, &req, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "worker updated successfully"}) } // AdminGetWorkerEmails gets emails connected to a worker func (h *Handler) AdminGetWorkerEmails(c *gin.Context) { workerID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid worker ID")) return } cursor := parseCursor(c.Query("cursor")) limit := parseLimit(c.Query("limit"), 50) emails, pagination, xerr := h.AdminService.GetWorkerEmails(c.Request.Context(), workerID, cursor, limit) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"data": emails, "pagination": pagination}) } // AdminGetWorkerStats gets statistics for a worker func (h *Handler) AdminGetWorkerStats(c *gin.Context) { workerID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid worker ID")) return } stats, xerr := h.AdminService.GetWorkerStats(c.Request.Context(), workerID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, stats) } // AdminReassignEmails reassigns emails to a new worker func (h *Handler) AdminReassignEmails(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } workerID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid worker ID")) return } var req models.ReassignEmailsRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } // Use the workerID from the URL as the target xerr := h.AdminService.ReassignEmails(c.Request.Context(), *adminID, req.EmailIDs, workerID, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "emails reassigned successfully"}) } // Warmup Management Handlers // AdminListWarmupPools lists all warmup pools func (h *Handler) AdminListWarmupPools(c *gin.Context) { pools, xerr := h.AdminService.ListWarmupPools(c.Request.Context()) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"pools": pools}) } // AdminGetWarmupHealthSummary returns an aggregate health overview of all warmup pools func (h *Handler) AdminGetWarmupHealthSummary(c *gin.Context) { if h.WarmupService == nil { errx.JSON(c, errx.New(errx.Internal, "warmup service not available")) return } summary, xerr := h.WarmupService.GetPoolHealthSummary(c.Request.Context()) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, summary) } // AdminGetPoolParticipants gets participants in a warmup pool func (h *Handler) AdminGetPoolParticipants(c *gin.Context) { poolType := c.Param("type") cursor := parseCursor(c.Query("cursor")) limit := parseLimit(c.Query("limit"), 50) result, xerr := h.AdminService.GetPoolParticipants(c.Request.Context(), poolType, cursor, limit) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminListBlockedAccounts lists blocked warmup accounts func (h *Handler) AdminListBlockedAccounts(c *gin.Context) { cursor := parseCursor(c.Query("cursor")) limit := parseLimit(c.Query("limit"), 50) result, xerr := h.AdminService.ListBlockedAccounts(c.Request.Context(), cursor, limit) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminBlockAccount blocks an account from warmup func (h *Handler) AdminBlockAccount(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } accountID, err := uuid.Parse(c.Param("accountId")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid account ID")) return } var req models.BlockAccountRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } xerr := h.AdminService.BlockAccount(c.Request.Context(), *adminID, accountID, req.Reason, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "account blocked successfully"}) } // AdminUnblockAccount unblocks an account from warmup func (h *Handler) AdminUnblockAccount(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } accountID, err := uuid.Parse(c.Param("accountId")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid account ID")) return } xerr := h.AdminService.UnblockAccount(c.Request.Context(), *adminID, accountID, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "account unblocked successfully"}) } // Appeal Handlers // AdminListAppeals lists warmup appeals func (h *Handler) AdminListAppeals(c *gin.Context) { status := c.Query("status") cursor := parseCursor(c.Query("cursor")) limit := parseLimit(c.Query("limit"), 50) result, xerr := h.AdminService.ListAppeals(c.Request.Context(), status, cursor, limit) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminGetAppeal gets a specific appeal func (h *Handler) AdminGetAppeal(c *gin.Context) { appealID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid appeal ID")) return } appeal, xerr := h.AdminService.GetAppeal(c.Request.Context(), appealID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, appeal) } // AdminApproveAppeal approves a warmup appeal func (h *Handler) AdminApproveAppeal(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } appealID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid appeal ID")) return } var req models.ReviewAppealRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.ErrInvalid) return } xerr := h.AdminService.ReviewAppeal(c.Request.Context(), *adminID, appealID, true, req.Notes, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "appeal approved successfully"}) } // AdminRejectAppeal rejects a warmup appeal func (h *Handler) AdminRejectAppeal(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } appealID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid appeal ID")) return } var req models.ReviewAppealRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.ErrInvalid) return } xerr := h.AdminService.ReviewAppeal(c.Request.Context(), *adminID, appealID, false, req.Notes, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "appeal rejected"}) } // Campaign Management Handlers // AdminSearchCampaigns searches for campaigns func (h *Handler) AdminSearchCampaigns(c *gin.Context) { var search models.AdminCampaignSearch if err := c.ShouldBindQuery(&search); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid query parameters")) return } result, xerr := h.AdminService.SearchCampaigns(c.Request.Context(), &search) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminGetCampaign gets a campaign's details func (h *Handler) AdminGetCampaign(c *gin.Context) { campaignID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid campaign ID")) return } campaign, xerr := h.AdminService.GetCampaignDetail(c.Request.Context(), campaignID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, campaign) } // AdminStopCampaign force-stops a campaign func (h *Handler) AdminStopCampaign(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } campaignID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid campaign ID")) return } xerr := h.AdminService.StopCampaign(c.Request.Context(), *adminID, campaignID, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "campaign stopped successfully"}) } // Analytics Handlers // AdminGetPlatformOverview gets platform overview statistics func (h *Handler) AdminGetPlatformOverview(c *gin.Context) { overview, xerr := h.AdminService.GetPlatformOverview(c.Request.Context()) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, overview) } // AdminGetAnalyticsTrends gets analytics trend data func (h *Handler) AdminGetAnalyticsTrends(c *gin.Context) { trends, xerr := h.AdminService.GetAnalyticsTrends(c.Request.Context()) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, trends) } // AdminGetDailyEmailStats gets daily email statistics func (h *Handler) AdminGetDailyEmailStats(c *gin.Context) { startDate, endDate := parseDateRange(c) stats, xerr := h.AdminService.GetDailyEmailStats(c.Request.Context(), startDate, endDate) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"stats": stats}) } // AdminGetHourlyEmailStats gets hourly email statistics func (h *Handler) AdminGetHourlyEmailStats(c *gin.Context) { dateStr := c.Query("date") date := time.Now() if dateStr != "" { if parsed, err := time.Parse("2006-01-02", dateStr); err == nil { date = parsed } } stats, xerr := h.AdminService.GetHourlyEmailStats(c.Request.Context(), date) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"stats": stats}) } // AdminGetWorkerLoadStats gets worker load statistics func (h *Handler) AdminGetWorkerLoadStats(c *gin.Context) { stats, xerr := h.AdminService.GetWorkerLoadStats(c.Request.Context()) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"stats": stats}) } // AdminGetEmailDistribution gets email distribution across workers func (h *Handler) AdminGetEmailDistribution(c *gin.Context) { dist, xerr := h.AdminService.GetEmailDistribution(c.Request.Context()) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"distribution": dist}) } // AdminGetUserGrowthStats gets user growth statistics func (h *Handler) AdminGetUserGrowthStats(c *gin.Context) { startDate, endDate := parseDateRange(c) stats, xerr := h.AdminService.GetUserGrowthStats(c.Request.Context(), startDate, endDate) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"stats": stats}) } // Plan Management Handlers // AdminListPlans lists the plan catalog with the shared faceted query params; // returns the standard {data, pagination} envelope. func (h *Handler) AdminListPlans(c *gin.Context) { var search models.AdminPlanSearch if err := c.ShouldBindQuery(&search); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid query parameters")) return } // duration is durations.title (free text, no DB enum) — validate app-side. if search.Duration != "" && search.Duration != "month" && search.Duration != "year" { errx.JSON(c, errx.New(errx.BadRequest, "invalid duration")) return } result, xerr := h.AdminService.SearchPlansForAdmin(c.Request.Context(), &search) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminCreatePlan creates a new plan func (h *Handler) AdminCreatePlan(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } var req models.CreatePlanRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } plan, xerr := h.AdminService.CreatePlan(c.Request.Context(), *adminID, &req, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusCreated, plan) } // AdminGetPlan gets a plan by ID func (h *Handler) AdminGetPlan(c *gin.Context) { planID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid plan ID")) return } plan, xerr := h.AdminService.GetPlan(c.Request.Context(), planID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, plan) } // AdminUpdatePlan updates a plan func (h *Handler) AdminUpdatePlan(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } planID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid plan ID")) return } var req models.UpdatePlanRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } plan, xerr := h.AdminService.UpdatePlan(c.Request.Context(), *adminID, planID, &req, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, plan) } // AdminDeletePlan deletes a plan func (h *Handler) AdminDeletePlan(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } planID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid plan ID")) return } xerr := h.AdminService.DeletePlan(c.Request.Context(), *adminID, planID, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "plan deleted successfully"}) } // Enterprise Inquiry Handlers // AdminListEnterpriseInquiries lists enterprise inquiries with the shared // faceted query params; returns the standard {data, pagination} envelope. func (h *Handler) AdminListEnterpriseInquiries(c *gin.Context) { var search models.AdminEnterpriseInquirySearch if err := c.ShouldBindQuery(&search); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid query parameters")) return } result, xerr := h.AdminService.ListEnterpriseInquiries(c.Request.Context(), &search) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminGetEnterpriseInquiry gets a specific enterprise inquiry func (h *Handler) AdminGetEnterpriseInquiry(c *gin.Context) { inquiryID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid inquiry ID")) return } inquiry, xerr := h.AdminService.GetEnterpriseInquiry(c.Request.Context(), inquiryID) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, inquiry) } // AdminUpdateEnterpriseInquiry updates an enterprise inquiry func (h *Handler) AdminUpdateEnterpriseInquiry(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } inquiryID, err := uuid.Parse(c.Param("id")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid inquiry ID")) return } var req models.UpdateEnterpriseInquiryRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } xerr := h.AdminService.UpdateEnterpriseInquiry(c.Request.Context(), *adminID, inquiryID, &req, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "inquiry updated successfully"}) } // Admin Management Handlers // AdminListAdmins lists all admin users func (h *Handler) AdminListAdmins(c *gin.Context) { cursor := parseCursor(c.Query("cursor")) limit := parseLimit(c.Query("limit"), 50) result, xerr := h.AdminService.ListAdmins(c.Request.Context(), cursor, limit) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminGrantPermissions grants admin permissions to a user func (h *Handler) AdminGrantPermissions(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } userID, err := uuid.Parse(c.Param("userId")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } var req models.GrantAdminRequest if err := c.ShouldBindJSON(&req); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid request body")) return } xerr := h.AdminService.GrantAdminPermissions(c.Request.Context(), *adminID, userID, req.Permissions, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "admin permissions granted successfully"}) } // AdminRevokePermissions revokes admin permissions from a user func (h *Handler) AdminRevokePermissions(c *gin.Context) { adminID := middleware.GetAdminUserID(c) if adminID == nil { errx.JSON(c, errx.ErrUnauthorized) return } userID, err := uuid.Parse(c.Param("userId")) if err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid user ID")) return } xerr := h.AdminService.RevokeAdminPermissions(c.Request.Context(), *adminID, userID, c.ClientIP(), c.GetHeader("User-Agent")) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, gin.H{"message": "admin permissions revoked successfully"}) } // AdminSearchAuditLogs searches audit logs func (h *Handler) AdminSearchAuditLogs(c *gin.Context) { var search models.AdminAuditLogSearch if err := c.ShouldBindQuery(&search); err != nil { errx.JSON(c, errx.New(errx.BadRequest, "invalid query parameters")) return } result, xerr := h.AdminService.SearchAuditLogs(c.Request.Context(), &search) if xerr != nil { errx.JSON(c, xerr) return } c.JSON(http.StatusOK, result) } // AdminGetPermissionList returns the list of available admin permissions func (h *Handler) AdminGetPermissionList(c *gin.Context) { permissions := models.GetAllPermissionInfos() c.JSON(http.StatusOK, gin.H{"permissions": permissions}) } // Helper functions func parseCursor(s string) *uuid.UUID { if s == "" { return nil } id, err := uuid.Parse(s) if err != nil { return nil } return &id } func parseLimit(s string, defaultLimit int) int { if s == "" { return defaultLimit } limit, err := strconv.Atoi(s) if err != nil || limit <= 0 { return defaultLimit } if limit > 100 { return 100 } return limit } func parseDateRange(c *gin.Context) (time.Time, time.Time) { now := time.Now() endDate := now startDate := now.AddDate(0, 0, -30) // Default to last 30 days if s := c.Query("start_date"); s != "" { if t, err := time.Parse("2006-01-02", s); err == nil { startDate = t } } if s := c.Query("end_date"); s != "" { if t, err := time.Parse("2006-01-02", s); err == nil { endDate = t } } return startDate, endDate }