Wire AIS end to end: telemetry ingestion + live map overlay
node-26#9, same shape as the ADS-B commit. Adds POST /telemetry/ais (same node-key auth, same org_id-stamped upsert-by-key pattern, this time by mmsi into a new `vessels` collection) and its docInMyOrg() firestore rule. Frontend gets useVessels() (mirrors useAircraft(), longer staleness window since AIS position reports are minutes apart, not seconds) and an opt-in "Vessels" map overlay. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5537b095df
commit
3d2b722c64
@@ -69,3 +69,57 @@ async def upload_adsb(
|
||||
logger.warning(f"Failed to upsert {collection}/{doc_id} from node {node_id}: {e}")
|
||||
|
||||
return {"ok": True, "count": len(writes)}
|
||||
|
||||
|
||||
class VesselReport(BaseModel):
|
||||
mmsi: str
|
||||
name: Optional[str] = None
|
||||
lat: Optional[float] = None
|
||||
lon: Optional[float] = None
|
||||
speed_kt: Optional[float] = None
|
||||
heading_deg: Optional[float] = None
|
||||
|
||||
|
||||
class AisUploadBody(BaseModel):
|
||||
vessels: List[VesselReport]
|
||||
|
||||
|
||||
@router.post("/ais")
|
||||
async def upload_ais(
|
||||
body: AisUploadBody,
|
||||
decoded: dict = Depends(require_node_service_or_firebase_token),
|
||||
):
|
||||
"""Same shape as /telemetry/adsb, one doc per mmsi in `vessels`."""
|
||||
node_id = decoded.get("node_id")
|
||||
if not node_id:
|
||||
raise HTTPException(400, "This endpoint requires node identity, not a service/admin token.")
|
||||
|
||||
node = await fstore.doc_get_cached("nodes", node_id)
|
||||
org_id = node.get("org_id") if node else None
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
|
||||
writes = []
|
||||
for v in body.vessels:
|
||||
if not v.mmsi:
|
||||
continue
|
||||
doc = {
|
||||
"mmsi": v.mmsi,
|
||||
"node_id": node_id,
|
||||
"name": v.name,
|
||||
"lat": v.lat,
|
||||
"lon": v.lon,
|
||||
"speed_kt": v.speed_kt,
|
||||
"heading_deg": v.heading_deg,
|
||||
"last_seen": now,
|
||||
}
|
||||
if org_id:
|
||||
doc["org_id"] = org_id
|
||||
writes.append(("vessels", v.mmsi, doc))
|
||||
|
||||
for collection, doc_id, doc in writes:
|
||||
try:
|
||||
await fstore.doc_set(collection, doc_id, doc, merge=True)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to upsert {collection}/{doc_id} from node {node_id}: {e}")
|
||||
|
||||
return {"ok": True, "count": len(writes)}
|
||||
|
||||
Reference in New Issue
Block a user