219 lines
6.4 KiB
TypeScript
219 lines
6.4 KiB
TypeScript
import pkg from '@/package.json';
|
|
|
|
// OpenAPI 3.1 document for the app's public JSON API, kept in code so it can be
|
|
// served machine-readable at /api/openapi (see AGENTS.md "## API"). Must be updated
|
|
// whenever an endpoint changes.
|
|
export const openApiSpec = {
|
|
openapi: '3.1.0',
|
|
info: {
|
|
title: 'Flight Ops API',
|
|
description:
|
|
'JSON API for Flight Ops — EU airport data for the map, the live game-state snapshot, and app metadata.',
|
|
// Sourced from package.json so it stays in sync with every version bump.
|
|
version: pkg.version,
|
|
},
|
|
// Empty url = current origin (the spec is served from the same host that runs the API).
|
|
servers: [{ url: '' }],
|
|
paths: {
|
|
'/api/airports': {
|
|
get: {
|
|
summary: 'List EU airports',
|
|
description:
|
|
'All fixed-wing EU airports (large, medium and seaplane bases) as a GeoJSON FeatureCollection.',
|
|
operationId: 'listAirports',
|
|
responses: {
|
|
200: {
|
|
description: 'GeoJSON FeatureCollection of EU airports',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/AirportFeatureCollection' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'/api/state': {
|
|
get: {
|
|
summary: 'Game state snapshot',
|
|
description:
|
|
'Full current game state for client polling — account, sim clock, planes and contracts. Read-only.',
|
|
operationId: 'getState',
|
|
responses: {
|
|
200: {
|
|
description: 'Current game state',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/StateSnapshot' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
'/api/version': {
|
|
get: {
|
|
summary: 'App version',
|
|
description: 'The running app version, read from package.json.',
|
|
operationId: 'getVersion',
|
|
responses: {
|
|
200: {
|
|
description: 'Current app version',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/AppVersion' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
components: {
|
|
schemas: {
|
|
AppVersion: {
|
|
type: 'object',
|
|
properties: {
|
|
version: { type: 'string', description: 'Semver of the running build' },
|
|
},
|
|
required: ['version'],
|
|
},
|
|
AirportFeatureCollection: {
|
|
type: 'object',
|
|
properties: {
|
|
type: { const: 'FeatureCollection' },
|
|
features: {
|
|
type: 'array',
|
|
items: { $ref: '#/components/schemas/AirportFeature' },
|
|
},
|
|
},
|
|
required: ['type', 'features'],
|
|
},
|
|
AirportFeature: {
|
|
type: 'object',
|
|
properties: {
|
|
type: { const: 'Feature' },
|
|
geometry: { $ref: '#/components/schemas/PointGeometry' },
|
|
properties: { $ref: '#/components/schemas/AirportProperties' },
|
|
},
|
|
required: ['type', 'geometry', 'properties'],
|
|
},
|
|
PointGeometry: {
|
|
type: 'object',
|
|
properties: {
|
|
type: { const: 'Point' },
|
|
coordinates: {
|
|
type: 'array',
|
|
description: '[longitude, latitude]',
|
|
items: { type: 'number' },
|
|
minItems: 2,
|
|
maxItems: 2,
|
|
},
|
|
},
|
|
required: ['type', 'coordinates'],
|
|
},
|
|
AirportProperties: {
|
|
type: 'object',
|
|
properties: {
|
|
name: { type: 'string' },
|
|
icao: { type: 'string' },
|
|
lat: { type: 'number' },
|
|
lon: { type: 'number' },
|
|
},
|
|
required: ['name', 'icao', 'lat', 'lon'],
|
|
},
|
|
StateSnapshot: {
|
|
type: 'object',
|
|
properties: {
|
|
account: { $ref: '#/components/schemas/AccountState' },
|
|
simState: { $ref: '#/components/schemas/SimState' },
|
|
planes: {
|
|
type: 'array',
|
|
items: { $ref: '#/components/schemas/PlaneState' },
|
|
},
|
|
contracts: {
|
|
type: 'array',
|
|
items: { $ref: '#/components/schemas/ContractState' },
|
|
},
|
|
},
|
|
required: ['account', 'simState', 'planes', 'contracts'],
|
|
},
|
|
AccountState: {
|
|
type: 'object',
|
|
properties: {
|
|
cash: { type: 'number' },
|
|
reputation: { type: 'number' },
|
|
},
|
|
required: ['cash', 'reputation'],
|
|
},
|
|
SimState: {
|
|
type: 'object',
|
|
properties: {
|
|
clockMs: { type: 'integer', description: 'Sim-clock epoch ms (game time)' },
|
|
timeScale: { type: 'number', minimum: 1, maximum: 60 },
|
|
paused: { type: 'boolean' },
|
|
},
|
|
required: ['clockMs', 'timeScale', 'paused'],
|
|
},
|
|
PlaneState: {
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'string' },
|
|
model: { type: 'string' },
|
|
status: { enum: ['available', 'flying'] },
|
|
fuel: { type: 'number' },
|
|
maxFuel: { type: 'number' },
|
|
locationIcao: { type: 'string' },
|
|
locationName: { type: 'string' },
|
|
contractId: { type: ['string', 'null'] },
|
|
lat: { type: 'number' },
|
|
lon: { type: 'number' },
|
|
},
|
|
required: [
|
|
'id',
|
|
'model',
|
|
'status',
|
|
'fuel',
|
|
'maxFuel',
|
|
'locationIcao',
|
|
'locationName',
|
|
'contractId',
|
|
'lat',
|
|
'lon',
|
|
],
|
|
},
|
|
ContractState: {
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'string' },
|
|
originIcao: { type: 'string' },
|
|
originName: { type: 'string' },
|
|
destIcao: { type: 'string' },
|
|
destName: { type: 'string' },
|
|
reward: { type: 'number' },
|
|
cargoType: { type: 'string' },
|
|
cargoCount: { type: 'integer' },
|
|
deadlineSimTime: { type: 'integer', description: 'Sim-clock epoch ms (game time)' },
|
|
status: { enum: ['open', 'accepted', 'completed'] },
|
|
planeId: { type: ['string', 'null'] },
|
|
createdSimTime: { type: 'integer' },
|
|
},
|
|
required: [
|
|
'id',
|
|
'originIcao',
|
|
'originName',
|
|
'destIcao',
|
|
'destName',
|
|
'reward',
|
|
'cargoType',
|
|
'cargoCount',
|
|
'deadlineSimTime',
|
|
'status',
|
|
'planeId',
|
|
'createdSimTime',
|
|
],
|
|
},
|
|
},
|
|
},
|
|
};
|