Add assignment engine, FCM, WebSockets, city gate, and internal APIs

- internal/assignment: GEORADIUS miler assignment with retry/escalation,
  customer-side provider scoring, FCM notifications on assign
- internal/notify: Firebase Admin SDK (FCM) client initialisation
- internal/ws: WebSocket handlers for live parcel tracking and
  customer↔miler chat
- middlewares: city gate (pincode prefix validation), internal API key
  auth, WebSocket JWT auth
- controllers: InternalNotify + InternalReassign for machine-to-machine
  calls; pricing helpers wired into CreateCustomerBooking and CreateCRMBooking
- routes: /internal/*, /ws/bookings/:id/track, /ws/bookings/:id/chat
- models/users, models/doormile_pricing: new fields for device tokens,
  assignment state, pricing bands
- seed_data.sql: initial pricing seed rows

.env and Firebase service-account JSON intentionally excluded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 12:31:53 +05:30
parent c577d47b75
commit c91c887726
21 changed files with 2399 additions and 75 deletions

View File

@@ -7,9 +7,11 @@ import (
"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) {
@@ -73,7 +75,9 @@ func RegisterRoutes(app *fiber.App, cfg *config.Config) {
customerAuth.Put("/locations/:id", controllers.UpdateCustomerLocation)
customerAuth.Delete("/locations/:id", controllers.DeleteCustomerLocation)
customerAuth.Post("/bookings", controllers.CreateCustomerBooking)
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)
@@ -92,6 +96,8 @@ func RegisterRoutes(app *fiber.App, cfg *config.Config) {
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)
@@ -178,7 +184,7 @@ func RegisterRoutes(app *fiber.App, cfg *config.Config) {
// Bookings
adminAuth.Get("/bookings", controllers.GetAdminBookings)
adminAuth.Post("/crmbooking", controllers.CreateCRMBooking)
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)
@@ -261,4 +267,23 @@ func RegisterRoutes(app *fiber.App, cfg *config.Config) {
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)
// --------------------
// 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))
}