- Created a new SVG file for login devices with detailed paths and styles. - Added a new SVG file for the passkey icon with multiple vector paths and fill attributes.
3142 lines
83 KiB
YAML
3142 lines
83 KiB
YAML
openapi: 3.0.3
|
||
info:
|
||
title: RideBus DE API
|
||
version: 1.0.0-draft
|
||
description: |
|
||
Черновик OpenAPI 3.0, восстановленный по реальному коду проекта.
|
||
В проекте нет встроенного swagger/openapi генератора, поэтому часть схем описана
|
||
по фактическим SQL-запросам и runtime-поведению контроллеров.
|
||
servers:
|
||
- url: http://localhost:3000
|
||
description: Local backend
|
||
tags:
|
||
- name: System
|
||
- name: Auth
|
||
- name: Upload
|
||
- name: Routes
|
||
- name: Route Stops
|
||
- name: Schedules
|
||
- name: Holidays
|
||
- name: Stops
|
||
- name: ETA
|
||
- name: Sync
|
||
- name: Alerts
|
||
- name: Users
|
||
- name: Telemetry
|
||
- name: Passenger Telemetry
|
||
- name: Realtime
|
||
paths:
|
||
/health:
|
||
get:
|
||
tags: [System]
|
||
summary: Health check
|
||
security: []
|
||
responses:
|
||
'200':
|
||
description: Service status
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [status, timestamp, version]
|
||
properties:
|
||
status:
|
||
type: string
|
||
example: healthy
|
||
timestamp:
|
||
type: string
|
||
format: date-time
|
||
version:
|
||
type: string
|
||
example: 1.0.0
|
||
/api/v1/auth/login:
|
||
post:
|
||
tags: [Auth]
|
||
summary: Login
|
||
security: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/LoginRequest'
|
||
responses:
|
||
'200':
|
||
description: Authenticated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/LoginResponse'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/auth/refresh:
|
||
post:
|
||
tags: [Auth]
|
||
summary: Refresh access token
|
||
security: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/RefreshRequest'
|
||
responses:
|
||
'200':
|
||
description: New access token
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/RefreshResponse'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/auth/logout:
|
||
post:
|
||
tags: [Auth]
|
||
summary: Logout by refresh token
|
||
security: []
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/LogoutRequest'
|
||
responses:
|
||
'200':
|
||
description: Logged out
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/auth/me:
|
||
get:
|
||
tags: [Auth]
|
||
summary: Current user
|
||
security:
|
||
- BearerAuth: []
|
||
responses:
|
||
'200':
|
||
description: Current authenticated user
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UserMe'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/auth/change-password:
|
||
post:
|
||
tags: [Auth]
|
||
summary: Change password
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ChangePasswordRequest'
|
||
responses:
|
||
'200':
|
||
description: Password changed
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/upload/avatar:
|
||
post:
|
||
tags: [Upload]
|
||
summary: Upload or replace current user avatar
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
multipart/form-data:
|
||
schema:
|
||
type: object
|
||
required: [avatar]
|
||
properties:
|
||
avatar:
|
||
type: string
|
||
format: binary
|
||
responses:
|
||
'200':
|
||
description: Avatar uploaded
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/AvatarUploadResponse'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
delete:
|
||
tags: [Upload]
|
||
summary: Delete current user avatar
|
||
security:
|
||
- BearerAuth: []
|
||
responses:
|
||
'200':
|
||
description: Avatar deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/upload/avatar/{userId}:
|
||
delete:
|
||
tags: [Upload]
|
||
summary: Admin deletes any user avatar
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/UserId'
|
||
responses:
|
||
'200':
|
||
description: Avatar deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/routes:
|
||
get:
|
||
tags: [Routes]
|
||
summary: List active routes
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
responses:
|
||
'200':
|
||
description: Route list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/RouteSummary'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
post:
|
||
tags: [Routes]
|
||
summary: Create route
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CreateRouteRequest'
|
||
responses:
|
||
'201':
|
||
description: Route created
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Route'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/routes/{id}:
|
||
get:
|
||
tags: [Routes]
|
||
summary: Get route detail with stops
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Route detail
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/RouteDetail'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
put:
|
||
tags: [Routes]
|
||
summary: Update route
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UpdateRouteRequest'
|
||
responses:
|
||
'200':
|
||
description: Updated route
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Route'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
delete:
|
||
tags: [Routes]
|
||
summary: Delete route
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Route deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/routes/{routeId}/stops:
|
||
get:
|
||
tags: [Route Stops]
|
||
summary: Get ordered stops for route
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/RouteId'
|
||
responses:
|
||
'200':
|
||
description: Route stop list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/RouteStopView'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
post:
|
||
tags: [Route Stops]
|
||
summary: Add stop to route
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/RouteId'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/AddRouteStopRequest'
|
||
responses:
|
||
'201':
|
||
description: Route stop created
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/RouteStopRecord'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/routes/{routeId}/available-stops:
|
||
get:
|
||
tags: [Route Stops]
|
||
summary: Stops not yet assigned to route
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/RouteId'
|
||
responses:
|
||
'200':
|
||
description: Available stops
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/AvailableStop'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/routes/{routeId}/stops/{routeStopId}:
|
||
put:
|
||
tags: [Route Stops]
|
||
summary: Update route stop position or offset
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/RouteId'
|
||
- $ref: '#/components/parameters/RouteStopId'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UpdateRouteStopRequest'
|
||
responses:
|
||
'200':
|
||
description: Updated route stop
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/RouteStopRecord'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
delete:
|
||
tags: [Route Stops]
|
||
summary: Remove stop from route
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/RouteId'
|
||
- $ref: '#/components/parameters/RouteStopId'
|
||
responses:
|
||
'200':
|
||
description: Route stop deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/routes/{routeId}/stops-reorder:
|
||
put:
|
||
tags: [Route Stops]
|
||
summary: Bulk reorder route stops
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/RouteId'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ReorderRouteStopsRequest'
|
||
responses:
|
||
'200':
|
||
description: Route stops reordered
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [message, count]
|
||
properties:
|
||
message:
|
||
type: string
|
||
count:
|
||
type: integer
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/routes/{routeId}/schedules:
|
||
get:
|
||
tags: [Schedules]
|
||
summary: List route schedules
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/RouteId'
|
||
responses:
|
||
'200':
|
||
description: Schedules for route
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ScheduleWithRoute'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/routes/{routeId}/schedule/today:
|
||
get:
|
||
tags: [Schedules]
|
||
summary: Get active schedule for date
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/RouteId'
|
||
- in: query
|
||
name: date
|
||
schema:
|
||
type: string
|
||
format: date
|
||
required: false
|
||
responses:
|
||
'200':
|
||
description: Schedule for date
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/TodayScheduleResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/routes/{routeId}/schedules/copy:
|
||
post:
|
||
tags: [Schedules]
|
||
summary: Copy schedule from one day type to another
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/RouteId'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CopyScheduleRequest'
|
||
responses:
|
||
'200':
|
||
description: Existing schedule updated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Schedule'
|
||
'201':
|
||
description: New schedule created
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Schedule'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/schedules:
|
||
post:
|
||
tags: [Schedules]
|
||
summary: Create schedule
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CreateScheduleRequest'
|
||
responses:
|
||
'201':
|
||
description: Schedule created
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Schedule'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'409':
|
||
description: Schedule already exists
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
error:
|
||
type: string
|
||
message:
|
||
type: string
|
||
existingId:
|
||
type: integer
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/schedules/{id}:
|
||
get:
|
||
tags: [Schedules]
|
||
summary: Get schedule by id
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Schedule detail
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ScheduleWithRoute'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
put:
|
||
tags: [Schedules]
|
||
summary: Update schedule
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UpdateScheduleRequest'
|
||
responses:
|
||
'200':
|
||
description: Schedule updated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Schedule'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
delete:
|
||
tags: [Schedules]
|
||
summary: Delete schedule
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Schedule deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/holidays:
|
||
get:
|
||
tags: [Holidays]
|
||
summary: List holidays
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: query
|
||
name: year
|
||
schema:
|
||
type: integer
|
||
required: false
|
||
responses:
|
||
'200':
|
||
description: Holiday list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Holiday'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
post:
|
||
tags: [Holidays]
|
||
summary: Create holiday
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CreateHolidayRequest'
|
||
responses:
|
||
'201':
|
||
description: Holiday created
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Holiday'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/holidays/upcoming:
|
||
get:
|
||
tags: [Holidays]
|
||
summary: Get upcoming holidays
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: query
|
||
name: limit
|
||
schema:
|
||
type: integer
|
||
required: false
|
||
responses:
|
||
'200':
|
||
description: Upcoming holidays
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/UpcomingHoliday'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/holidays/year/{year}:
|
||
get:
|
||
tags: [Holidays]
|
||
summary: Get holidays for year with resolved recurring dates
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: path
|
||
name: year
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
responses:
|
||
'200':
|
||
description: Holidays for year
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/HolidayForYear'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/holidays/check/{date}:
|
||
get:
|
||
tags: [Holidays]
|
||
summary: Check if date is holiday
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: path
|
||
name: date
|
||
required: true
|
||
schema:
|
||
type: string
|
||
format: date
|
||
responses:
|
||
'200':
|
||
description: Holiday check
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [date, isHoliday, holidayName]
|
||
properties:
|
||
date:
|
||
type: string
|
||
format: date
|
||
isHoliday:
|
||
type: boolean
|
||
holidayName:
|
||
type: string
|
||
nullable: true
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/holidays/{id}:
|
||
put:
|
||
tags: [Holidays]
|
||
summary: Update holiday
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UpdateHolidayRequest'
|
||
responses:
|
||
'200':
|
||
description: Holiday updated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Holiday'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
delete:
|
||
tags: [Holidays]
|
||
summary: Delete holiday
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Holiday deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/stops:
|
||
get:
|
||
tags: [Stops]
|
||
summary: List active stops
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
responses:
|
||
'200':
|
||
description: Stop list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/StopSummary'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
post:
|
||
tags: [Stops]
|
||
summary: Create stop
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CreateStopRequest'
|
||
responses:
|
||
'201':
|
||
description: Stop created
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Stop'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/stops/nearby:
|
||
get:
|
||
tags: [Stops]
|
||
summary: Find nearby stops
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: query
|
||
name: lat
|
||
required: true
|
||
schema:
|
||
type: number
|
||
- in: query
|
||
name: lon
|
||
required: true
|
||
schema:
|
||
type: number
|
||
- in: query
|
||
name: radius
|
||
required: false
|
||
schema:
|
||
type: number
|
||
default: 500
|
||
responses:
|
||
'200':
|
||
description: Nearby stops
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Stop'
|
||
- type: object
|
||
properties:
|
||
distance_meters:
|
||
type: number
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/stops/{id}:
|
||
get:
|
||
tags: [Stops]
|
||
summary: Get stop detail
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Stop detail
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/StopDetail'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
put:
|
||
tags: [Stops]
|
||
summary: Update stop
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UpdateStopRequest'
|
||
responses:
|
||
'200':
|
||
description: Stop updated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Stop'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
delete:
|
||
tags: [Stops]
|
||
summary: Delete stop
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Stop deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/eta/calculate:
|
||
post:
|
||
tags: [ETA]
|
||
summary: Calculate ETA for route+stop
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CalculateEtaRequest'
|
||
responses:
|
||
'200':
|
||
description: ETA calculated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
oneOf:
|
||
- $ref: '#/components/schemas/CalculateEtaResponse'
|
||
- type: object
|
||
properties:
|
||
message:
|
||
type: string
|
||
nextDay:
|
||
type: string
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/eta/stop/{stopId}:
|
||
get:
|
||
tags: [ETA]
|
||
summary: ETA for all routes at stop
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: path
|
||
name: stopId
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
responses:
|
||
'200':
|
||
description: ETA list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/StopEtasResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/sync:
|
||
get:
|
||
tags: [Sync]
|
||
summary: Full or incremental sync
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: query
|
||
name: lastSync
|
||
required: false
|
||
schema:
|
||
type: string
|
||
format: date-time
|
||
responses:
|
||
'200':
|
||
description: Sync payload
|
||
content:
|
||
application/json:
|
||
schema:
|
||
oneOf:
|
||
- $ref: '#/components/schemas/FullSyncResponse'
|
||
- $ref: '#/components/schemas/IncrementalSyncResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/sync/status:
|
||
get:
|
||
tags: [Sync]
|
||
summary: Sync status
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
responses:
|
||
'200':
|
||
description: Status payload
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/SyncStatusResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/alerts:
|
||
get:
|
||
tags: [Alerts]
|
||
summary: List alerts
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: query
|
||
name: route_id
|
||
required: false
|
||
schema:
|
||
type: integer
|
||
- in: query
|
||
name: is_active
|
||
required: false
|
||
schema:
|
||
type: boolean
|
||
- in: query
|
||
name: alert_type
|
||
required: false
|
||
schema:
|
||
$ref: '#/components/schemas/AlertType'
|
||
responses:
|
||
'200':
|
||
description: Alert list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/AlertRead'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
post:
|
||
tags: [Alerts]
|
||
summary: Create alert
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: header
|
||
name: Idempotency-Key
|
||
required: false
|
||
description: Stable key for retrying one logical alert creation without duplicate alerts or pushes
|
||
schema:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 255
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CreateAlertRequest'
|
||
responses:
|
||
'201':
|
||
description: Alert created
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Alert'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'409':
|
||
description: Idempotency-Key was already used with a different request payload
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ErrorResponse'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/alerts/active:
|
||
get:
|
||
tags: [Alerts]
|
||
summary: List currently active alerts
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: query
|
||
name: route_id
|
||
required: false
|
||
schema:
|
||
type: integer
|
||
responses:
|
||
'200':
|
||
description: Active alerts
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/AlertRead'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/alerts/cleanup:
|
||
post:
|
||
tags: [Alerts]
|
||
summary: Deactivate expired alerts
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
responses:
|
||
'200':
|
||
description: Cleanup stats
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [message, deactivated_count]
|
||
properties:
|
||
message:
|
||
type: string
|
||
deactivated_count:
|
||
type: integer
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/alerts/{id}:
|
||
get:
|
||
tags: [Alerts]
|
||
summary: Get alert by id
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Alert detail
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/AlertRead'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
put:
|
||
tags: [Alerts]
|
||
summary: Update alert
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UpdateAlertRequest'
|
||
responses:
|
||
'200':
|
||
description: Alert updated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Alert'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
delete:
|
||
tags: [Alerts]
|
||
summary: Delete alert
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: Alert deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/users:
|
||
get:
|
||
tags: [Users]
|
||
summary: List users
|
||
security:
|
||
- BearerAuth: []
|
||
responses:
|
||
'200':
|
||
description: User list
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/UserAdminView'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
post:
|
||
tags: [Users]
|
||
summary: Create user
|
||
security:
|
||
- BearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/CreateUserRequest'
|
||
responses:
|
||
'201':
|
||
description: User created
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UserCreated'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/users/{id}:
|
||
get:
|
||
tags: [Users]
|
||
summary: Get user by id
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: User detail
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UserAdminView'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
put:
|
||
tags: [Users]
|
||
summary: Update user
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UpdateUserRequest'
|
||
responses:
|
||
'200':
|
||
description: User updated
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/UserUpdated'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
delete:
|
||
tags: [Users]
|
||
summary: Delete user
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
responses:
|
||
'200':
|
||
description: User deleted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/users/{id}/reset-password:
|
||
post:
|
||
tags: [Users]
|
||
summary: Reset user password
|
||
security:
|
||
- BearerAuth: []
|
||
parameters:
|
||
- $ref: '#/components/parameters/Id'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ResetPasswordRequest'
|
||
responses:
|
||
'200':
|
||
description: Password reset
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/MessageResponse'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/telemetry/ingest:
|
||
post:
|
||
tags: [Telemetry]
|
||
summary: Ingest official vehicle GPS telemetry
|
||
description: For driver apps, GPS trackers, and official telemetry only. Passenger apps must use /api/v1/passenger-telemetry/ingest instead.
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/TelemetryIngestRequest'
|
||
example:
|
||
routeId: 7
|
||
vehicleNumber: DEMO-7A
|
||
vehicleType: bus
|
||
lat: 55.4869
|
||
lon: 28.7856
|
||
speedKmh: 24
|
||
heading: 90
|
||
accuracyMeters: 5
|
||
recordedAt: '2026-04-16T10:30:00.000Z'
|
||
responses:
|
||
'201':
|
||
description: Telemetry accepted
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/TelemetryIngestResponse'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/passenger-telemetry/ingest:
|
||
post:
|
||
tags: [Passenger Telemetry]
|
||
summary: Ingest anonymized passenger telemetry
|
||
description: |
|
||
Accepts opt-in, temporary, anonymous passenger geo-signals for short-lived aggregation.
|
||
Raw passenger points are not exposed through public REST APIs or WebSocket messages.
|
||
The server may emit only aggregate approximate virtual vehicles with source=crowdsourced.
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/PassengerTelemetryIngestRequest'
|
||
example:
|
||
session_id: temporary-anonymous-id
|
||
latitude: 55.485
|
||
longitude: 28.768
|
||
accuracy_m: 18.5
|
||
speed_kmh: 32
|
||
heading: 91
|
||
observed_at: '2026-05-20T12:30:00Z'
|
||
route_id: '12'
|
||
stop_id: '123'
|
||
app_state: foreground
|
||
responses:
|
||
'202':
|
||
description: Passenger signal accepted for aggregation
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/PassengerTelemetryIngestResponse'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/realtime/routes/{routeId}/vehicles:
|
||
get:
|
||
tags: [Realtime]
|
||
summary: Live vehicles snapshot for route
|
||
description: Returns official live vehicles plus aggregate approximate crowdsourced virtual vehicles when confidence is sufficient. Raw passenger points are never returned.
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: path
|
||
name: routeId
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
- in: query
|
||
name: includeStale
|
||
schema:
|
||
type: boolean
|
||
- in: query
|
||
name: staleAfterSeconds
|
||
schema:
|
||
type: integer
|
||
default: 180
|
||
responses:
|
||
'200':
|
||
description: Route live vehicles
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/LiveVehicleState'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
/api/v1/realtime/vehicles/{vehicleId}:
|
||
get:
|
||
tags: [Realtime]
|
||
summary: Live vehicle state
|
||
security:
|
||
- BearerAuth: []
|
||
- ApiKeyAuth: []
|
||
parameters:
|
||
- in: path
|
||
name: vehicleId
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
- in: query
|
||
name: staleAfterSeconds
|
||
schema:
|
||
type: integer
|
||
default: 180
|
||
responses:
|
||
'200':
|
||
description: Vehicle live state
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/LiveVehicleState'
|
||
'400':
|
||
$ref: '#/components/responses/BadRequest'
|
||
'401':
|
||
$ref: '#/components/responses/Unauthorized'
|
||
'403':
|
||
$ref: '#/components/responses/Forbidden'
|
||
'404':
|
||
$ref: '#/components/responses/NotFound'
|
||
'429':
|
||
$ref: '#/components/responses/TooManyRequests'
|
||
'500':
|
||
$ref: '#/components/responses/InternalError'
|
||
components:
|
||
securitySchemes:
|
||
ApiKeyAuth:
|
||
type: apiKey
|
||
in: header
|
||
name: X-API-Key
|
||
BearerAuth:
|
||
type: http
|
||
scheme: bearer
|
||
bearerFormat: JWT
|
||
parameters:
|
||
Id:
|
||
in: path
|
||
name: id
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
RouteId:
|
||
in: path
|
||
name: routeId
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
RouteStopId:
|
||
in: path
|
||
name: routeStopId
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
UserId:
|
||
in: path
|
||
name: userId
|
||
required: true
|
||
schema:
|
||
type: integer
|
||
responses:
|
||
BadRequest:
|
||
description: Bad request
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ErrorResponse'
|
||
Unauthorized:
|
||
description: Authentication required or invalid credentials
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ErrorResponse'
|
||
Forbidden:
|
||
description: Forbidden
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ErrorResponse'
|
||
NotFound:
|
||
description: Not found
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ErrorResponse'
|
||
TooManyRequests:
|
||
description: Too many requests
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
error:
|
||
type: string
|
||
message:
|
||
type: string
|
||
retryAfter:
|
||
type: integer
|
||
InternalError:
|
||
description: Internal server error
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/ErrorResponse'
|
||
schemas:
|
||
ErrorResponse:
|
||
type: object
|
||
properties:
|
||
error:
|
||
type: string
|
||
message:
|
||
type: string
|
||
MessageResponse:
|
||
type: object
|
||
required: [message]
|
||
properties:
|
||
message:
|
||
type: string
|
||
RouteType:
|
||
type: string
|
||
enum: [bus, minibus, trolleybus, tram]
|
||
ScheduleDayType:
|
||
type: string
|
||
enum: [weekday, saturday, sunday, holiday]
|
||
ScheduleTrip:
|
||
type: object
|
||
required: [departureTime]
|
||
properties:
|
||
departureTime:
|
||
type: string
|
||
description: Departure time in HH:MM or HH:MM:SS format.
|
||
startStopId:
|
||
type: integer
|
||
nullable: true
|
||
description: Optional stop id where a partial trip starts.
|
||
endStopId:
|
||
type: integer
|
||
nullable: true
|
||
description: Optional stop id where a partial trip ends.
|
||
viaStopIds:
|
||
type: array
|
||
items:
|
||
type: integer
|
||
description: Optional via-stop markers for display and route variants.
|
||
daysOfWeek:
|
||
type: array
|
||
items:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 7
|
||
description: ISO weekdays when this trip runs, 1=Monday, 7=Sunday.
|
||
exceptDaysOfWeek:
|
||
type: array
|
||
items:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 7
|
||
description: ISO weekdays when this trip does not run.
|
||
note:
|
||
type: string
|
||
description: Human-readable note such as "по пятницам" or "через Мариненко".
|
||
AlertType:
|
||
type: string
|
||
enum: [delay, cancellation, detour, info]
|
||
UserRole:
|
||
type: string
|
||
enum: [admin, user]
|
||
Route:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
route_number:
|
||
type: string
|
||
name:
|
||
type: string
|
||
type:
|
||
$ref: '#/components/schemas/RouteType'
|
||
color:
|
||
type: string
|
||
is_active:
|
||
type: boolean
|
||
description:
|
||
type: string
|
||
nullable: true
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
updated_at:
|
||
type: string
|
||
format: date-time
|
||
RouteSummary:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Route'
|
||
- type: object
|
||
properties:
|
||
stops_count:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
Stop:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
name:
|
||
type: string
|
||
location:
|
||
description: Raw PostGIS geography serialization; exact shape is not guaranteed
|
||
nullable: true
|
||
latitude:
|
||
type: number
|
||
longitude:
|
||
type: number
|
||
address:
|
||
type: string
|
||
nullable: true
|
||
description:
|
||
type: string
|
||
nullable: true
|
||
is_active:
|
||
type: boolean
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
updated_at:
|
||
type: string
|
||
format: date-time
|
||
StopSummary:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Stop'
|
||
- type: object
|
||
properties:
|
||
routes_count:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
StopOnRoute:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Stop'
|
||
- type: object
|
||
properties:
|
||
sequence:
|
||
type: integer
|
||
time_offset_minutes:
|
||
type: integer
|
||
RouteDetail:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Route'
|
||
- type: object
|
||
properties:
|
||
stops:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/StopOnRoute'
|
||
RouteAtStop:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Route'
|
||
- type: object
|
||
properties:
|
||
sequence:
|
||
type: integer
|
||
time_offset_minutes:
|
||
type: integer
|
||
StopDetail:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Stop'
|
||
- type: object
|
||
properties:
|
||
routes:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/RouteAtStop'
|
||
RouteStopRecord:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
route_id:
|
||
type: integer
|
||
stop_id:
|
||
type: integer
|
||
sequence:
|
||
type: integer
|
||
time_offset_minutes:
|
||
type: integer
|
||
RouteStopView:
|
||
type: object
|
||
properties:
|
||
route_stop_id:
|
||
type: integer
|
||
sequence:
|
||
type: integer
|
||
time_offset_minutes:
|
||
type: integer
|
||
stop_id:
|
||
type: integer
|
||
name:
|
||
type: string
|
||
address:
|
||
type: string
|
||
nullable: true
|
||
latitude:
|
||
type: number
|
||
longitude:
|
||
type: number
|
||
AvailableStop:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
name:
|
||
type: string
|
||
address:
|
||
type: string
|
||
nullable: true
|
||
latitude:
|
||
type: number
|
||
longitude:
|
||
type: number
|
||
Schedule:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
route_id:
|
||
type: integer
|
||
day_type:
|
||
$ref: '#/components/schemas/ScheduleDayType'
|
||
departure_times:
|
||
type: array
|
||
items:
|
||
type: string
|
||
trips:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ScheduleTrip'
|
||
valid_from:
|
||
type: string
|
||
format: date
|
||
nullable: true
|
||
valid_until:
|
||
type: string
|
||
format: date
|
||
nullable: true
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
updated_at:
|
||
type: string
|
||
format: date-time
|
||
ScheduleWithRoute:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Schedule'
|
||
- type: object
|
||
properties:
|
||
route_number:
|
||
type: string
|
||
route_name:
|
||
type: string
|
||
TodayScheduleResponse:
|
||
type: object
|
||
properties:
|
||
date:
|
||
type: string
|
||
format: date
|
||
scheduleType:
|
||
$ref: '#/components/schemas/ScheduleDayType'
|
||
isHoliday:
|
||
type: boolean
|
||
holidayName:
|
||
type: string
|
||
nullable: true
|
||
schedule:
|
||
$ref: '#/components/schemas/ScheduleWithRoute'
|
||
Holiday:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
date:
|
||
type: string
|
||
format: date
|
||
name:
|
||
type: string
|
||
is_recurring:
|
||
type: boolean
|
||
recurring_month:
|
||
type: integer
|
||
nullable: true
|
||
recurring_day:
|
||
type: integer
|
||
nullable: true
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
HolidayForYear:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
date:
|
||
type: string
|
||
format: date
|
||
name:
|
||
type: string
|
||
is_recurring:
|
||
type: boolean
|
||
UpcomingHoliday:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
name:
|
||
type: string
|
||
next_date:
|
||
type: string
|
||
format: date
|
||
is_recurring:
|
||
type: boolean
|
||
Alert:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
route_id:
|
||
type: integer
|
||
nullable: true
|
||
alert_type:
|
||
$ref: '#/components/schemas/AlertType'
|
||
title:
|
||
type: string
|
||
message:
|
||
type: string
|
||
start_time:
|
||
type: string
|
||
format: date-time
|
||
end_time:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
is_active:
|
||
type: boolean
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
AlertRead:
|
||
allOf:
|
||
- $ref: '#/components/schemas/Alert'
|
||
- type: object
|
||
properties:
|
||
route_name:
|
||
type: string
|
||
nullable: true
|
||
route_number:
|
||
type: string
|
||
nullable: true
|
||
color:
|
||
type: string
|
||
nullable: true
|
||
UserLoginPayload:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
username:
|
||
type: string
|
||
full_name:
|
||
type: string
|
||
nullable: true
|
||
email:
|
||
type: string
|
||
nullable: true
|
||
role:
|
||
$ref: '#/components/schemas/UserRole'
|
||
is_active:
|
||
type: boolean
|
||
last_login:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
updated_at:
|
||
type: string
|
||
format: date-time
|
||
avatar_url:
|
||
type: string
|
||
nullable: true
|
||
UserMe:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
username:
|
||
type: string
|
||
full_name:
|
||
type: string
|
||
nullable: true
|
||
email:
|
||
type: string
|
||
nullable: true
|
||
role:
|
||
$ref: '#/components/schemas/UserRole'
|
||
avatar_url:
|
||
type: string
|
||
nullable: true
|
||
last_login:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
UserAdminView:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
username:
|
||
type: string
|
||
full_name:
|
||
type: string
|
||
nullable: true
|
||
email:
|
||
type: string
|
||
nullable: true
|
||
role:
|
||
$ref: '#/components/schemas/UserRole'
|
||
is_active:
|
||
type: boolean
|
||
last_login:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
UserCreated:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
username:
|
||
type: string
|
||
full_name:
|
||
type: string
|
||
nullable: true
|
||
email:
|
||
type: string
|
||
nullable: true
|
||
role:
|
||
$ref: '#/components/schemas/UserRole'
|
||
is_active:
|
||
type: boolean
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
UserUpdated:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
username:
|
||
type: string
|
||
full_name:
|
||
type: string
|
||
nullable: true
|
||
email:
|
||
type: string
|
||
nullable: true
|
||
role:
|
||
$ref: '#/components/schemas/UserRole'
|
||
is_active:
|
||
type: boolean
|
||
Arrival:
|
||
type: object
|
||
properties:
|
||
scheduledTime:
|
||
type: string
|
||
estimatedTime:
|
||
type: string
|
||
minutesUntil:
|
||
type: integer
|
||
delay:
|
||
type: integer
|
||
CalculateEtaResponse:
|
||
type: object
|
||
properties:
|
||
routeId:
|
||
type: integer
|
||
stopId:
|
||
type: integer
|
||
currentTime:
|
||
type: string
|
||
dayType:
|
||
$ref: '#/components/schemas/ScheduleDayType'
|
||
arrivals:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Arrival'
|
||
StopEtasRoute:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
route_number:
|
||
type: string
|
||
name:
|
||
type: string
|
||
type:
|
||
$ref: '#/components/schemas/RouteType'
|
||
color:
|
||
type: string
|
||
arrivals:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Arrival'
|
||
error:
|
||
type: string
|
||
nullable: true
|
||
StopEtasResponse:
|
||
type: object
|
||
properties:
|
||
stopId:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
routes:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/StopEtasRoute'
|
||
FullSyncResponse:
|
||
type: object
|
||
properties:
|
||
syncType:
|
||
type: string
|
||
enum: [full]
|
||
timestamp:
|
||
type: string
|
||
format: date-time
|
||
version:
|
||
type: string
|
||
data:
|
||
type: object
|
||
properties:
|
||
routes:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Route'
|
||
stops:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Stop'
|
||
routeStops:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/RouteStopRecord'
|
||
schedules:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Schedule'
|
||
alerts:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Alert'
|
||
holidays:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Holiday'
|
||
metadata:
|
||
type: object
|
||
properties:
|
||
routesCount:
|
||
type: integer
|
||
stopsCount:
|
||
type: integer
|
||
schedulesCount:
|
||
type: integer
|
||
holidaysCount:
|
||
type: integer
|
||
IncrementalSyncResponse:
|
||
type: object
|
||
properties:
|
||
syncType:
|
||
type: string
|
||
enum: [incremental]
|
||
timestamp:
|
||
type: string
|
||
format: date-time
|
||
lastSync:
|
||
type: string
|
||
format: date-time
|
||
changes:
|
||
type: object
|
||
properties:
|
||
routes:
|
||
$ref: '#/components/schemas/SyncChangeBucketRoutes'
|
||
stops:
|
||
$ref: '#/components/schemas/SyncChangeBucketStops'
|
||
routeStops:
|
||
$ref: '#/components/schemas/SyncChangeBucketRouteStops'
|
||
schedules:
|
||
$ref: '#/components/schemas/SyncChangeBucketSchedules'
|
||
alerts:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Alert'
|
||
metadata:
|
||
type: object
|
||
properties:
|
||
changesCount:
|
||
type: integer
|
||
SyncChangeBucketRoutes:
|
||
type: object
|
||
properties:
|
||
updated:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Route'
|
||
deleted:
|
||
type: array
|
||
items:
|
||
type: integer
|
||
SyncChangeBucketStops:
|
||
type: object
|
||
properties:
|
||
updated:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Stop'
|
||
deleted:
|
||
type: array
|
||
items:
|
||
type: integer
|
||
SyncChangeBucketRouteStops:
|
||
type: object
|
||
properties:
|
||
updated:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/RouteStopRecord'
|
||
deleted:
|
||
type: array
|
||
items:
|
||
type: integer
|
||
SyncChangeBucketSchedules:
|
||
type: object
|
||
properties:
|
||
updated:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/Schedule'
|
||
deleted:
|
||
type: array
|
||
items:
|
||
type: integer
|
||
SyncStatusResponse:
|
||
type: object
|
||
properties:
|
||
status:
|
||
type: string
|
||
timestamp:
|
||
type: string
|
||
format: date-time
|
||
database:
|
||
type: object
|
||
properties:
|
||
routes_count:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
stops_count:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
schedules_count:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
last_change:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
LoginRequest:
|
||
type: object
|
||
required: [username, password]
|
||
properties:
|
||
username:
|
||
type: string
|
||
password:
|
||
type: string
|
||
LoginResponse:
|
||
type: object
|
||
properties:
|
||
user:
|
||
$ref: '#/components/schemas/UserLoginPayload'
|
||
accessToken:
|
||
type: string
|
||
refreshToken:
|
||
type: string
|
||
RefreshRequest:
|
||
type: object
|
||
required: [refreshToken]
|
||
properties:
|
||
refreshToken:
|
||
type: string
|
||
RefreshResponse:
|
||
type: object
|
||
required: [accessToken]
|
||
properties:
|
||
accessToken:
|
||
type: string
|
||
LogoutRequest:
|
||
type: object
|
||
properties:
|
||
refreshToken:
|
||
type: string
|
||
ChangePasswordRequest:
|
||
type: object
|
||
required: [currentPassword, newPassword]
|
||
properties:
|
||
currentPassword:
|
||
type: string
|
||
newPassword:
|
||
type: string
|
||
minLength: 6
|
||
refreshToken:
|
||
type: string
|
||
CreateRouteRequest:
|
||
type: object
|
||
required: [route_number, name, type]
|
||
properties:
|
||
route_number:
|
||
type: string
|
||
name:
|
||
type: string
|
||
type:
|
||
$ref: '#/components/schemas/RouteType'
|
||
color:
|
||
type: string
|
||
default: '#0066CC'
|
||
description:
|
||
type: string
|
||
UpdateRouteRequest:
|
||
type: object
|
||
properties:
|
||
route_number:
|
||
type: string
|
||
name:
|
||
type: string
|
||
type:
|
||
$ref: '#/components/schemas/RouteType'
|
||
color:
|
||
type: string
|
||
description:
|
||
type: string
|
||
is_active:
|
||
type: boolean
|
||
CreateStopRequest:
|
||
type: object
|
||
required: [name, latitude, longitude]
|
||
properties:
|
||
name:
|
||
type: string
|
||
latitude:
|
||
type: number
|
||
longitude:
|
||
type: number
|
||
address:
|
||
type: string
|
||
description:
|
||
type: string
|
||
UpdateStopRequest:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
latitude:
|
||
type: number
|
||
longitude:
|
||
type: number
|
||
address:
|
||
type: string
|
||
description:
|
||
type: string
|
||
is_active:
|
||
type: boolean
|
||
AddRouteStopRequest:
|
||
type: object
|
||
required: [stopId]
|
||
properties:
|
||
stopId:
|
||
type: integer
|
||
sequence:
|
||
type: integer
|
||
timeOffsetMinutes:
|
||
type: integer
|
||
default: 0
|
||
UpdateRouteStopRequest:
|
||
type: object
|
||
properties:
|
||
sequence:
|
||
type: integer
|
||
timeOffsetMinutes:
|
||
type: integer
|
||
ReorderRouteStopsRequest:
|
||
type: object
|
||
required: [stops]
|
||
properties:
|
||
stops:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [stopId, sequence]
|
||
properties:
|
||
stopId:
|
||
type: integer
|
||
sequence:
|
||
type: integer
|
||
timeOffsetMinutes:
|
||
type: integer
|
||
CreateScheduleRequest:
|
||
type: object
|
||
required: [routeId, dayType]
|
||
properties:
|
||
routeId:
|
||
type: integer
|
||
dayType:
|
||
$ref: '#/components/schemas/ScheduleDayType'
|
||
departureTimes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Legacy flat departure list. If trips is supplied, departureTimes is derived from trips.
|
||
trips:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ScheduleTrip'
|
||
validFrom:
|
||
type: string
|
||
format: date
|
||
nullable: true
|
||
validUntil:
|
||
type: string
|
||
format: date
|
||
nullable: true
|
||
UpdateScheduleRequest:
|
||
type: object
|
||
properties:
|
||
departureTimes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: Legacy flat departure list. If trips is supplied, departureTimes is derived from trips.
|
||
trips:
|
||
type: array
|
||
items:
|
||
$ref: '#/components/schemas/ScheduleTrip'
|
||
validFrom:
|
||
type: string
|
||
format: date
|
||
nullable: true
|
||
validUntil:
|
||
type: string
|
||
format: date
|
||
nullable: true
|
||
CopyScheduleRequest:
|
||
type: object
|
||
required: [fromDayType, toDayType]
|
||
properties:
|
||
fromDayType:
|
||
$ref: '#/components/schemas/ScheduleDayType'
|
||
toDayType:
|
||
$ref: '#/components/schemas/ScheduleDayType'
|
||
CreateHolidayRequest:
|
||
type: object
|
||
required: [date, name]
|
||
properties:
|
||
date:
|
||
type: string
|
||
format: date
|
||
name:
|
||
type: string
|
||
isRecurring:
|
||
type: boolean
|
||
UpdateHolidayRequest:
|
||
type: object
|
||
properties:
|
||
date:
|
||
type: string
|
||
format: date
|
||
name:
|
||
type: string
|
||
isRecurring:
|
||
type: boolean
|
||
CreateAlertRequest:
|
||
type: object
|
||
required: [alert_type, title, message, start_time]
|
||
properties:
|
||
route_id:
|
||
type: integer
|
||
nullable: true
|
||
alert_type:
|
||
$ref: '#/components/schemas/AlertType'
|
||
title:
|
||
type: string
|
||
message:
|
||
type: string
|
||
start_time:
|
||
type: string
|
||
format: date-time
|
||
end_time:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
is_active:
|
||
type: boolean
|
||
UpdateAlertRequest:
|
||
type: object
|
||
properties:
|
||
route_id:
|
||
type: integer
|
||
nullable: true
|
||
alert_type:
|
||
$ref: '#/components/schemas/AlertType'
|
||
title:
|
||
type: string
|
||
message:
|
||
type: string
|
||
start_time:
|
||
type: string
|
||
format: date-time
|
||
end_time:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
is_active:
|
||
type: boolean
|
||
CreateUserRequest:
|
||
type: object
|
||
required: [username, password]
|
||
properties:
|
||
username:
|
||
type: string
|
||
password:
|
||
type: string
|
||
minLength: 6
|
||
full_name:
|
||
type: string
|
||
email:
|
||
type: string
|
||
format: email
|
||
role:
|
||
$ref: '#/components/schemas/UserRole'
|
||
UpdateUserRequest:
|
||
type: object
|
||
properties:
|
||
full_name:
|
||
type: string
|
||
email:
|
||
type: string
|
||
format: email
|
||
role:
|
||
$ref: '#/components/schemas/UserRole'
|
||
is_active:
|
||
type: boolean
|
||
ResetPasswordRequest:
|
||
type: object
|
||
required: [newPassword]
|
||
properties:
|
||
newPassword:
|
||
type: string
|
||
minLength: 6
|
||
AvatarUploadResponse:
|
||
type: object
|
||
properties:
|
||
message:
|
||
type: string
|
||
avatar_url:
|
||
type: string
|
||
user:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
username:
|
||
type: string
|
||
avatar_url:
|
||
type: string
|
||
nullable: true
|
||
CalculateEtaRequest:
|
||
type: object
|
||
required: [stopId, routeId]
|
||
properties:
|
||
stopId:
|
||
type: integer
|
||
routeId:
|
||
type: integer
|
||
TelemetryIngestRequest:
|
||
type: object
|
||
required: [lat, lon]
|
||
properties:
|
||
vehicleId:
|
||
type: integer
|
||
nullable: true
|
||
vehicleNumber:
|
||
type: string
|
||
nullable: true
|
||
routeId:
|
||
type: integer
|
||
nullable: true
|
||
vehicleType:
|
||
$ref: '#/components/schemas/RouteType'
|
||
lat:
|
||
type: number
|
||
minimum: -90
|
||
maximum: 90
|
||
lon:
|
||
type: number
|
||
minimum: -180
|
||
maximum: 180
|
||
speedKmh:
|
||
type: number
|
||
nullable: true
|
||
heading:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 360
|
||
nullable: true
|
||
accuracyMeters:
|
||
type: number
|
||
nullable: true
|
||
recordedAt:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
sourceType:
|
||
type: string
|
||
nullable: true
|
||
sourceRef:
|
||
type: string
|
||
nullable: true
|
||
PassengerTelemetryIngestRequest:
|
||
type: object
|
||
required: [session_id, latitude, longitude]
|
||
additionalProperties: false
|
||
properties:
|
||
session_id:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 128
|
||
description: Temporary anonymous session id; not a user, account, phone, or device id.
|
||
latitude:
|
||
type: number
|
||
minimum: -90
|
||
maximum: 90
|
||
longitude:
|
||
type: number
|
||
minimum: -180
|
||
maximum: 180
|
||
accuracy_m:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 1000
|
||
nullable: true
|
||
speed_kmh:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 120
|
||
nullable: true
|
||
heading:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 360
|
||
nullable: true
|
||
observed_at:
|
||
type: string
|
||
format: date-time
|
||
nullable: true
|
||
route_id:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
nullable: true
|
||
description: Optional client hint; server verifies it before aggregation.
|
||
stop_id:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
nullable: true
|
||
app_state:
|
||
type: string
|
||
enum: [foreground, background]
|
||
nullable: true
|
||
LiveVehicleState:
|
||
type: object
|
||
properties:
|
||
vehicle_id:
|
||
oneOf:
|
||
- type: integer
|
||
- type: string
|
||
description: Official vehicles use numeric ids; crowdsourced virtual vehicles use crowd:{routeId}:{clusterId}.
|
||
vehicle_number:
|
||
type: string
|
||
nullable: true
|
||
registration:
|
||
type: string
|
||
nullable: true
|
||
vehicle_type:
|
||
$ref: '#/components/schemas/RouteType'
|
||
capacity:
|
||
type: integer
|
||
nullable: true
|
||
route_id:
|
||
type: integer
|
||
route_number:
|
||
type: string
|
||
route_name:
|
||
type: string
|
||
route_color:
|
||
type: string
|
||
nullable: true
|
||
latitude:
|
||
type: number
|
||
longitude:
|
||
type: number
|
||
speed_kmh:
|
||
type: number
|
||
nullable: true
|
||
heading:
|
||
type: number
|
||
nullable: true
|
||
accuracy_meters:
|
||
type: number
|
||
nullable: true
|
||
accuracy_m:
|
||
type: number
|
||
nullable: true
|
||
source_type:
|
||
type: string
|
||
nullable: true
|
||
source_ref:
|
||
type: string
|
||
nullable: true
|
||
source:
|
||
type: string
|
||
enum: [official, crowdsourced]
|
||
nullable: true
|
||
confidence:
|
||
type: number
|
||
minimum: 0
|
||
maximum: 1
|
||
nullable: true
|
||
confidence_label:
|
||
type: string
|
||
enum: [LOW, MEDIUM, HIGH]
|
||
nullable: true
|
||
is_estimated:
|
||
type: boolean
|
||
nullable: true
|
||
last_seen_at:
|
||
type: string
|
||
format: date-time
|
||
updated_at:
|
||
type: string
|
||
format: date-time
|
||
is_online:
|
||
type: boolean
|
||
seconds_since_update:
|
||
type: integer
|
||
is_moving:
|
||
type: boolean
|
||
TelemetryIngestResponse:
|
||
type: object
|
||
properties:
|
||
message:
|
||
type: string
|
||
vehicle:
|
||
$ref: '#/components/schemas/LiveVehicleState'
|
||
PassengerTelemetryIngestResponse:
|
||
type: object
|
||
required: [accepted]
|
||
properties:
|
||
accepted:
|
||
type: boolean
|
||
example: true
|