| @@ -1,3 +1,4 @@ | |||
| import asyncio | |||
| from fastapi import APIRouter, Depends, HTTPException, Query, Request | |||
| import httpx | |||
| import config_env | |||
| @@ -151,10 +152,75 @@ CORE_GET_SYNC = { | |||
| "/reslevis/getGateways": (gateway_repo, _normalize_gateway), | |||
| "/reslevis/getZones": (zone_repo, _normalize_zone), | |||
| "/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( | |||
| "/getGateways", | |||
| response_model=List[GatewayItem], | |||
| @@ -324,12 +390,27 @@ def removeTrackerZone(tracker_zone_id: str): | |||
| @router.get( | |||
| "/getTracks", | |||
| response_model=List[TrackItem], | |||
| response_model=List[TrackHistoryItem], | |||
| tags=["Reslevis"], | |||
| 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( | |||
| @@ -354,27 +435,10 @@ async def getTrack( | |||
| try: | |||
| 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: | |||
| 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)]) | |||
| def postTrack(item: TrackItem): | |||