diff --git a/controllers/adminController.go b/controllers/adminController.go index 3e3b334..ed32b1e 100644 --- a/controllers/adminController.go +++ b/controllers/adminController.go @@ -1843,11 +1843,6 @@ func UpdateMiler(c *fiber.Ctx) error { Displayname string `json:"displayname"` Defaultvehicletype string `json:"defaultvehicletype"` Hubid *int `json:"hubid"` - // Legacyuserid links this rider to their old jupiter userid, so telemetry - // still arriving over the jupiter NATS chain resolves to them. Writable - // here because there is no other way to set it — the column is populated - // by ops for migrated riders, never by the rider themselves. - Legacyuserid *int `json:"legacyuserid"` } req := new(MilerUpdate) @@ -1864,9 +1859,6 @@ func UpdateMiler(c *fiber.Ctx) error { if req.Hubid != nil { profile.Hubid = req.Hubid } - if req.Legacyuserid != nil { - profile.Legacyuserid = req.Legacyuserid - } profile.Updatedat = time.Now() if err := db.DB.Save(profile).Error; err != nil { diff --git a/middlewares/legacy_identity.go b/middlewares/legacy_identity.go deleted file mode 100644 index b58fd00..0000000 --- a/middlewares/legacy_identity.go +++ /dev/null @@ -1,76 +0,0 @@ -package middlewares - -import ( - "strconv" - - "doormile/db" - "doormile/models" - "doormile/utils" - - "github.com/gofiber/fiber/v2" -) - -// LegacyMilerIdentity resolves a rider identity for machine-to-machine ingest -// and puts it in c.Locals("userid"), which is exactly where the normal miler -// handlers read it from. That is the whole point: the ingest routes reuse the -// existing handlers unchanged rather than growing a parallel set with their own -// (inevitably drifting) validation. -// -// It must be mounted *behind* InternalKeyAuth. On its own it is not -// authentication — it names a rider, it does not prove anything about the -// caller. The X-Internal-Key check is what makes that safe, and it is the reason -// this cannot be reached from the public internet. -// -// Two headers, checked in order: -// -// X-Miler-Userid a Doormile userid, used directly -// X-Legacy-Userid a jupiter/Nearle userid, resolved via MilerProfile.Legacyuserid -// -// The identity deliberately does NOT come from the request body. The miler -// telemetry handlers overwrite any body-supplied userid with the token's, which -// is what stops one rider writing another's GPS trail; taking it from the body -// here would reopen that hole from behind the internal key. A header keeps the -// rule intact and works for POST /consignments/logs, whose body is a bare JSON -// array with nowhere to put an id anyway. -func LegacyMilerIdentity(c *fiber.Ctx) error { - if raw := c.Get("X-Miler-Userid"); raw != "" { - userID, err := strconv.Atoi(raw) - if err != nil || userID <= 0 { - return utils.BadRequest(c, "invalid X-Miler-Userid") - } - - var count int64 - if err := db.DB.Model(&models.MilerProfile{}). - Where("userid = ?", userID).Count(&count).Error; err != nil { - return utils.Internal(c, "failed to resolve miler") - } - if count == 0 { - return utils.NotFound(c, "no miler with that userid") - } - - c.Locals("userid", userID) - return c.Next() - } - - raw := c.Get("X-Legacy-Userid") - if raw == "" { - return utils.BadRequest(c, "X-Miler-Userid or X-Legacy-Userid header is required") - } - - legacyID, err := strconv.Atoi(raw) - if err != nil || legacyID <= 0 { - return utils.BadRequest(c, "invalid X-Legacy-Userid") - } - - var profile models.MilerProfile - if err := db.DB.Where("legacyuserid = ?", legacyID).First(&profile).Error; err != nil { - // Unmapped is the expected case for every rider who was never migrated - // from jupiter, so this is a routine 404 rather than an error condition. - // The forwarder treats it as "drop, do not retry" — retrying cannot - // invent a mapping, and NAK-looping on it would wedge the consumer. - return utils.NotFound(c, "no Doormile miler mapped to that legacy userid") - } - - c.Locals("userid", profile.Userid) - return c.Next() -} diff --git a/models/users.go b/models/users.go index ab0afdf..5f915af 100644 --- a/models/users.go +++ b/models/users.go @@ -92,12 +92,11 @@ type MilerProfile struct { Profilephotourl string `json:"profilephotourl" gorm:"column:profilephotourl"` Vehicleid *int `json:"vehicleid" gorm:"column:vehicleid"` Hubid *int `json:"hubid" gorm:"column:hubid"` - // Legacyuserid is this rider's userid in the old jupiter/Nearle system, for - // riders migrated from it. It exists so telemetry still arriving over the - // jupiter NATS chain — which identifies a rider by jupiter's userid and has - // no Doormile token — can be resolved to a Doormile rider. Nil for riders - // created natively in Doormile, which is the normal case. Never used for - // authentication: it identifies, the X-Internal-Key authenticates. + // Legacyuserid holds this rider's userid in the old jupiter system for the + // six riders migrated from it. Nothing reads it any more — the jupiter + // telemetry bridge it existed for was removed — but it is kept as a record + // of where each migrated rider came from, which is worth having during the + // cutover. Nil for riders created natively in Doormile. Legacyuserid *int `json:"legacyuserid,omitempty" gorm:"column:legacyuserid;index"` Defaultvehicletype string `json:"defaultvehicletype" gorm:"column:defaultvehicletype"` Currentlatitude float64 `json:"currentlatitude" gorm:"column:currentlatitude"` diff --git a/routes/routes.go b/routes/routes.go index 3ca6f20..f1cee94 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -417,23 +417,6 @@ func RegisterRoutes(app *fiber.App, cfg *config.Config) { internal.Get("/agent-decisions/similar", controllers.FindSimilarDecisions) internal.Patch("/agent-decisions/:id/outcome", controllers.UpdateDecisionOutcome) - // Rider telemetry ingest for the jupiter NATS chain. The forwarding worker - // holds no rider JWT — the rider app is still jupiter-shaped and its token is - // jupiter's — so identity arrives as a header and LegacyMilerIdentity turns - // it into c.Locals("userid"). These are the *same handlers* the miler app - // hits under /miler; only the way identity is established differs, so - // validation and storage cannot drift between the two paths. - // - // Only fire-and-forget telemetry is exposed this way. Transactional actions - // (pickup-complete, deliver, payment) are deliberately absent: the rider - // needs a real answer from those, which a queue in front of them cannot give. - ingest := internal.Group("/miler", middlewares.LegacyMilerIdentity) - ingest.Post("/logs", controllers.CreateMilerPeriodicLog) - ingest.Post("/status", controllers.CreateMilerStatus) - ingest.Post("/consignments/logs", controllers.PublishConsignmentLogs) - ingest.Post("/breaks/start", controllers.MilerStartBreak) - ingest.Put("/breaks/end", controllers.MilerEndBreak) - // -------------------- // WEBSOCKET — live miler tracking (no auth, public tracking link) // --------------------