387 lines
10 KiB
Go
387 lines
10 KiB
Go
package controllers
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
"doormile/db"
|
|
"doormile/dto"
|
|
"doormile/models"
|
|
"doormile/utils"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
)
|
|
|
|
func RegisterClient(c *fiber.Ctx) error {
|
|
var input dto.CreateClientRequest
|
|
if err := c.BodyParser(&input); err != nil {
|
|
return utils.BadRequest(c, "invalid request body")
|
|
}
|
|
|
|
if input.FirstName == "" || input.Phone == "" {
|
|
return utils.BadRequest(c, "first_name and phone are required")
|
|
}
|
|
|
|
if input.RegistrationSource == "" {
|
|
input.RegistrationSource = DetermineSource(string(c.Request().Header.UserAgent()))
|
|
}
|
|
if input.DataConsent == "" {
|
|
input.DataConsent = "full"
|
|
}
|
|
if input.Status == "" {
|
|
input.Status = "newClient"
|
|
}
|
|
|
|
createAuth := input.Email != "" && input.Password != ""
|
|
|
|
var hashedPassword string
|
|
if createAuth {
|
|
var err error
|
|
hashedPassword, err = utils.HashPassword(input.Password)
|
|
if err != nil {
|
|
return utils.Internal(c, "failed to process registration")
|
|
}
|
|
}
|
|
|
|
tx := db.DB.Begin()
|
|
|
|
var existingClient models.DoormileClient
|
|
if tx.Where("phone = ?", input.Phone).First(&existingClient).Error == nil {
|
|
tx.Rollback()
|
|
return utils.Conflict(c, "a client with this phone number already exists")
|
|
}
|
|
|
|
if createAuth {
|
|
var existingAuth models.DoormileAuth
|
|
if tx.Where("email = ?", input.Email).First(&existingAuth).Error == nil {
|
|
tx.Rollback()
|
|
return utils.Conflict(c, "this email address is already registered")
|
|
}
|
|
}
|
|
|
|
client := models.DoormileClient{
|
|
FirstName: input.FirstName,
|
|
LastName: input.LastName,
|
|
Phone: input.Phone,
|
|
Address: input.Address,
|
|
City: input.City,
|
|
State: input.State,
|
|
Neighbourhood: input.Neighbourhood,
|
|
Pincode: input.Pincode,
|
|
SurveyLat: input.SurveyLat,
|
|
SurveyLong: input.SurveyLong,
|
|
SurveyAddress: input.SurveyAddress,
|
|
SurveyZone: input.SurveyZone,
|
|
SurveyPincode: input.SurveyPincode,
|
|
BusinessType: input.BusinessType,
|
|
Status: input.Status,
|
|
ShippingFrequency: input.ShippingFrequency,
|
|
LogisticsSegment: input.LogisticsSegment,
|
|
TransitFrom: input.TransitFrom,
|
|
TransitTo: input.TransitTo,
|
|
DataConsent: input.DataConsent,
|
|
RegistrationSource: input.RegistrationSource,
|
|
RegisteredByID: input.RegisteredByID,
|
|
}
|
|
|
|
if input.DataConsent == "full" {
|
|
client.ParcelVolume = input.ParcelVolume
|
|
client.ActiveContracts = input.ActiveContracts
|
|
client.LogisticsProvider = input.LogisticsProvider
|
|
client.ProviderEfficiency = input.ProviderEfficiency
|
|
client.Notes = input.Notes
|
|
} else {
|
|
client.ParcelVolume = 0
|
|
client.ActiveContracts = 0
|
|
client.LogisticsProvider = "Not disclosed"
|
|
client.ProviderEfficiency = ""
|
|
client.Notes = ""
|
|
}
|
|
|
|
if err := tx.Create(&client).Error; err != nil {
|
|
tx.Rollback()
|
|
return utils.Internal(c, "failed to create client")
|
|
}
|
|
|
|
email, role := "", ""
|
|
if createAuth {
|
|
auth := models.DoormileAuth{
|
|
ClientID: &client.ID,
|
|
Email: input.Email,
|
|
PasswordHash: hashedPassword,
|
|
Role: "user",
|
|
}
|
|
if err := tx.Create(&auth).Error; err != nil {
|
|
tx.Rollback()
|
|
return utils.Internal(c, "failed to create auth credentials")
|
|
}
|
|
email = auth.Email
|
|
role = auth.Role
|
|
}
|
|
|
|
tx.Commit()
|
|
|
|
return utils.Created(c, buildClientResponse(client, email, role))
|
|
}
|
|
|
|
func GetClients(c *fiber.Ctx) error {
|
|
var clients []models.DoormileClient
|
|
if err := db.DB.Find(&clients).Error; err != nil {
|
|
return utils.Internal(c, "failed to fetch clients")
|
|
}
|
|
|
|
// Bulk load auth records once and map by client_id for O(1) lookup
|
|
var auths []models.DoormileAuth
|
|
db.DB.Find(&auths)
|
|
authByClientID := make(map[uint64]models.DoormileAuth, len(auths))
|
|
for _, a := range auths {
|
|
if a.ClientID != nil {
|
|
authByClientID[*a.ClientID] = a
|
|
}
|
|
}
|
|
|
|
responses := make([]dto.ClientResponse, 0, len(clients))
|
|
for _, client := range clients {
|
|
auth := authByClientID[client.ID]
|
|
responses = append(responses, buildClientResponse(client, auth.Email, auth.Role))
|
|
}
|
|
|
|
return utils.List(c, responses, int64(len(responses)))
|
|
}
|
|
|
|
func GetClientDetails(c *fiber.Ctx) error {
|
|
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
return utils.BadRequest(c, "invalid client ID")
|
|
}
|
|
|
|
var client models.DoormileClient
|
|
if err := db.DB.First(&client, id).Error; err != nil {
|
|
return utils.NotFound(c, "client not found")
|
|
}
|
|
|
|
var auth models.DoormileAuth
|
|
email, role := "", ""
|
|
if db.DB.Where("client_id = ?", id).First(&auth).Error == nil {
|
|
email = auth.Email
|
|
role = auth.Role
|
|
}
|
|
|
|
return utils.OK(c, buildClientResponse(client, email, role))
|
|
}
|
|
|
|
func UpdateClient(c *fiber.Ctx) error {
|
|
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
return utils.BadRequest(c, "invalid client ID")
|
|
}
|
|
|
|
var client models.DoormileClient
|
|
if err := db.DB.First(&client, id).Error; err != nil {
|
|
return utils.NotFound(c, "client not found")
|
|
}
|
|
|
|
var input dto.CreateClientRequest
|
|
if err := c.BodyParser(&input); err != nil {
|
|
return utils.BadRequest(c, "invalid request body")
|
|
}
|
|
|
|
if input.FirstName != "" {
|
|
client.FirstName = input.FirstName
|
|
}
|
|
if input.LastName != "" {
|
|
client.LastName = input.LastName
|
|
}
|
|
if input.Phone != "" {
|
|
client.Phone = input.Phone
|
|
}
|
|
if input.Address != "" {
|
|
client.Address = input.Address
|
|
}
|
|
if input.City != "" {
|
|
client.City = input.City
|
|
}
|
|
if input.State != "" {
|
|
client.State = input.State
|
|
}
|
|
if input.Neighbourhood != "" {
|
|
client.Neighbourhood = input.Neighbourhood
|
|
}
|
|
if input.Pincode != "" {
|
|
client.Pincode = input.Pincode
|
|
}
|
|
if input.SurveyLat != 0 {
|
|
client.SurveyLat = input.SurveyLat
|
|
}
|
|
if input.SurveyLong != 0 {
|
|
client.SurveyLong = input.SurveyLong
|
|
}
|
|
if input.SurveyAddress != "" {
|
|
client.SurveyAddress = input.SurveyAddress
|
|
}
|
|
if input.SurveyZone != "" {
|
|
client.SurveyZone = input.SurveyZone
|
|
}
|
|
if input.SurveyPincode != "" {
|
|
client.SurveyPincode = input.SurveyPincode
|
|
}
|
|
if input.BusinessType != "" {
|
|
client.BusinessType = input.BusinessType
|
|
}
|
|
if input.Status != "" {
|
|
client.Status = input.Status
|
|
}
|
|
if input.ShippingFrequency != "" {
|
|
client.ShippingFrequency = input.ShippingFrequency
|
|
}
|
|
if input.LogisticsSegment != "" {
|
|
client.LogisticsSegment = input.LogisticsSegment
|
|
}
|
|
if input.TransitFrom != "" {
|
|
client.TransitFrom = input.TransitFrom
|
|
}
|
|
if input.TransitTo != "" {
|
|
client.TransitTo = input.TransitTo
|
|
}
|
|
if input.RegistrationSource != "" {
|
|
client.RegistrationSource = input.RegistrationSource
|
|
}
|
|
if input.RegisteredByID != 0 {
|
|
client.RegisteredByID = input.RegisteredByID
|
|
}
|
|
client.Notes = input.Notes
|
|
|
|
if input.DataConsent != "" {
|
|
client.DataConsent = input.DataConsent
|
|
}
|
|
|
|
if client.DataConsent == "full" {
|
|
client.ParcelVolume = input.ParcelVolume
|
|
client.ActiveContracts = input.ActiveContracts
|
|
client.LogisticsProvider = input.LogisticsProvider
|
|
client.ProviderEfficiency = input.ProviderEfficiency
|
|
} else {
|
|
client.ParcelVolume = 0
|
|
client.ActiveContracts = 0
|
|
client.LogisticsProvider = "Not disclosed"
|
|
client.ProviderEfficiency = ""
|
|
client.Notes = ""
|
|
}
|
|
|
|
tx := db.DB.Begin()
|
|
|
|
if err := tx.Save(&client).Error; err != nil {
|
|
tx.Rollback()
|
|
return utils.Internal(c, "failed to update client")
|
|
}
|
|
|
|
var auth models.DoormileAuth
|
|
email, role := "", ""
|
|
if db.DB.Where("client_id = ?", id).First(&auth).Error == nil {
|
|
email = auth.Email
|
|
role = auth.Role
|
|
}
|
|
|
|
authUpdated := false
|
|
if input.Email != "" && input.Email != auth.Email {
|
|
auth.Email = input.Email
|
|
email = input.Email
|
|
authUpdated = true
|
|
}
|
|
if input.Password != "" {
|
|
hashed, err := utils.HashPassword(input.Password)
|
|
if err != nil {
|
|
tx.Rollback()
|
|
return utils.Internal(c, "failed to process password update")
|
|
}
|
|
auth.PasswordHash = hashed
|
|
authUpdated = true
|
|
}
|
|
if authUpdated && auth.ID != 0 {
|
|
if err := tx.Save(&auth).Error; err != nil {
|
|
tx.Rollback()
|
|
return utils.Internal(c, "failed to update credentials")
|
|
}
|
|
}
|
|
|
|
tx.Commit()
|
|
|
|
return utils.OK(c, buildClientResponse(client, email, role))
|
|
}
|
|
|
|
func DeleteClient(c *fiber.Ctx) error {
|
|
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
return utils.BadRequest(c, "invalid client ID")
|
|
}
|
|
|
|
var client models.DoormileClient
|
|
if err := db.DB.First(&client, id).Error; err != nil {
|
|
return utils.NotFound(c, "client not found")
|
|
}
|
|
|
|
tx := db.DB.Begin()
|
|
|
|
if err := tx.Where("client_id = ?", id).Delete(&models.DoormileAuth{}).Error; err != nil {
|
|
tx.Rollback()
|
|
return utils.Internal(c, "failed to delete client credentials")
|
|
}
|
|
|
|
if err := tx.Delete(&client).Error; err != nil {
|
|
tx.Rollback()
|
|
return utils.Internal(c, "failed to delete client")
|
|
}
|
|
|
|
tx.Commit()
|
|
|
|
return utils.Message(c, "client deleted successfully")
|
|
}
|
|
|
|
func buildClientResponse(client models.DoormileClient, email, role string) dto.ClientResponse {
|
|
return dto.ClientResponse{
|
|
ID: client.ID,
|
|
CreatedAt: client.CreatedAt.Format("2006-01-02T15:04:05Z"),
|
|
LastUpdated: client.UpdatedAt.Format("2006-01-02"),
|
|
FirstName: client.FirstName,
|
|
LastName: client.LastName,
|
|
Email: email,
|
|
Phone: client.Phone,
|
|
Address: client.Address,
|
|
City: client.City,
|
|
State: client.State,
|
|
Neighbourhood: client.Neighbourhood,
|
|
Pincode: client.Pincode,
|
|
SurveyLat: client.SurveyLat,
|
|
SurveyLong: client.SurveyLong,
|
|
SurveyAddress: client.SurveyAddress,
|
|
SurveyZone: client.SurveyZone,
|
|
SurveyPincode: client.SurveyPincode,
|
|
BusinessType: client.BusinessType,
|
|
Status: client.Status,
|
|
ShippingFrequency: client.ShippingFrequency,
|
|
LogisticsSegment: client.LogisticsSegment,
|
|
TransitFrom: client.TransitFrom,
|
|
TransitTo: client.TransitTo,
|
|
ParcelVolume: client.ParcelVolume,
|
|
ActiveContracts: client.ActiveContracts,
|
|
LogisticsProvider: client.LogisticsProvider,
|
|
ProviderEfficiency: client.ProviderEfficiency,
|
|
Notes: client.Notes,
|
|
DataConsent: client.DataConsent,
|
|
RegistrationSource: client.RegistrationSource,
|
|
RegisteredByID: client.RegisteredByID,
|
|
Role: role,
|
|
}
|
|
}
|
|
|
|
func DetermineSource(userAgent string) string {
|
|
ua := strings.ToLower(userAgent)
|
|
if strings.Contains(ua, "dart") || strings.Contains(ua, "flutter") || strings.Contains(ua, "doormile") {
|
|
return "mobile"
|
|
}
|
|
if strings.Contains(ua, "mozilla") || strings.Contains(ua, "chrome") || strings.Contains(ua, "safari") {
|
|
return "web"
|
|
}
|
|
return "api_tool"
|
|
}
|