- models/agentdecision.go: AgentDecision GORM model (context/decision as jsonb, reasoning as text) - migrations/migrate.go: AutoMigrate AgentDecision then ALTER TABLE to add vector(1536) column and CREATE ivfflat index via raw SQL - controllers/agentDecisionController.go: CreateAgentDecision, FindSimilarDecisions (cosine distance), UpdateDecisionOutcome - routes/routes.go: three routes under /api/v1/internal (InternalKeyAuth applied at group level) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
293 lines
12 KiB
Go
293 lines
12 KiB
Go
package routes
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"doormile/config"
|
|
"doormile/controllers"
|
|
"doormile/db"
|
|
"doormile/internal/ws"
|
|
"doormile/middlewares"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/gofiber/websocket/v2"
|
|
)
|
|
|
|
func RegisterRoutes(app *fiber.App, cfg *config.Config) {
|
|
api := app.Group("/api/v1")
|
|
|
|
// --------------------
|
|
// HEALTH & READINESS PROBES
|
|
// --------------------
|
|
api.Get("/health", func(c *fiber.Ctx) error {
|
|
return c.JSON(fiber.Map{"status": "OK", "time": time.Now()})
|
|
})
|
|
|
|
api.Get("/ready", func(c *fiber.Ctx) error {
|
|
dbStatus := "connected"
|
|
sqlDB, err := db.DB.DB()
|
|
if err != nil || sqlDB.Ping() != nil {
|
|
dbStatus = "unavailable"
|
|
}
|
|
|
|
redisStatus := "connected"
|
|
if db.Rdb == nil {
|
|
redisStatus = "unavailable"
|
|
} else {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
if _, pingErr := db.Rdb.Ping(ctx).Result(); pingErr != nil {
|
|
redisStatus = "unavailable"
|
|
}
|
|
}
|
|
|
|
status := fiber.StatusOK
|
|
if dbStatus == "unavailable" || redisStatus == "unavailable" {
|
|
status = fiber.StatusServiceUnavailable
|
|
}
|
|
|
|
return c.Status(status).JSON(fiber.Map{
|
|
"status": "ready",
|
|
"checks": fiber.Map{
|
|
"postgres": dbStatus,
|
|
"redis": redisStatus,
|
|
},
|
|
})
|
|
})
|
|
|
|
// --------------------
|
|
// CUSTOMER APIS
|
|
// --------------------
|
|
customer := api.Group("/customer")
|
|
customer.Post("/register", controllers.RegisterCustomer(cfg))
|
|
customer.Post("/login", controllers.LoginCustomer)
|
|
customer.Post("/verify-pin", controllers.VerifyCustomerPin(cfg))
|
|
customer.Post("/reset-pin", controllers.ResetCustomerPin)
|
|
|
|
// Authenticated Customer App routes
|
|
customerAuth := customer.Use(middlewares.AuthMiddleware(cfg), middlewares.RoleCheckMiddleware(9))
|
|
customerAuth.Get("/profile", controllers.GetCustomerProfile)
|
|
customerAuth.Put("/profile", controllers.UpdateCustomerProfile)
|
|
|
|
customerAuth.Get("/locations", controllers.GetCustomerLocations)
|
|
customerAuth.Post("/locations", controllers.CreateCustomerLocation)
|
|
customerAuth.Put("/locations/:id", controllers.UpdateCustomerLocation)
|
|
customerAuth.Delete("/locations/:id", controllers.DeleteCustomerLocation)
|
|
|
|
customerAuth.Put("/device-token", controllers.SaveCustomerDeviceToken)
|
|
|
|
customerAuth.Post("/bookings", middlewares.CityGateMiddleware, controllers.CreateCustomerBooking)
|
|
customerAuth.Get("/bookings", controllers.GetCustomerBookings)
|
|
customerAuth.Get("/bookings/:bookingid", controllers.GetCustomerBookingDetails)
|
|
customerAuth.Post("/bookings/:bookingid/cancel", controllers.CancelCustomerBooking)
|
|
customerAuth.Get("/bookings/:bookingid/price", controllers.GetCustomerBookingQuote)
|
|
customerAuth.Get("/track/:trackingno", controllers.TrackConsignment)
|
|
|
|
// --------------------
|
|
// MILER APIS
|
|
// --------------------
|
|
miler := api.Group("/miler")
|
|
miler.Post("/login", controllers.LoginMiler(cfg))
|
|
miler.Post("/verify-pin", controllers.VerifyMilerPin(cfg))
|
|
|
|
// Authenticated Miler App routes
|
|
milerAuth := miler.Use(middlewares.AuthMiddleware(cfg), middlewares.RoleCheckMiddleware(5))
|
|
milerAuth.Get("/profile", controllers.GetMilerProfile)
|
|
milerAuth.Put("/profile", controllers.UpdateMilerProfile)
|
|
|
|
milerAuth.Put("/device-token", controllers.SaveMilerDeviceToken)
|
|
|
|
milerAuth.Put("/location", controllers.UpdateMilerLocation)
|
|
milerAuth.Put("/availability", controllers.UpdateMilerAvailability)
|
|
|
|
milerAuth.Get("/assignments", controllers.GetMilerAssignments)
|
|
milerAuth.Get("/assignments/:id", controllers.GetMilerAssignmentDetails)
|
|
milerAuth.Post("/assignments/:id/accept", controllers.AcceptMilerAssignment)
|
|
milerAuth.Post("/assignments/:id/reject", controllers.RejectMilerAssignment)
|
|
|
|
milerAuth.Post("/bookings/:bookingid/reached", controllers.BookingReachedCustomer)
|
|
milerAuth.Post("/bookings/:bookingid/parcel", controllers.BookingParcelConfirm)
|
|
milerAuth.Post("/bookings/:bookingid/payment", controllers.BookingPaymentCollect)
|
|
milerAuth.Post("/bookings/:bookingid/pickup-complete", controllers.BookingPickupComplete)
|
|
milerAuth.Post("/bookings/:bookingid/vehicle-required", controllers.BookingVehicleRequiredEscalate)
|
|
|
|
// Redis periodic telemetry and status logs
|
|
milerAuth.Post("/logs", controllers.CreateMilerPeriodicLog)
|
|
milerAuth.Get("/logs", controllers.GetMilerPeriodicLogs)
|
|
milerAuth.Post("/status", controllers.CreateMilerStatus)
|
|
milerAuth.Get("/status", controllers.GetMilerStatus)
|
|
|
|
// Redis consignment tracking and telemetry logs
|
|
milerAuth.Post("/consignments/logs", controllers.PublishConsignmentLogs)
|
|
milerAuth.Get("/consignments/logs/:consignmentid", controllers.GetConsignmentLogs)
|
|
milerAuth.Get("/consignments/userlogs/:userid", controllers.GetUserConsignmentLogs)
|
|
|
|
// --------------------
|
|
// ADMIN CONSOLE APIS — all open, no token required
|
|
// --------------------
|
|
admin := api.Group("/admin")
|
|
|
|
admin.Post("/login", controllers.LoginAdmin(cfg))
|
|
|
|
// Authenticated Admin Console routes
|
|
adminAuth := admin.Use(middlewares.AuthMiddleware(cfg), middlewares.RoleCheckMiddleware(1, 3, 4))
|
|
|
|
adminAuth.Get("/dashboard", controllers.GetAdminDashboard)
|
|
adminAuth.Get("/profile", controllers.GetAdminProfile)
|
|
adminAuth.Get("/me", controllers.GetAdminProfile)
|
|
|
|
// App Users Management
|
|
adminAuth.Get("/users", controllers.GetAppUsers)
|
|
adminAuth.Post("/users", controllers.CreateAppUser)
|
|
adminAuth.Put("/users/:id", controllers.UpdateAppUser)
|
|
adminAuth.Delete("/users/:id", controllers.DeleteAppUser)
|
|
|
|
// Tenant management
|
|
adminAuth.Get("/tenants", controllers.GetTenants)
|
|
adminAuth.Post("/tenants", controllers.CreateTenant)
|
|
adminAuth.Get("/tenants/:id", controllers.GetTenantDetails)
|
|
adminAuth.Put("/tenants/:id", controllers.UpdateTenant)
|
|
adminAuth.Delete("/tenants/:id", controllers.DeleteTenant)
|
|
adminAuth.Get("/tenants/:id/locations", controllers.GetTenantLocations)
|
|
adminAuth.Post("/tenants/:id/locations", controllers.CreateTenantLocation)
|
|
adminAuth.Put("/tenantlocations/:id", controllers.UpdateTenantLocation)
|
|
|
|
// Tenant customers
|
|
adminAuth.Get("/tenantcustomers", controllers.GetTenantCustomers)
|
|
adminAuth.Post("/tenantcustomers", controllers.CreateTenantCustomer)
|
|
adminAuth.Get("/tenantcustomers/:id", controllers.GetTenantCustomerDetails)
|
|
adminAuth.Put("/tenantcustomers/:id", controllers.UpdateTenantCustomer)
|
|
adminAuth.Delete("/tenantcustomers/:id", controllers.DeleteTenantCustomer)
|
|
|
|
// Hubs
|
|
adminAuth.Get("/hubs", controllers.GetHubs)
|
|
adminAuth.Post("/hubs", controllers.CreateHub)
|
|
adminAuth.Get("/hubs/:id", controllers.GetHubDetails)
|
|
adminAuth.Put("/hubs/:id", controllers.UpdateHub)
|
|
adminAuth.Delete("/hubs/:id", controllers.DeleteHub)
|
|
|
|
// Vehicles
|
|
adminAuth.Get("/vehicles", controllers.GetVehicles)
|
|
adminAuth.Post("/vehicles", controllers.CreateVehicle)
|
|
adminAuth.Get("/vehicles/:id", controllers.GetVehicleDetails)
|
|
adminAuth.Put("/vehicles/:id", controllers.UpdateVehicle)
|
|
adminAuth.Delete("/vehicles/:id", controllers.DeleteVehicle)
|
|
|
|
// Milers
|
|
adminAuth.Get("/milers", controllers.GetMilers)
|
|
adminAuth.Post("/milers", controllers.CreateMiler)
|
|
adminAuth.Get("/milers/:id", controllers.GetMilerDetails)
|
|
adminAuth.Put("/milers/:id", controllers.UpdateMiler)
|
|
adminAuth.Put("/milers/:id/block", controllers.BlockMiler)
|
|
adminAuth.Put("/milers/:id/assign-vehicle", controllers.AssignMilerVehicle)
|
|
|
|
// Bookings
|
|
adminAuth.Get("/bookings", controllers.GetAdminBookings)
|
|
adminAuth.Post("/crmbooking", middlewares.CityGateMiddleware, controllers.CreateCRMBooking)
|
|
adminAuth.Get("/bookings/:id", controllers.GetAdminBookingDetails)
|
|
adminAuth.Post("/bookings/:id/assign-miler", controllers.AdminAssignMiler)
|
|
adminAuth.Post("/bookings/:id/assign-vehicle", controllers.AdminAssignVehicle)
|
|
adminAuth.Put("/bookings/:id/status", controllers.AdminUpdateBookingStatus)
|
|
|
|
// Consignments
|
|
adminAuth.Get("/consignments", controllers.GetAdminConsignments)
|
|
adminAuth.Get("/consignments/:id", controllers.GetAdminConsignmentDetails)
|
|
adminAuth.Get("/consignments/track/:trackingno", controllers.GetAdminConsignmentTracking)
|
|
adminAuth.Put("/consignments/:id/status", controllers.AdminUpdateConsignmentStatus)
|
|
|
|
// Tripsheets
|
|
adminAuth.Get("/tripsheets", controllers.GetTripsheets)
|
|
adminAuth.Post("/tripsheets", controllers.CreateTripsheet)
|
|
adminAuth.Get("/tripsheets/:id", controllers.GetTripsheetDetails)
|
|
adminAuth.Post("/tripsheets/:id/items", controllers.AddTripsheetItem)
|
|
adminAuth.Delete("/tripsheets/:id/items/:itemid", controllers.DeleteTripsheetItem)
|
|
adminAuth.Put("/tripsheets/:id/dispatch", controllers.DispatchTripsheet)
|
|
adminAuth.Put("/tripsheets/:id/arrive", controllers.ArriveTripsheet)
|
|
|
|
// Competitor branch survey data (from Enquiry.xlsx Sheet 1)
|
|
adminAuth.Get("/competitor-branches", controllers.GetCompetitorBranches)
|
|
adminAuth.Post("/competitor-branches", controllers.CreateCompetitorBranch)
|
|
adminAuth.Put("/competitor-branches/:id", controllers.UpdateCompetitorBranch)
|
|
adminAuth.Delete("/competitor-branches/:id", controllers.DeleteCompetitorBranch)
|
|
|
|
// Carrier pricing benchmarks (from Enquiry.xlsx Sheet 2)
|
|
adminAuth.Get("/carrier-pricing", controllers.GetCarrierPricing)
|
|
adminAuth.Post("/carrier-pricing", controllers.CreateCarrierPricing)
|
|
adminAuth.Put("/carrier-pricing/:id", controllers.UpdateCarrierPricing)
|
|
adminAuth.Delete("/carrier-pricing/:id", controllers.DeleteCarrierPricing)
|
|
|
|
// Pricing
|
|
adminAuth.Get("/pricing", controllers.GetPricing)
|
|
adminAuth.Post("/pricing", controllers.CreatePricing)
|
|
adminAuth.Put("/pricing/:id", controllers.UpdatePricing)
|
|
adminAuth.Delete("/pricing/:id", controllers.DeletePricing)
|
|
adminAuth.Post("/pricing/simulate", controllers.GetPricingQuoteSimulate)
|
|
adminAuth.Post("/pricing/quote", controllers.GetPricingQuoteSimulate)
|
|
|
|
// Doormile pricing bands
|
|
adminAuth.Get("/doormile-pricing", controllers.GetDoormilePricing)
|
|
adminAuth.Post("/doormile-pricing", controllers.CreateDoormilePricing)
|
|
adminAuth.Put("/doormile-pricing/:id", controllers.UpdateDoormilePricing)
|
|
adminAuth.Delete("/doormile-pricing/:id", controllers.DeleteDoormilePricing)
|
|
|
|
// Exceptions
|
|
adminAuth.Get("/exceptions", controllers.GetExceptions)
|
|
adminAuth.Post("/exceptions", controllers.CreateException)
|
|
adminAuth.Get("/exceptions/:id", controllers.GetExceptionDetails)
|
|
adminAuth.Put("/exceptions/:id/status", controllers.ResolveException)
|
|
|
|
// Redis active user caching utilities
|
|
redisUsers := api.Group("/utils/users/redis")
|
|
redisUsers.Post("/", controllers.CreateUserRedis)
|
|
redisUsers.Get("/", controllers.GetUserRedis)
|
|
redisUsers.Put("/:userid", controllers.UpdateUserRedis)
|
|
redisUsers.Delete("/:userid", controllers.DeleteUserRedis)
|
|
|
|
// --------------------
|
|
// DOORMILE PRICING — public check & meta (no auth)
|
|
// --------------------
|
|
api.Get("/pricing/meta", controllers.GetPricingMeta)
|
|
api.Post("/pricing/check", controllers.CheckPrice)
|
|
|
|
// --------------------
|
|
// BOOKING CACHE APIS — no auth required (testing)
|
|
// --------------------
|
|
bookingCache := api.Group("/bookings/cache")
|
|
bookingCache.Get("/", controllers.ListAllBookingsFromCache)
|
|
bookingCache.Get("/customer/:customer_id", controllers.GetCustomerBookingsFromCache)
|
|
bookingCache.Get("/:booking_id", controllers.GetBookingFromCache)
|
|
|
|
// --------------------
|
|
// CRM APIS — all open (field reps register from Flutter; web console reads without a separate CRM login)
|
|
// --------------------
|
|
crm := api.Group("/crm")
|
|
crm.Post("/clients", controllers.RegisterClient)
|
|
crm.Get("/clients", controllers.GetClients)
|
|
crm.Get("/clients/:id", controllers.GetClientDetails)
|
|
crm.Put("/clients/:id", controllers.UpdateClient)
|
|
crm.Delete("/clients/:id", controllers.DeleteClient)
|
|
|
|
// --------------------
|
|
// INTERNAL — machine-to-machine endpoints (API key auth, no JWT)
|
|
// --------------------
|
|
internal := api.Group("/internal", middlewares.InternalKeyAuth)
|
|
internal.Post("/notify", controllers.InternalNotify)
|
|
internal.Post("/bookings/:id/reassign", controllers.InternalReassign)
|
|
internal.Post("/agent-decisions", controllers.CreateAgentDecision)
|
|
internal.Get("/agent-decisions/similar", controllers.FindSimilarDecisions)
|
|
internal.Patch("/agent-decisions/:id/outcome", controllers.UpdateDecisionOutcome)
|
|
|
|
// --------------------
|
|
// WEBSOCKET — live miler tracking (no auth, public tracking link)
|
|
// --------------------
|
|
app.Use("/ws", func(c *fiber.Ctx) error {
|
|
if websocket.IsWebSocketUpgrade(c) {
|
|
return c.Next()
|
|
}
|
|
return fiber.ErrUpgradeRequired
|
|
})
|
|
app.Get("/ws/bookings/:bookingid/track", websocket.New(ws.TrackingHandler))
|
|
app.Get("/ws/bookings/:bookingid/chat", middlewares.WsChatAuth(cfg), websocket.New(ws.ChatHandler))
|
|
}
|