mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 11:02:54 +00:00
feat: Add tenant handler layer with CRUD, user management, API keys, and analytics endpoints
- Add TenantHandler struct with tenant service and logger fields - Add CreateTenant, GetTenant, GetTenantByDomain, UpdateTenant, DeleteTenant, and ListTenants endpoints - Add AddUserToTenant, GetTenantUser, UpdateTenantUser, RemoveUserFromTenant, and ListTenantUsers endpoints - Add CreateAPIKey, GetAPIKey, UpdateAPIKey, DeleteAPIKey, and ListAPIKeys endpoints with key string return on creation
This commit is contained in:
@@ -0,0 +1,510 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/tenant"
|
||||
)
|
||||
|
||||
// TenantHandler handles tenant-related HTTP requests
|
||||
type TenantHandler struct {
|
||||
tenantService tenant.Service
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
// NewTenantHandler creates a new tenant handler
|
||||
func NewTenantHandler(tenantService tenant.Service, logger *logrus.Logger) *TenantHandler {
|
||||
return &TenantHandler{
|
||||
tenantService: tenantService,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTenant handles tenant creation requests
|
||||
func (h *TenantHandler) CreateTenant(c *gin.Context) {
|
||||
var req tenant.CreateTenantRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.WithError(err).Error("Failed to bind tenant creation request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
tenant, err := h.tenantService.CreateTenant(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).Error("Failed to create tenant")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, tenant)
|
||||
}
|
||||
|
||||
// GetTenant handles tenant retrieval requests
|
||||
func (h *TenantHandler) GetTenant(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
tenant, err := h.tenantService.GetTenant(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", id).Error("Failed to get tenant")
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "tenant not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tenant)
|
||||
}
|
||||
|
||||
// GetTenantByDomain handles tenant retrieval by domain requests
|
||||
func (h *TenantHandler) GetTenantByDomain(c *gin.Context) {
|
||||
domain := c.Param("domain")
|
||||
if domain == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "domain is required"})
|
||||
return
|
||||
}
|
||||
|
||||
tenant, err := h.tenantService.GetTenantByDomain(c.Request.Context(), domain)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("domain", domain).Error("Failed to get tenant by domain")
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "tenant not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tenant)
|
||||
}
|
||||
|
||||
// UpdateTenant handles tenant update requests
|
||||
func (h *TenantHandler) UpdateTenant(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
var req tenant.UpdateTenantRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.WithError(err).Error("Failed to bind tenant update request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
tenant, err := h.tenantService.UpdateTenant(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", id).Error("Failed to update tenant")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tenant)
|
||||
}
|
||||
|
||||
// DeleteTenant handles tenant deletion requests
|
||||
func (h *TenantHandler) DeleteTenant(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.tenantService.DeleteTenant(c.Request.Context(), id); err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", id).Error("Failed to delete tenant")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// ListTenants handles tenant listing requests
|
||||
func (h *TenantHandler) ListTenants(c *gin.Context) {
|
||||
filter := &tenant.TenantFilter{
|
||||
Name: c.Query("name"),
|
||||
Domain: c.Query("domain"),
|
||||
Status: tenant.TenantStatus(c.Query("status")),
|
||||
Plan: tenant.TenantPlan(c.Query("plan")),
|
||||
SortBy: c.Query("sort_by"),
|
||||
SortOrder: c.Query("sort_order"),
|
||||
}
|
||||
|
||||
// Parse pagination parameters
|
||||
if limitStr := c.Query("limit"); limitStr != "" {
|
||||
if limit, err := strconv.Atoi(limitStr); err == nil {
|
||||
filter.Limit = limit
|
||||
}
|
||||
}
|
||||
if offsetStr := c.Query("offset"); offsetStr != "" {
|
||||
if offset, err := strconv.Atoi(offsetStr); err == nil {
|
||||
filter.Offset = offset
|
||||
}
|
||||
}
|
||||
|
||||
tenants, err := h.tenantService.ListTenants(c.Request.Context(), filter)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).Error("Failed to list tenants")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, tenants)
|
||||
}
|
||||
|
||||
// AddUserToTenant handles user addition to tenant requests
|
||||
func (h *TenantHandler) AddUserToTenant(c *gin.Context) {
|
||||
var req tenant.CreateTenantUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.WithError(err).Error("Failed to bind tenant user creation request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.tenantService.AddUserToTenant(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).Error("Failed to add user to tenant")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, user)
|
||||
}
|
||||
|
||||
// GetTenantUser handles tenant user retrieval requests
|
||||
func (h *TenantHandler) GetTenantUser(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
userID := c.Param("user_id")
|
||||
|
||||
if tenantID == "" || userID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID and user ID are required"})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.tenantService.GetTenantUser(c.Request.Context(), tenantID, userID)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"tenant_id": tenantID,
|
||||
"user_id": userID,
|
||||
}).Error("Failed to get tenant user")
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
// UpdateTenantUser handles tenant user update requests
|
||||
func (h *TenantHandler) UpdateTenantUser(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
userID := c.Param("user_id")
|
||||
|
||||
if tenantID == "" || userID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID and user ID are required"})
|
||||
return
|
||||
}
|
||||
|
||||
var req tenant.UpdateTenantUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.WithError(err).Error("Failed to bind tenant user update request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.tenantService.UpdateTenantUser(c.Request.Context(), tenantID, userID, &req)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"tenant_id": tenantID,
|
||||
"user_id": userID,
|
||||
}).Error("Failed to update tenant user")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
// RemoveUserFromTenant handles user removal from tenant requests
|
||||
func (h *TenantHandler) RemoveUserFromTenant(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
userID := c.Param("user_id")
|
||||
|
||||
if tenantID == "" || userID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID and user ID are required"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.tenantService.RemoveUserFromTenant(c.Request.Context(), tenantID, userID); err != nil {
|
||||
h.logger.WithError(err).WithFields(logrus.Fields{
|
||||
"tenant_id": tenantID,
|
||||
"user_id": userID,
|
||||
}).Error("Failed to remove user from tenant")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// ListTenantUsers handles tenant user listing requests
|
||||
func (h *TenantHandler) ListTenantUsers(c *gin.Context) {
|
||||
filter := &tenant.TenantUserFilter{
|
||||
TenantID: c.Param("tenant_id"),
|
||||
Email: c.Query("email"),
|
||||
Role: tenant.TenantRole(c.Query("role")),
|
||||
Status: tenant.TenantUserStatus(c.Query("status")),
|
||||
}
|
||||
|
||||
// Parse pagination parameters
|
||||
if limitStr := c.Query("limit"); limitStr != "" {
|
||||
if limit, err := strconv.Atoi(limitStr); err == nil {
|
||||
filter.Limit = limit
|
||||
}
|
||||
}
|
||||
if offsetStr := c.Query("offset"); offsetStr != "" {
|
||||
if offset, err := strconv.Atoi(offsetStr); err == nil {
|
||||
filter.Offset = offset
|
||||
}
|
||||
}
|
||||
|
||||
users, err := h.tenantService.ListTenantUsers(c.Request.Context(), filter)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).Error("Failed to list tenant users")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, users)
|
||||
}
|
||||
|
||||
// CreateAPIKey handles API key creation requests
|
||||
func (h *TenantHandler) CreateAPIKey(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
var req tenant.CreateAPIKeyRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.WithError(err).Error("Failed to bind API key creation request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
apiKey, keyString, err := h.tenantService.CreateAPIKey(c.Request.Context(), tenantID, &req)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", tenantID).Error("Failed to create API key")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Return API key and the actual key (only shown once)
|
||||
response := gin.H{
|
||||
"api_key": apiKey,
|
||||
"key": keyString, // Only returned on creation
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, response)
|
||||
}
|
||||
|
||||
// GetAPIKey handles API key retrieval requests
|
||||
func (h *TenantHandler) GetAPIKey(c *gin.Context) {
|
||||
id := c.Param("key_id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "API key ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
apiKey, err := h.tenantService.GetAPIKey(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("key_id", id).Error("Failed to get API key")
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "API key not found"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, apiKey)
|
||||
}
|
||||
|
||||
// UpdateAPIKey handles API key update requests
|
||||
func (h *TenantHandler) UpdateAPIKey(c *gin.Context) {
|
||||
id := c.Param("key_id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "API key ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
var req tenant.UpdateAPIKeyRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.WithError(err).Error("Failed to bind API key update request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
apiKey, err := h.tenantService.UpdateAPIKey(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("key_id", id).Error("Failed to update API key")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, apiKey)
|
||||
}
|
||||
|
||||
// DeleteAPIKey handles API key deletion requests
|
||||
func (h *TenantHandler) DeleteAPIKey(c *gin.Context) {
|
||||
id := c.Param("key_id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "API key ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.tenantService.DeleteAPIKey(c.Request.Context(), id); err != nil {
|
||||
h.logger.WithError(err).WithField("key_id", id).Error("Failed to delete API key")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
// ListAPIKeys handles API key listing requests
|
||||
func (h *TenantHandler) ListAPIKeys(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
apiKeys, err := h.tenantService.ListAPIKeys(c.Request.Context(), tenantID)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", tenantID).Error("Failed to list API keys")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, apiKeys)
|
||||
}
|
||||
|
||||
// GetUsageStats handles usage statistics requests
|
||||
func (h *TenantHandler) GetUsageStats(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
stats, err := h.tenantService.GetUsageStats(c.Request.Context(), tenantID)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", tenantID).Error("Failed to get usage stats")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, stats)
|
||||
}
|
||||
|
||||
// GetQuotaStatus handles quota status requests
|
||||
func (h *TenantHandler) GetQuotaStatus(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
quotaStatus, err := h.tenantService.GetQuotaStatus(c.Request.Context(), tenantID)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", tenantID).Error("Failed to get quota status")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, quotaStatus)
|
||||
}
|
||||
|
||||
// GetTenantConfig handles tenant configuration requests
|
||||
func (h *TenantHandler) GetTenantConfig(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.tenantService.GetTenantConfig(c.Request.Context(), tenantID)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", tenantID).Error("Failed to get tenant config")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, config)
|
||||
}
|
||||
|
||||
// UpdateTenantConfig handles tenant configuration update requests
|
||||
func (h *TenantHandler) UpdateTenantConfig(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
var config tenant.TenantConfig
|
||||
if err := c.ShouldBindJSON(&config); err != nil {
|
||||
h.logger.WithError(err).Error("Failed to bind tenant config request")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.tenantService.UpdateTenantConfig(c.Request.Context(), tenantID, &config); err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", tenantID).Error("Failed to update tenant config")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, config)
|
||||
}
|
||||
|
||||
// GetTenantMetrics handles tenant metrics requests
|
||||
func (h *TenantHandler) GetTenantMetrics(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
metrics, err := h.tenantService.GetTenantMetrics(c.Request.Context(), tenantID)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", tenantID).Error("Failed to get tenant metrics")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, metrics)
|
||||
}
|
||||
|
||||
// GetTenantEvents handles tenant events requests
|
||||
func (h *TenantHandler) GetTenantEvents(c *gin.Context) {
|
||||
tenantID := c.Param("tenant_id")
|
||||
if tenantID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
limit := 100 // default limit
|
||||
if limitStr := c.Query("limit"); limitStr != "" {
|
||||
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 {
|
||||
limit = l
|
||||
}
|
||||
}
|
||||
|
||||
events, err := h.tenantService.GetTenantEvents(c.Request.Context(), tenantID, limit)
|
||||
if err != nil {
|
||||
h.logger.WithError(err).WithField("tenant_id", tenantID).Error("Failed to get tenant events")
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, events)
|
||||
}
|
||||
Reference in New Issue
Block a user