Add CORS support and response caching to aggregate endpoints
- Introduced CORS configuration options in config.example.yaml, allowing specification of allowed origins for cross-origin requests. - Enhanced the aggregate handler to support response caching with a configurable TTL, improving performance for repeated requests. - Updated the aggregate API to return a structured response indicating whether any upstream requests failed, enhancing error handling and response clarity. - Modified documentation in AGGREGATE.md and README.md to reflect the new CORS and caching features. - Added tests to validate the new functionality in the aggregate handler.
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Telemt API gateway — aggregate API
|
||||
description: >
|
||||
Эндпоинты под префиксом /api/agg на шлюзе telemt-api.
|
||||
Базовый URL задаётся listen шлюза (например http://127.0.0.1:8080).
|
||||
version: 1.0.0
|
||||
|
||||
paths:
|
||||
/api/agg/summary:
|
||||
get:
|
||||
summary: Сводка флота и топы пользователей
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/aliases'
|
||||
- name: top_n
|
||||
in: query
|
||||
schema: { type: integer, minimum: 1, maximum: 1000, default: 10 }
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/AggEnvelopeSummary' }
|
||||
|
||||
/api/agg/traffic:
|
||||
get:
|
||||
summary: Трафик по пользователям и серверам
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/aliases'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/AggEnvelopeTrafficRows' }
|
||||
|
||||
/api/agg/unique-ips:
|
||||
get:
|
||||
summary: Уникальные IP с привязкой к серверам (и опционально GeoIP)
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/aliases'
|
||||
- name: geo
|
||||
in: query
|
||||
description: 'false — не обогащать GeoIP'
|
||||
schema: { type: string, enum: ['false', 'true'] }
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/AggEnvelopeUniqueIPs' }
|
||||
|
||||
/api/agg/users:
|
||||
get:
|
||||
summary: Список пользователей с merge по серверам
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/aliases'
|
||||
- name: include_links
|
||||
in: query
|
||||
schema: { type: string, enum: ['true', 'false'] }
|
||||
- name: min_total_megabytes
|
||||
in: query
|
||||
schema: { type: number, format: float }
|
||||
- name: min_total_octets
|
||||
in: query
|
||||
schema: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/AggEnvelopeUsersRows' }
|
||||
|
||||
/api/agg/user/{username}:
|
||||
get:
|
||||
summary: Один пользователь (тот же объект, что в users[])
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: '^[A-Za-z0-9_.-]+$'
|
||||
- $ref: '#/components/parameters/aliases'
|
||||
- name: include_links
|
||||
in: query
|
||||
schema: { type: string, enum: ['true', 'false'] }
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/AggEnvelopeUsersRow' }
|
||||
'404':
|
||||
description: Пользователь не найден ни на одном upstream
|
||||
|
||||
/api/agg/fleet-status:
|
||||
get:
|
||||
summary: Health + system/info по всем выбранным серверам
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/aliases'
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/AggEnvelopeFleetStatus' }
|
||||
|
||||
components:
|
||||
parameters:
|
||||
aliases:
|
||||
name: aliases
|
||||
in: query
|
||||
description: Список алиасов через запятую
|
||||
schema: { type: string }
|
||||
|
||||
schemas:
|
||||
AggSuccessBase:
|
||||
type: object
|
||||
required: [ok, data, generated_at]
|
||||
properties:
|
||||
ok: { type: boolean, enum: [true] }
|
||||
generated_at: { type: string, format: date-time }
|
||||
partial: { type: boolean, description: true если часть upstream недоступна }
|
||||
|
||||
AggEnvelopeSummary:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AggSuccessBase'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
description: SummaryData (см. реализацию / доку AGGREGATE.md)
|
||||
|
||||
AggEnvelopeTrafficRows:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AggSuccessBase'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items: { $ref: '#/components/schemas/TrafficRow' }
|
||||
|
||||
AggEnvelopeUniqueIPs:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AggSuccessBase'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items: { $ref: '#/components/schemas/UniqueIPsRow' }
|
||||
|
||||
AggEnvelopeUsersRows:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AggSuccessBase'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
type: array
|
||||
items: { $ref: '#/components/schemas/UsersRow' }
|
||||
|
||||
AggEnvelopeUsersRow:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AggSuccessBase'
|
||||
- type: object
|
||||
properties:
|
||||
data: { $ref: '#/components/schemas/UsersRow' }
|
||||
|
||||
AggEnvelopeFleetStatus:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AggSuccessBase'
|
||||
- type: object
|
||||
properties:
|
||||
data: { $ref: '#/components/schemas/FleetStatusData' }
|
||||
|
||||
TrafficRow:
|
||||
type: object
|
||||
properties:
|
||||
username: { type: string }
|
||||
servers:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/components/schemas/TrafficServerStats'
|
||||
|
||||
TrafficServerStats:
|
||||
type: object
|
||||
properties:
|
||||
total_megabytes: { type: number, format: float }
|
||||
current_connections: { type: integer, format: int64 }
|
||||
revision: { type: string }
|
||||
|
||||
UniqueIPsRow:
|
||||
type: object
|
||||
properties:
|
||||
username: { type: string }
|
||||
ips:
|
||||
type: array
|
||||
items: { $ref: '#/components/schemas/IPAssignments' }
|
||||
|
||||
IPAssignments:
|
||||
type: object
|
||||
properties:
|
||||
ip: { type: string }
|
||||
active_on_servers:
|
||||
type: array
|
||||
items: { type: string }
|
||||
recent_on_servers:
|
||||
type: array
|
||||
items: { type: string }
|
||||
primary_server: { type: string, nullable: true }
|
||||
country_code: { type: string, nullable: true }
|
||||
country_name: { type: string, nullable: true }
|
||||
city_name: { type: string, nullable: true }
|
||||
asn: { type: integer, format: int64, nullable: true }
|
||||
as_organization: { type: string, nullable: true }
|
||||
|
||||
UserLinks:
|
||||
type: object
|
||||
properties:
|
||||
classic:
|
||||
type: array
|
||||
items: { type: string }
|
||||
secure:
|
||||
type: array
|
||||
items: { type: string }
|
||||
tls:
|
||||
type: array
|
||||
items: { type: string }
|
||||
|
||||
UsersRow:
|
||||
type: object
|
||||
properties:
|
||||
username: { type: string }
|
||||
total_megabytes: { type: number, format: float }
|
||||
by_server:
|
||||
type: object
|
||||
additionalProperties:
|
||||
$ref: '#/components/schemas/TrafficServerStats'
|
||||
links: { $ref: '#/components/schemas/UserLinks' }
|
||||
active_unique_ips: { type: integer, format: int64 }
|
||||
recent_unique_ips: { type: integer, format: int64 }
|
||||
user_ad_tag: { type: string, nullable: true }
|
||||
max_tcp_conns: { type: integer, format: int64, nullable: true }
|
||||
expiration_rfc3339: { type: string, nullable: true }
|
||||
data_quota_bytes: { type: integer, format: int64, nullable: true }
|
||||
max_unique_ips: { type: integer, format: int64, nullable: true }
|
||||
|
||||
HealthData:
|
||||
type: object
|
||||
properties:
|
||||
status: { type: string }
|
||||
read_only: { type: boolean }
|
||||
|
||||
SystemInfoData:
|
||||
type: object
|
||||
properties:
|
||||
version: { type: string }
|
||||
target_arch: { type: string }
|
||||
target_os: { type: string }
|
||||
build_profile: { type: string }
|
||||
git_commit: { type: string, nullable: true }
|
||||
build_time_utc: { type: string, nullable: true }
|
||||
rustc_version: { type: string, nullable: true }
|
||||
process_started_at_epoch_secs: { type: integer, format: int64 }
|
||||
uptime_seconds: { type: number, format: float }
|
||||
config_path: { type: string }
|
||||
config_hash: { type: string }
|
||||
config_reload_count: { type: integer, format: int64 }
|
||||
last_config_reload_epoch_secs: { type: integer, format: int64, nullable: true }
|
||||
|
||||
FleetServerStatus:
|
||||
type: object
|
||||
properties:
|
||||
alias: { type: string }
|
||||
ok: { type: boolean }
|
||||
health_ok: { type: boolean }
|
||||
health_http_status: { type: integer }
|
||||
health_latency_ms: { type: integer, format: int64 }
|
||||
health_error: { type: string }
|
||||
health_revision: { type: string }
|
||||
health: { $ref: '#/components/schemas/HealthData' }
|
||||
system_info_ok: { type: boolean }
|
||||
system_info_http_status: { type: integer }
|
||||
system_info_latency_ms: { type: integer, format: int64 }
|
||||
system_info_error: { type: string }
|
||||
system_info_revision: { type: string }
|
||||
system_info: { $ref: '#/components/schemas/SystemInfoData' }
|
||||
|
||||
FleetStatusData:
|
||||
type: object
|
||||
properties:
|
||||
servers:
|
||||
type: array
|
||||
items: { $ref: '#/components/schemas/FleetServerStatus' }
|
||||
servers_total: { type: integer }
|
||||
servers_all_ok: { type: integer }
|
||||
servers_failed: { type: integer }
|
||||
Reference in New Issue
Block a user