Files
doormile_backend/models/users.go
Suriya 6738d37d7b feat: own the JetStream subject contract, and ingest jupiter rider telemetry
Half of this binary's js.Publish calls were bound to no stream at all.
The streams were declared only by an external Python script on another
machine (Birock/doormile-bookings/setup_jetstream.py) and had drifted
from the code: booking.cancelled, booking.outcome and
booking.assignment_failed had no stream, and CHAT declared the literal
"chat.room.closed" while chat.go publishes "chat.room.closed.<id>",
which it does not match. Every publish site is best-effort
(`if db.Js != nil` + warn-log), so those events were failing and being
dropped silently — every cancellation, delivery outcome and assignment
failure since the streams were created.

db.EnsureStreams now declares the streams at startup from a map that
sits next to the code that publishes, so the contract cannot drift
again. It only ever adds: existing streams keep their storage type,
retention, limits and every subject they already have. Nothing is
deleted. Losing the create race against a sibling replica is expected
and reconciles rather than erroring.

Alongside that, /internal/miler/* ingests rider telemetry still arriving
over the jupiter NATS chain. The forwarding worker holds no rider JWT —
the rider app is still jupiter-shaped — so LegacyMilerIdentity resolves
an identity from a header into c.Locals("userid") behind the existing
X-Internal-Key guard. That lets the routes reuse the miler handlers
unchanged instead of growing a parallel set that would drift.

Identity comes from a header, never the body: the telemetry handlers
overwrite a body-supplied userid precisely so one rider cannot write
another's GPS trail, and reading it from the body here would reopen that
from behind the internal key. MilerProfile.Legacyuserid (nullable,
indexed) maps a jupiter userid to a Doormile one.

Only fire-and-forget telemetry is exposed. Transactional actions stay
synchronous — a rider needs a real answer from pickup-complete, which a
queue in front of it cannot give.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 12:56:30 +05:30

247 lines
14 KiB
Go

package models
import (
"time"
)
type PartnerInfo struct {
Partnerid int `json:"partnerid" gorm:"primaryKey;column:partnerid"`
Partnername string `json:"partnername" gorm:"column:partnername;not null"`
Partnertypeid int `json:"partnertypeid" gorm:"column:partnertypeid"`
Contactno string `json:"contactno" gorm:"column:contactno"`
Status string `json:"status" gorm:"column:status;default:Active"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (PartnerInfo) TableName() string {
return "partnerinfo"
}
type Hub struct {
Hubid int `json:"hubid" gorm:"primaryKey;column:hubid"`
Hubname string `json:"hubname" gorm:"column:hubname;not null"`
Hubtype string `json:"hubtype" gorm:"column:hubtype;not null"` // sorting_center, delivery_hub
Applocationid int `json:"applocationid" gorm:"column:applocationid"`
Contactno string `json:"contactno" gorm:"column:contactno"`
Address string `json:"address" gorm:"column:address"`
Latitude float64 `json:"latitude" gorm:"column:latitude"`
Longitude float64 `json:"longitude" gorm:"column:longitude"`
Pincode string `json:"pincode" gorm:"column:pincode"`
Capacity int `json:"capacity" gorm:"column:capacity;default:50"`
Status string `json:"status" gorm:"column:status;default:Active"` // Active, InActive
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
Createdby int `json:"createdby" gorm:"column:createdby"`
Updatedby int `json:"updatedby" gorm:"column:updatedby"`
Deletedat *time.Time `json:"deletedat,omitempty" gorm:"column:deletedat"`
}
func (Hub) TableName() string {
return "hubs"
}
type Vehicle struct {
Vehicleid int `json:"vehicleid" gorm:"primaryKey;column:vehicleid"`
Vehicleno string `json:"vehicleno" gorm:"primaryKey;column:vehicleno;unique;not null"`
Vehicletype string `json:"vehicletype" gorm:"column:vehicletype;not null"`
Maxweight float64 `json:"maxweight" gorm:"column:maxweight;not null"`
Maxvolume float64 `json:"maxvolume" gorm:"column:maxvolume;not null"`
Partnerid *int `json:"partnerid" gorm:"column:partnerid"`
Batterypercentage int `json:"batterypercentage" gorm:"column:batterypercentage"`
Status string `json:"status" gorm:"column:status;default:Available"` // Available, In_Transit, Maintenance, InActive
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
Createdby int `json:"createdby" gorm:"column:createdby"`
Updatedby int `json:"updatedby" gorm:"column:updatedby"`
Deletedat *time.Time `json:"deletedat,omitempty" gorm:"column:deletedat"`
}
func (Vehicle) TableName() string {
return "vehicles"
}
type AppUser struct {
Userid int `json:"userid" gorm:"primaryKey;column:userid"`
Authname string `json:"authname" gorm:"column:authname;not null"`
Email string `json:"email" gorm:"column:email;unique;not null"`
Contactno string `json:"contactno" gorm:"column:contactno;not null"`
Password string `json:"-" gorm:"column:password;not null"`
Roleid int `json:"roleid" gorm:"column:roleid;not null"`
Hubid *int `json:"hubid" gorm:"column:hubid"`
Applocationid int `json:"applocationid" gorm:"column:applocationid"`
Partnerid *int `json:"partnerid" gorm:"column:partnerid"`
Tenantid int `json:"tenantid" gorm:"column:tenantid"`
Configid int `json:"configid" gorm:"column:configid;default:1"`
Onduty int `json:"onduty" gorm:"column:onduty;default:0"`
Status string `json:"status" gorm:"column:status;default:Active"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (AppUser) TableName() string {
return "appusers"
}
type MilerProfile struct {
Milerprofileid int `json:"milerprofileid" gorm:"primaryKey;column:milerprofileid"`
Userid int `json:"userid" gorm:"column:userid;unique;not null"`
Applocationid int `json:"applocationid" gorm:"column:applocationid;default:1"`
Displayname string `json:"displayname" gorm:"column:displayname;not null"`
Phone string `json:"phone" gorm:"column:phone;not null"`
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 *int `json:"legacyuserid,omitempty" gorm:"column:legacyuserid;index"`
Defaultvehicletype string `json:"defaultvehicletype" gorm:"column:defaultvehicletype"`
Currentlatitude float64 `json:"currentlatitude" gorm:"column:currentlatitude"`
Currentlongitude float64 `json:"currentlongitude" gorm:"column:currentlongitude"`
Currentpincode string `json:"currentpincode" gorm:"column:currentpincode"`
Availabilitystatus string `json:"availabilitystatus" gorm:"column:availabilitystatus;default:Offline"` // Offline, Available, Assigned, On_Pickup, At_Customer, Picked_Up, On_Delivery, Break, Blocked
Rating float64 `json:"rating" gorm:"column:rating;default:5.00"`
Totalcompletedpickups int `json:"totalcompletedpickups" gorm:"column:totalcompletedpickups;default:0"`
Totalcancelledpickups int `json:"totalcancelledpickups" gorm:"column:totalcancelledpickups;default:0"`
Devicetoken string `json:"device_token,omitempty" gorm:"column:device_token"`
Lastlocationupdatedat *time.Time `json:"lastlocationupdatedat" gorm:"column:lastlocationupdatedat"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (MilerProfile) TableName() string {
return "milerprofiles"
}
type AppCustomer struct {
Appcustomerid int `json:"appcustomerid" gorm:"primaryKey;column:appcustomerid"`
Firstname string `json:"firstname" gorm:"column:firstname;not null"`
Lastname string `json:"lastname" gorm:"column:lastname"`
Phone string `json:"phone" gorm:"column:phone;unique;not null"`
Email string `json:"email" gorm:"column:email"`
Loginpinhash string `json:"-" gorm:"column:loginpinhash"`
Defaultlatitude float64 `json:"defaultlatitude" gorm:"column:defaultlatitude"`
Defaultlongitude float64 `json:"defaultlongitude" gorm:"column:defaultlongitude"`
Defaultpincode string `json:"defaultpincode" gorm:"column:defaultpincode"`
Devicetoken string `json:"device_token,omitempty" gorm:"column:device_token"`
Status string `json:"status" gorm:"column:status;default:Active"` // Active, Blocked, Deleted
Configid int `json:"configid" gorm:"column:configid;default:1"`
Lastloginat *time.Time `json:"lastloginat" gorm:"column:lastloginat"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (AppCustomer) TableName() string {
return "appcustomers"
}
type AppCustomerLocation struct {
Appcustomerlocationid int `json:"appcustomerlocationid" gorm:"primaryKey;column:appcustomerlocationid"`
Appcustomerid int `json:"appcustomerid" gorm:"column:appcustomerid"`
Label string `json:"label" gorm:"column:label;default:Home"`
Receivername string `json:"receivername" gorm:"column:receivername"`
Receiverphone string `json:"receiverphone" gorm:"column:receiverphone"`
Address string `json:"address" gorm:"column:address;not null"`
Landmark string `json:"landmark" gorm:"column:landmark"`
City string `json:"city" gorm:"column:city"`
State string `json:"state" gorm:"column:state"`
Pincode string `json:"pincode" gorm:"column:pincode;not null"`
Latitude float64 `json:"latitude" gorm:"column:latitude;not null"`
Longitude float64 `json:"longitude" gorm:"column:longitude;not null"`
Isdefault bool `json:"isdefault" gorm:"column:isdefault;default:false"`
Status string `json:"status" gorm:"column:status;default:Active"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (AppCustomerLocation) TableName() string {
return "appcustomerlocations"
}
type TenantLocation struct {
Tenantlocationid int `json:"tenantlocationid" gorm:"primaryKey;column:tenantlocationid"`
Tenantid int `json:"tenantid" gorm:"column:tenantid;not null"`
Locationname string `json:"locationname" gorm:"column:locationname"`
Address string `json:"address" gorm:"column:address;not null"`
City string `json:"city" gorm:"column:city;not null"`
State string `json:"state" gorm:"column:state;not null"`
Pincode string `json:"pincode" gorm:"column:pincode;not null"`
Latitude float64 `json:"latitude" gorm:"column:latitude;not null"`
Longitude float64 `json:"longitude" gorm:"column:longitude;not null"`
Isprimary bool `json:"isprimary" gorm:"column:isprimary;default:false"`
Status string `json:"status" gorm:"column:status;default:Active"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (TenantLocation) TableName() string {
return "tenantlocations"
}
type HubStaffAccount struct {
Hubstaffaccountid int `json:"hubstaffaccountid" gorm:"primaryKey;column:hubstaffaccountid"`
Hubid int `json:"hubid" gorm:"column:hubid;index;not null"`
Email string `json:"email" gorm:"column:email;unique;not null"`
Passwordhash string `json:"-" gorm:"column:passwordhash;not null"`
Displayname string `json:"displayname" gorm:"column:displayname"`
Roleid int `json:"roleid" gorm:"column:roleid;default:6"`
Isactive bool `json:"isactive" gorm:"column:isactive;default:true"`
Tenantid *int `json:"tenantid" gorm:"column:tenantid;index"` // null = Doormile staff (can manage any hub in their city); set = partner staff scoped to their own hub
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (HubStaffAccount) TableName() string {
return "hubstaffaccounts"
}
type MilerDutyLog struct {
Dutylogid int `json:"dutylogid" gorm:"primaryKey;column:dutylogid"`
Userid int `json:"userid" gorm:"column:userid;index;not null"`
Loginat time.Time `json:"loginat" gorm:"column:loginat;not null"`
Logoutat *time.Time `json:"logoutat" gorm:"column:logoutat"`
Onduty bool `json:"onduty" gorm:"column:onduty;default:true"`
Startlat float64 `json:"startlat" gorm:"column:startlat"`
Startlon float64 `json:"startlon" gorm:"column:startlon"`
Lastlat float64 `json:"lastlat" gorm:"column:lastlat"`
Lastlon float64 `json:"lastlon" gorm:"column:lastlon"`
Totalkms float64 `json:"totalkms" gorm:"column:totalkms;default:0"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
}
func (MilerDutyLog) TableName() string {
return "milerdutylogs"
}
type MilerBreakLog struct {
Breaklogid int `json:"breaklogid" gorm:"primaryKey;column:breaklogid"`
Userid int `json:"userid" gorm:"column:userid;index;not null"`
Dutylogid int `json:"dutylogid" gorm:"column:dutylogid;not null"`
Breaktype string `json:"breaktype" gorm:"column:breaktype;default:Personal"`
Startat time.Time `json:"startat" gorm:"column:startat;not null"`
Endat *time.Time `json:"endat" gorm:"column:endat"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
}
func (MilerBreakLog) TableName() string {
return "milerbreaklogs"
}
type MilerSupportTicket struct {
Ticketid int `json:"ticketid" gorm:"primaryKey;column:ticketid"`
Userid int `json:"userid" gorm:"column:userid;index;not null"`
Subject string `json:"subject" gorm:"column:subject;size:200;not null"`
Description string `json:"description" gorm:"column:description;type:text;not null"`
Status string `json:"status" gorm:"column:status;default:Open"`
Createdat time.Time `json:"createdat" gorm:"column:createdat;default:CURRENT_TIMESTAMP"`
Updatedat time.Time `json:"updatedat" gorm:"column:updatedat;default:CURRENT_TIMESTAMP"`
}
func (MilerSupportTicket) TableName() string {
return "milersupporttickets"
}