The Dashboard talks to the Gateway over four transports — HTTP REST, Socket.IO telemetry, the redispad command WebSocket, and Janus WebRTC (see Frontend ↔ Gateway Integration). This page is the REST catalog: it enumerates every HTTP-client service and the exact gateway route each method hits, so you can answer “which service owns POST /drone/action/arm?” without grepping. The realtime services are documented separately — WebsocketService (telemetry), VehicleCommandService / GamepadService / GuidedControlService (redispad commands), and JanusService / AppStateService (video + state). AuthService owns the JWT lifecycle; its endpoints are listed here for completeness only.

Conventions every REST service follows

All services are @Injectable({ providedIn: 'root' }) singletons that take Angular HttpClient in the constructor and build URLs by interpolating ${environment.url}/.... There is no shared typed API-client abstraction — each service hardcodes its route strings. environment.url is http://localhost:5000/api/v1 in dev and https://prod.skyhub.ai:5000/api/v1 in prod (src/environments/environment.ts).
  • Auth: the global AuthInterceptor (src/app/auth/auth.interceptor.ts) attaches Authorization: Bearer <accessToken> to every outgoing request and retries once on 401 by refreshing. No service adds auth headers itself (except AuthService.updatePassword, which passes a temporary token).
  • Response shape is inconsistent — some services get a raw payload, others an ApiResponse<T> envelope ({ data, message? }). See Response envelopes below; getting this wrong is the most common integration bug.
  • drone_id is loosely typed — the Drone model declares id: string, but action payloads send it as string or number interchangeably. The gateway accepts both.

DroneService

src/app/services/drone-service/drone.service.ts — fleet CRUD plus the entire /drone/action/* command family, video-room control, VPN config, and flight-controller parameters. It returns raw payloads (no envelope). Backend counterpart: Drone Management & Control Actions.
startMission only includes takeoff_altitude in the body when the argument is defined (drone.service.ts:88). startVideoStream deliberately appends ?update=true — the gateway distinguishes a first-time room creation from a refresh by that query param, so do not drop it. DroneMovement (drone-movement.interface.ts) mirrors a MAVROS SET_POSITION_TARGET_LOCAL_NED message (coordinate_frame, type_mask, position/velocity/accel/yaw).

MissionService

src/app/services/mission.service.ts — mission + waypoint CRUD, returning raw Mission/Mission[]. It defensively coerces mission_points to an array on every read. Backend counterpart: Missions & Geofences API; waypoint semantics live in Mission & Geofence MAVLink Format. updateCurrentMissionPointIndex(...) is a client-side helper only (no HTTP): it advances the active-waypoint index when the vehicle is within 3 m of the next point, used to redraw the AUTO-mode mission line. See Mission & Waypoint Planning.

GeofenceService

src/app/services/geofence.service.ts — geofence + point CRUD, an enable/disable toggle, and batch point reordering. Returns raw Geofence/Geofence[] (array-coerces geofence_points). type is 'polygon' | 'circle' and fence_type is 'inclusion' | 'exclusion'. A circle geofence carries a single center point with its radius in param1.

AssetService

src/app/services/asset.service.ts — photo/video assets stored in S3. baseUrl = ${environment.url}/assets. Responses use a { data } wrapper (AssetListResponse, AssetsByVehicleResponse). Backend counterparts: Executions, Assets & Reports API and S3 Assets, HLS Video & Execution Archives.
Video assets are HLS: getVideoAssetByVehicleIdAndAssetId fetches the raw .m3u8 playlist as text (not JSON), which the video-player dialog feeds to hls.js. Deletion is a POST .../delete with an ids array — there is no per-asset DELETE route.

ExecutionService

src/app/services/execution.service.ts — flight-execution history, archive downloads, and report-email settings. baseUrl = ${environment.url}/executions. Uses typed *Response models. Backend counterpart: Executions, Log Analysis & Reports.
The two report-email methods hang off ${environment.url}/user/report-emails (they use the full env URL, not this.baseUrl). If you rename the executions base, these will not move with it.

ScheduledEventService

src/app/services/scheduled-event.service.ts — calendar events with recurrence (RRULE). baseUrl = ${environment.url}/calendar. Every HTTP call unwraps an ApiResponse<T> via .data, and keeps an events$ BehaviorSubject cache. Backend counterpart: Billing, Calendar, VPN, Video & Isaac Sim API. Recurring events are expanded into “occurrences” client-side. buildRecurrenceRule / parseRecurrenceRule translate the UI’s {type, interval, endDate/endAfter} to and from an RRULE string (FREQ, INTERVAL, UNTIL, COUNT); occurrence edits target the parent event id plus an occurrence_time.

BillingService

src/app/services/billing.service.ts — Stripe subscription and vehicle-limit management. Every call returns ApiResponse<T>. Maintains a subscription$ BehaviorSubject and mirrors active status into localStorage['skyhub_subscription'] via syncSubscriptionCache. Backend counterpart: Stripe Billing & Vehicle Limits. Checkout and portal flows redirect the browser to a Stripe-hosted URL (redirectToCheckout / redirectToPortal set window.location.href). canAddVehicle is the client-side gate before creating a drone; its reason enumerates allowed | no_subscription | subscription_inactive | vehicle_limit_reached. The Stripe publishable key lives in environment.stripePublishableKey (pk_live_xxx) — never a secret key.

IsaacSimService

src/app/services/isaac-sim.service.ts — controls a GPU EC2 instance running NVIDIA Isaac Sim. apiUrl = ${environment.url}/isaac-sim. Gated by the environment.enableIsaacSim feature flag (false by default) and its route is currently commented out in app-routing.module.ts. Polls status every 5 s. Backend counterpart: Users, IP Allocation, Calendar & Isaac Sim.

UserService

src/app/services/user.service.ts — current-user info and the per-user WireGuard VPN config (distinct from the per-drone VPN endpoints on DroneService).

AuthService (endpoints only)

src/app/auth/auth.service.ts owns login/logout, JWT storage, and the background refresh loop — fully documented in Auth: Guard, Interceptor & AuthService and Authentication & JWT Lifecycle. Its routes, for lookup:

Response envelopes

The gateway is not uniform about wrapping responses. Match the pattern per service or response.data will be undefined.

Gotchas to preserve

  • No base-URL helper. Every service inlines ${environment.url}/.... Changing the REST path shape (e.g. dropping /api/v1) silently breaks telemetry too, because WebsocketService derives its Socket.IO base from environment.url.replace('/api/v1', '').
  • Report emails are misfiled. ExecutionService.getReportEmails/updateReportEmails call /user/report-emails off the raw env URL, not the executions baseUrl.
  • ?update=true matters. DroneService.startVideoStream always appends it; the gateway treats plain /start and /start?update=true differently.
  • drone_id type drift. Sent as string or number across action payloads; keep the gateway tolerant of both.
  • Isaac Sim ships disabled. The service and components are bundled but the route is commented out and enableIsaacSim is false.
  • Method-name typo. MissionService.creatMission (missing “e”) is load-bearing across callers — rename with care.

Frontend ↔ Gateway Integration

The four transport channels and how ports 5000/7070/8188 map to them.

Auth: Guard, Interceptor & AuthService

JWT storage, Bearer injection, 401 refresh, and the token contract.

Real-time Telemetry Client

WebsocketService: subscribe_telemetry, stream types, RxJS routing.

Vehicle Commands & Gamepad

redispad WebSocket command path, gamepad, guided control.

App State & Video (Janus/WebRTC)

AppStateService store and JanusService video subscription.

Gateway HTTP API Overview

The backend side of every route listed above, with auth models.