573 lines
19 KiB
Go
573 lines
19 KiB
Go
package controller
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"users_management/m/config"
|
|
"users_management/m/middleware"
|
|
"users_management/m/model/dto/req"
|
|
"users_management/m/model/dto/res"
|
|
"users_management/m/usecase"
|
|
"users_management/m/utils/common"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type DeviceDetailsController struct {
|
|
deviceDetailsUC usecase.DeviceDetailsUseCase
|
|
rg *gin.RouterGroup
|
|
cfg *config.Config // Add config field for middleware
|
|
}
|
|
|
|
func NewDeviceDetailsController(deviceDetailsUC usecase.DeviceDetailsUseCase, rg *gin.RouterGroup, cfg *config.Config) *DeviceDetailsController {
|
|
return &DeviceDetailsController{
|
|
deviceDetailsUC: deviceDetailsUC,
|
|
rg: rg,
|
|
cfg: cfg,
|
|
}
|
|
}
|
|
|
|
func (c *DeviceDetailsController) Route() {
|
|
deviceDetails := c.rg.Group("/device-details")
|
|
deviceDetails.Use(middleware.ConditionalRequireAnyRole(c.cfg, "Teknisi", "Admin", "Super Admin"))
|
|
{
|
|
deviceDetails.GET("", c.getAllDeviceDetails)
|
|
deviceDetails.POST("", c.createDeviceDetails)
|
|
deviceDetails.GET("/:id", c.getDeviceDetailsByID)
|
|
deviceDetails.PUT("/:id", c.updateDeviceDetails)
|
|
deviceDetails.DELETE("/:id", c.deleteDeviceDetails)
|
|
deviceDetails.POST("/:id/recalculate-ports", c.recalculatePortUsage)
|
|
deviceDetails.POST("/:id/assign-customer", c.assignCustomer)
|
|
deviceDetails.PUT("/:id/port-usage", c.updatePortUsage) // New endpoint
|
|
deviceDetails.PUT("/:id/port-assignments", c.updatePortAssignments) // New endpoint
|
|
|
|
deviceDetails.PUT("/:id/update-customer-by-port", c.updateCustomerByPort) // Updated endpoint name
|
|
deviceDetails.PUT("/:id/bulk-update-customers-by-port", c.bulkUpdateCustomersByPort) // Updated endpoint name
|
|
deviceDetails.DELETE("/:id/remove-customer-by-port", c.removeCustomerByPort)
|
|
|
|
// delete images by filename
|
|
deviceDetails.DELETE("/:id/images/:filename", c.deleteDeviceImage)
|
|
deviceDetails.GET("/without-towers", c.getDevicesWithoutTowers)
|
|
|
|
deviceDetails.GET("/without-connections", c.getDevicesWithoutConnections)
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
func (c *DeviceDetailsController) getDevicesWithoutConnections(ctx *gin.Context) {
|
|
// Get device types from query parameters (optional)
|
|
deviceTypesParam := ctx.Query("device_types")
|
|
var deviceTypes []string
|
|
|
|
if deviceTypesParam != "" {
|
|
// Split by comma if multiple types provided
|
|
deviceTypes = strings.Split(deviceTypesParam, ",")
|
|
// Trim whitespace and normalize case
|
|
for i, dt := range deviceTypes {
|
|
trimmed := strings.TrimSpace(dt)
|
|
if strings.ToUpper(trimmed) == "OTB" {
|
|
deviceTypes[i] = "OTB"
|
|
} else if strings.ToLower(trimmed) == "closure" {
|
|
deviceTypes[i] = "closure"
|
|
} else {
|
|
deviceTypes[i] = trimmed
|
|
}
|
|
}
|
|
}
|
|
|
|
devices, err := c.deviceDetailsUC.GetDevicesWithoutConnections(deviceTypes)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
// Categorize devices by type for better response structure
|
|
closureDevices := make([]res.DeviceDetailsResponse, 0)
|
|
otbDevices := make([]res.DeviceDetailsResponse, 0)
|
|
|
|
for _, device := range devices {
|
|
if device.DeviceType == "closure" {
|
|
closureDevices = append(closureDevices, device)
|
|
} else if device.DeviceType == "OTB" {
|
|
otbDevices = append(otbDevices, device)
|
|
}
|
|
}
|
|
|
|
response := gin.H{
|
|
"devices": devices,
|
|
"total": len(devices),
|
|
"breakdown": gin.H{
|
|
"closure": gin.H{
|
|
"count": len(closureDevices),
|
|
"devices": closureDevices,
|
|
},
|
|
"otb": gin.H{
|
|
"count": len(otbDevices),
|
|
"devices": otbDevices,
|
|
},
|
|
},
|
|
"filter": gin.H{
|
|
"device_types": deviceTypes,
|
|
"criteria": "without_fishbones_and_backbones",
|
|
},
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Devices without connections retrieved successfully", response)
|
|
}
|
|
func (c *DeviceDetailsController) getDevicesWithoutTowers(ctx *gin.Context) {
|
|
// Get device types from query parameters (optional)
|
|
deviceTypesParam := ctx.Query("device_types")
|
|
var deviceTypes []string
|
|
|
|
if deviceTypesParam != "" {
|
|
// Split by comma if multiple types provided
|
|
deviceTypes = strings.Split(deviceTypesParam, ",")
|
|
// Trim whitespace
|
|
for i, dt := range deviceTypes {
|
|
deviceTypes[i] = strings.TrimSpace(strings.ToUpper(dt))
|
|
}
|
|
}
|
|
|
|
devices, err := c.deviceDetailsUC.GetDevicesWithoutTowers(deviceTypes)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
response := gin.H{
|
|
"devices": devices,
|
|
"total": len(devices),
|
|
"filter": gin.H{
|
|
"device_types": deviceTypes,
|
|
"without_towers": true,
|
|
},
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Devices without towers retrieved successfully", response)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) deleteDeviceImage(ctx *gin.Context) {
|
|
// Parse device ID
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
// Get filename from URL parameter
|
|
filename := ctx.Param("filename")
|
|
if filename == "" {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Filename is required")
|
|
return
|
|
}
|
|
|
|
// Validate filename format (basic security check)
|
|
if strings.Contains(filename, "..") || strings.Contains(filename, "/") || strings.Contains(filename, "\\") {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid filename format")
|
|
return
|
|
}
|
|
|
|
// Call use case to delete the image
|
|
err = c.deviceDetailsUC.DeleteDeviceImage(deviceID, filename)
|
|
if err != nil {
|
|
if err.Error() == "device not found" {
|
|
common.ErrorResponses(ctx, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
if err.Error() == "image not found in device" {
|
|
common.ErrorResponses(ctx, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, fmt.Sprintf("Image %s deleted successfully", filename), nil)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) updateCustomerByPort(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
var request req.UpdateCustomerByPortDTO
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
err = c.deviceDetailsUC.UpdateCustomerByPort(deviceID, request)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
if request.NewCustomerName == nil {
|
|
common.SingleResponses(ctx, fmt.Sprintf("Customer removed from port %d successfully", request.PortNumber), nil)
|
|
} else {
|
|
common.SingleResponses(ctx, fmt.Sprintf("Customer assigned to port %d successfully", request.PortNumber), nil)
|
|
}
|
|
}
|
|
|
|
func (c *DeviceDetailsController) bulkUpdateCustomersByPort(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
var request req.BulkUpdateCustomersByPortDTO
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
err = c.deviceDetailsUC.BulkUpdateCustomersByPort(deviceID, request.Updates)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, fmt.Sprintf("%d port assignments updated successfully", len(request.Updates)), nil)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) removeCustomerByPort(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
var request req.RemoveCustomerByPortDTO
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
err = c.deviceDetailsUC.RemoveCustomerByPort(deviceID, request.PortNumber)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, fmt.Sprintf("Customer removed from port %d successfully", request.PortNumber), nil)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) assignCustomer(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
// Try to parse as array first
|
|
var arrayRequest []req.AssignMultipleCustomersDTO
|
|
|
|
// Try to parse as single object
|
|
var singleRequest struct {
|
|
CustomerName string `json:"customer_name" binding:"required"`
|
|
PortNumber *int `json:"port_number,omitempty"`
|
|
}
|
|
|
|
// Get raw JSON to determine the structure
|
|
rawData, err := ctx.GetRawData()
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid JSON data")
|
|
return
|
|
}
|
|
|
|
// Reset the request body for subsequent parsing
|
|
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(rawData))
|
|
|
|
// Check if it's an array by looking at the first character
|
|
trimmed := bytes.TrimSpace(rawData)
|
|
if len(trimmed) > 0 && trimmed[0] == '[' {
|
|
// It's an array
|
|
if err := json.Unmarshal(rawData, &arrayRequest); err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid array format: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Process array of assignments
|
|
err = c.deviceDetailsUC.AssignMultipleCustomersToPort(deviceID, arrayRequest)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, fmt.Sprintf("%d customers assigned successfully", len(arrayRequest)), nil)
|
|
} else {
|
|
// It's a single object
|
|
if err := json.Unmarshal(rawData, &singleRequest); err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid object format: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Process single assignment
|
|
err = c.deviceDetailsUC.AssignCustomerToPort(deviceID, singleRequest.CustomerName, singleRequest.PortNumber)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Customer assigned successfully", nil)
|
|
}
|
|
}
|
|
|
|
|
|
func (c *DeviceDetailsController) getAllDeviceDetails(ctx *gin.Context) {
|
|
devices, err := c.deviceDetailsUC.GetAllDeviceDetails()
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Device details retrieved successfully", devices)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) createDeviceDetails(ctx *gin.Context) {
|
|
var request req.DeviceDetailsDTO
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
err := c.deviceDetailsUC.CreateDeviceDetails(request)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Device details created successfully", nil)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) getDeviceDetailsByID(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
device, err := c.deviceDetailsUC.GetDeviceDetailsByID(deviceID)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusNotFound, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Device details retrieved successfully", device)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) updateDeviceDetails(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
contentType := ctx.GetHeader("Content-Type")
|
|
|
|
// Handle JSON request (no images)
|
|
if strings.Contains(contentType, "application/json") {
|
|
var request req.UpdateDeviceDetailsDTO
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
err = c.deviceDetailsUC.UpdateDeviceDetails(deviceID, request)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Device details updated successfully", nil)
|
|
return
|
|
}
|
|
|
|
// Handle multipart form request
|
|
err = ctx.Request.ParseMultipartForm(50 << 20) // 50MB for multiple images
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Failed to parse multipart form")
|
|
return
|
|
}
|
|
|
|
// Create update DTO from form data
|
|
deviceUpdateDTO := req.UpdateDeviceDetailsDTO{}
|
|
|
|
if deviceCode := ctx.PostForm("device_code"); deviceCode != "" {
|
|
deviceUpdateDTO.DeviceCode = &deviceCode
|
|
}
|
|
if deviceType := ctx.PostForm("device_type"); deviceType != "" {
|
|
deviceUpdateDTO.DeviceType = &deviceType
|
|
}
|
|
if longitudeStr := ctx.PostForm("longitude"); longitudeStr != "" {
|
|
if longitude, err := strconv.ParseFloat(longitudeStr, 64); err == nil {
|
|
deviceUpdateDTO.Longitude = &longitude
|
|
} else {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid longitude format")
|
|
return
|
|
}
|
|
}
|
|
if latitudeStr := ctx.PostForm("latitude"); latitudeStr != "" {
|
|
if latitude, err := strconv.ParseFloat(latitudeStr, 64); err == nil {
|
|
deviceUpdateDTO.Latitude = &latitude
|
|
} else {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid latitude format")
|
|
return
|
|
}
|
|
}
|
|
if portAmountStr := ctx.PostForm("port_amount"); portAmountStr != "" {
|
|
if portAmount, err := strconv.Atoi(portAmountStr); err == nil {
|
|
deviceUpdateDTO.PortAmount = &portAmount
|
|
} else {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid port amount format")
|
|
return
|
|
}
|
|
}
|
|
if status := ctx.PostForm("status"); status != "" {
|
|
deviceUpdateDTO.Status = &status
|
|
}
|
|
if region := ctx.PostForm("region"); region != "" {
|
|
deviceUpdateDTO.Region = ®ion
|
|
}
|
|
if province := ctx.PostForm("province"); province != "" {
|
|
deviceUpdateDTO.Province = &province
|
|
}
|
|
if city := ctx.PostForm("city"); city != "" {
|
|
deviceUpdateDTO.City = &city
|
|
}
|
|
if district := ctx.PostForm("district"); district != "" {
|
|
deviceUpdateDTO.District = &district
|
|
}
|
|
|
|
// Handle TowerID
|
|
if towerIDStr := ctx.PostForm("tower_id"); towerIDStr != "" {
|
|
if towerID, err := uuid.Parse(towerIDStr); err == nil {
|
|
deviceUpdateDTO.TowerID = &towerID
|
|
} else {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid tower ID format")
|
|
return
|
|
}
|
|
}
|
|
|
|
// Get multiple image files
|
|
form := ctx.Request.MultipartForm
|
|
imageFiles := form.File["images"] // Support multiple images
|
|
|
|
// Also support single image upload for backward compatibility
|
|
if len(imageFiles) == 0 {
|
|
if singleImage, err := ctx.FormFile("image"); err == nil {
|
|
imageFiles = []*multipart.FileHeader{singleImage}
|
|
}
|
|
}
|
|
|
|
// Handle replace_images flag
|
|
replaceImages := ctx.PostForm("replace_images") == "true"
|
|
|
|
err = c.deviceDetailsUC.UpdateDeviceDetailsWithMultipleImages(deviceID, deviceUpdateDTO, imageFiles, replaceImages)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Device details updated successfully", nil)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) deleteDeviceDetails(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
err = c.deviceDetailsUC.DeleteDeviceDetails(deviceID)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Device details deleted successfully", nil)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) recalculatePortUsage(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
err = c.deviceDetailsUC.RecalculatePortUsage(deviceID)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusInternalServerError, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Port usage recalculated successfully", nil)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) updatePortUsage(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
var request req.UpdatePortUsageDTO
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
err = c.deviceDetailsUC.UpdatePortUsage(deviceID, request.PortUsed)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Port usage updated successfully", nil)
|
|
}
|
|
|
|
func (c *DeviceDetailsController) updatePortAssignments(ctx *gin.Context) {
|
|
id := ctx.Param("id")
|
|
deviceID, err := uuid.Parse(id)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
|
return
|
|
}
|
|
|
|
var request req.UpdatePortAssignmentsDTO
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
err = c.deviceDetailsUC.UpdatePortAssignments(deviceID, request.PortAssignments)
|
|
if err != nil {
|
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
|
return
|
|
}
|
|
|
|
common.SingleResponses(ctx, "Port assignments updated successfully", nil)
|
|
} |