| @@ -1,3 +1,4 @@ | |||||
| import asyncio | |||||
| from fastapi import APIRouter, Depends, HTTPException, Query, Request | from fastapi import APIRouter, Depends, HTTPException, Query, Request | ||||
| import httpx | import httpx | ||||
| import config_env | import config_env | ||||
| @@ -151,10 +152,75 @@ CORE_GET_SYNC = { | |||||
| "/reslevis/getGateways": (gateway_repo, _normalize_gateway), | "/reslevis/getGateways": (gateway_repo, _normalize_gateway), | ||||
| "/reslevis/getZones": (zone_repo, _normalize_zone), | "/reslevis/getZones": (zone_repo, _normalize_zone), | ||||
| "/reslevis/getTrackers": (tracker_repo, _normalize_tracker), | "/reslevis/getTrackers": (tracker_repo, _normalize_tracker), | ||||
| "/reslevis/getTracks": (track_repo, _normalize_track), | |||||
| } | } | ||||
| async def _core_get_json( | |||||
| client: httpx.AsyncClient, | |||||
| path: str, | |||||
| params: Optional[dict] = None, | |||||
| ): | |||||
| resp = await client.get(f"{CORE_BASE_URL}{path}", params=params) | |||||
| if resp.status_code >= 400: | |||||
| detail = resp.text.strip() or "CORE request failed" | |||||
| raise HTTPException(status_code=resp.status_code, detail=detail) | |||||
| try: | |||||
| return resp.json() | |||||
| except ValueError as exc: | |||||
| raise HTTPException(status_code=502, detail="Invalid CORE response") from exc | |||||
| async def _fetch_tracks_for_tracker( | |||||
| client: httpx.AsyncClient, | |||||
| tracker_id: str, | |||||
| params: Optional[dict] = None, | |||||
| ) -> List[dict]: | |||||
| payload = await _core_get_json(client, f"/reslevis/getTracks/{tracker_id}", params=params) | |||||
| if not isinstance(payload, list): | |||||
| raise HTTPException(status_code=502, detail="Unexpected CORE response type") | |||||
| return [_normalize_track(row) for row in payload if isinstance(row, dict)] | |||||
| def _sort_tracks_desc(rows: List[dict]) -> List[dict]: | |||||
| return sorted(rows, key=lambda row: row.get("timestamp") or "", reverse=True) | |||||
| async def _fetch_all_tracks(params: dict) -> List[dict]: | |||||
| async with httpx.AsyncClient(timeout=30.0, verify=False) as client: | |||||
| try: | |||||
| payload = await _core_get_json(client, "/reslevis/getTracks", params=params) | |||||
| if not isinstance(payload, list): | |||||
| raise HTTPException(status_code=502, detail="Unexpected CORE response type") | |||||
| return [_normalize_track(row) for row in payload if isinstance(row, dict)] | |||||
| except HTTPException as exc: | |||||
| if exc.status_code != 404: | |||||
| raise | |||||
| trackers_payload = await _core_get_json(client, "/reslevis/getTrackers") | |||||
| if not isinstance(trackers_payload, list): | |||||
| raise HTTPException(status_code=502, detail="Unexpected CORE tracker response type") | |||||
| tracker_ids = [] | |||||
| for item in trackers_payload: | |||||
| if not isinstance(item, dict): | |||||
| continue | |||||
| tracker_id = item.get("id") | |||||
| if tracker_id: | |||||
| tracker_ids.append(str(tracker_id)) | |||||
| batches = await asyncio.gather( | |||||
| *[_fetch_tracks_for_tracker(client, tracker_id, params) for tracker_id in tracker_ids] | |||||
| ) | |||||
| merged = [row for batch in batches for row in batch] | |||||
| merged = _sort_tracks_desc(merged) | |||||
| limit = params.get("limit") | |||||
| if isinstance(limit, int): | |||||
| return merged[:limit] | |||||
| return merged | |||||
| @router.get( | @router.get( | ||||
| "/getGateways", | "/getGateways", | ||||
| response_model=List[GatewayItem], | response_model=List[GatewayItem], | ||||
| @@ -324,12 +390,27 @@ def removeTrackerZone(tracker_zone_id: str): | |||||
| @router.get( | @router.get( | ||||
| "/getTracks", | "/getTracks", | ||||
| response_model=List[TrackItem], | |||||
| response_model=List[TrackHistoryItem], | |||||
| tags=["Reslevis"], | tags=["Reslevis"], | ||||
| dependencies=[Depends(get_current_user)], | dependencies=[Depends(get_current_user)], | ||||
| ) | ) | ||||
| def getTracks(): | |||||
| return track_repo.list() | |||||
| async def getTracks( | |||||
| limit: Optional[int] = Query(None, ge=1), | |||||
| from_: Optional[str] = Query(None, alias="from"), | |||||
| to: Optional[str] = Query(None), | |||||
| ): | |||||
| params = {} | |||||
| if limit is not None: | |||||
| params["limit"] = limit | |||||
| if from_: | |||||
| params["from"] = from_ | |||||
| if to: | |||||
| params["to"] = to | |||||
| try: | |||||
| return await _fetch_all_tracks(params) | |||||
| except httpx.RequestError as exc: | |||||
| raise HTTPException(status_code=502, detail=f"CORE request failed: {exc}") from exc | |||||
| @router.get( | @router.get( | ||||
| @@ -354,27 +435,10 @@ async def getTrack( | |||||
| try: | try: | ||||
| async with httpx.AsyncClient(timeout=30.0, verify=False) as client: | async with httpx.AsyncClient(timeout=30.0, verify=False) as client: | ||||
| resp = await client.get( | |||||
| f"{CORE_BASE_URL}/reslevis/getTracks/{tracker_id}", | |||||
| params=params, | |||||
| ) | |||||
| return await _fetch_tracks_for_tracker(client, tracker_id, params) | |||||
| except httpx.RequestError as exc: | except httpx.RequestError as exc: | ||||
| raise HTTPException(status_code=502, detail=f"CORE request failed: {exc}") from exc | raise HTTPException(status_code=502, detail=f"CORE request failed: {exc}") from exc | ||||
| if resp.status_code >= 400: | |||||
| detail = resp.text.strip() or "CORE request failed" | |||||
| raise HTTPException(status_code=resp.status_code, detail=detail) | |||||
| try: | |||||
| payload = resp.json() | |||||
| except ValueError as exc: | |||||
| raise HTTPException(status_code=502, detail="Invalid CORE response") from exc | |||||
| if not isinstance(payload, list): | |||||
| raise HTTPException(status_code=502, detail="Unexpected CORE response type") | |||||
| return [_normalize_track(row) for row in payload if isinstance(row, dict)] | |||||
| @router.post("/postTrack", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) | @router.post("/postTrack", tags=["Reslevis"], dependencies=[Depends(get_current_user)]) | ||||
| def postTrack(item: TrackItem): | def postTrack(item: TrackItem): | ||||