backend requirements onthe xustomer app

This commit is contained in:
2026-09-07 10:55:11 +05:30
parent 35675d8a9b
commit 1b2690b21a
56 changed files with 13342 additions and 1071 deletions

View File

@@ -78,18 +78,46 @@ func RegisterRoutes(app *fiber.App, cfg *config.Config) {
})
// --------------------
// CUSTOMER APIS
// CUSTOMER APIS — doormile_cx
// --------------------
// The customer books a PICKUP, not a shipment: one visit, 1..N
// destinations, and a tracking number per destination minted only when the
// miler completes the pickup. Everything under /customer/* answers in the
// customer envelope ({success, data, message}; errors carry error.code) —
// auth included, deliberately unlike /miler/verify-pin, whose payload sits
// outside `data` and cost the miler client a release to discover.
//
// This replaces the PIN-based customer surface (register / login /
// verify-pin / reset-pin, and the single-destination booking create, list,
// detail and cancel). Those were retired rather than moved: a booking with
// one delivery address in its own columns is not a shape the product has
// any more.
customer := api.Group("/customer")
customer.Post("/register", controllers.RegisterCustomer(cfg))
customer.Post("/login", authThrottle, controllers.LoginCustomer)
customer.Post("/verify-pin", authThrottle, controllers.VerifyCustomerPin(cfg))
customer.Post("/reset-pin", authThrottle, controllers.ResetCustomerPin)
customer.Post("/send-email-otp", authThrottle, controllers.SendCustomerEmailOtp(cfg))
customer.Post("/verify-email-otp", authThrottle, controllers.VerifyCustomerEmailOtp())
// Auth — a 4-digit code to a phone or an email address, no password
// anywhere. Throttled on the shared budget with every other credential
// endpoint so an attacker cannot reset it by rotating between them.
customer.Post("/auth/otp/request", authThrottle, controllers.CxRequestOtp(cfg))
customer.Post("/auth/signup", authThrottle, controllers.CxSignup(cfg))
// Idempotent: the client retries on flaky networks, and a replayed verify
// must return the original session rather than mint a second one.
customer.Post("/auth/otp/verify", authThrottle, middlewares.Idempotency(), controllers.CxVerifyOtp(cfg))
customer.Post("/auth/refresh", authThrottle, controllers.CxRefresh(cfg))
// Serviceability and configuration are read before sign-in: the booking
// form is explorable without an account, and gating the state picker behind
// auth would make the app's first screen a login wall.
customer.Get("/serviceability/states", controllers.GetCxStates)
customer.Get("/serviceability/states/:stateCode/districts", controllers.GetCxDistricts)
customer.Get("/pickup-slots", controllers.GetCxPickupSlots)
customer.Get("/config/booking-limits", controllers.GetCxBookingLimits)
// Authenticated Customer App routes
customerAuth := customer.Use(middlewares.AuthMiddleware(cfg), middlewares.RoleCheckMiddleware(9))
customerAuth.Get("/auth/me", controllers.CxMe)
customerAuth.Post("/auth/logout", controllers.CxLogout)
customerAuth.Get("/profile", controllers.GetCustomerProfile)
customerAuth.Put("/profile", controllers.UpdateCustomerProfile)
@@ -98,14 +126,38 @@ func RegisterRoutes(app *fiber.App, cfg *config.Config) {
customerAuth.Put("/locations/:id", controllers.UpdateCustomerLocation)
customerAuth.Delete("/locations/:id", controllers.DeleteCustomerLocation)
customerAuth.Put("/device-token", controllers.SaveCustomerDeviceToken)
// Push registration. One row per device token, not one column per customer:
// a phone and a tablet both have to receive the delivery notification.
customerAuth.Post("/devices", controllers.RegisterCxDevice)
customerAuth.Delete("/devices/:token", controllers.UnregisterCxDevice)
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)
// Places are proxied, never keyed: the legacy rider app shipped a Maps key
// in the binary and it had to be revoked. The customer app is handed
// results, not credentials.
customerAuth.Get("/places/reverse-geocode", controllers.ReverseGeocodeCx(cfg))
customerAuth.Get("/places/search", controllers.SearchCxPlaces(cfg))
// Called on every route and package-count change, so it is cheap and
// cacheable — and a failed estimate never blocks a booking.
customerAuth.Post("/fare/estimate", controllers.EstimateCxFare)
// Idempotency-Key on create: the client retries over bad networks and a
// duplicate pickup is unacceptable.
customerAuth.Post("/bookings", middlewares.CityGateMiddleware, middlewares.Idempotency(), controllers.CreateCxBooking)
customerAuth.Get("/bookings", controllers.GetCxBookings)
customerAuth.Get("/bookings/:reference", controllers.GetCxBookingDetail)
customerAuth.Post("/bookings/:reference/cancel", controllers.CancelCxBooking)
customerAuth.Patch("/bookings/:reference/destinations/:index", controllers.PatchCxDestination)
// One order by tracking number, for push deep links (doormile://track/…).
customerAuth.Get("/orders/:trackingId", controllers.GetCxOrder)
// QA only. Refused outright unless ENV is non-production AND
// CX_ALLOW_STAGE_OVERRIDE=true — two independent switches, because either
// one being wrong in production would let any customer mark their own
// parcel delivered. It exists so every tracking state is reachable for
// design QA and the app's debug stepper can be deleted.
customerAuth.Post("/ops/bookings/:reference/stage", controllers.ForceCxStage)
// --------------------
// MILER APIS