From 8b0d51f6ab34f346d973f3f3e7c3c4397f0dd233 Mon Sep 17 00:00:00 2001 From: blazSmehov Date: Tue, 24 Feb 2026 16:10:14 +0100 Subject: [PATCH] chore: add api schema --- api/openapi.yaml | 821 ++++++++++++++++++++++++++++++++++ internal/app/server/routes.go | 1 + 2 files changed, 822 insertions(+) create mode 100644 api/openapi.yaml diff --git a/api/openapi.yaml b/api/openapi.yaml new file mode 100644 index 0000000..fd3fa75 --- /dev/null +++ b/api/openapi.yaml @@ -0,0 +1,821 @@ +openapi: 3.0.3 +info: + title: Presence API + description: API for gateways, zones, trackers, parser configs, settings, alerts, and tracks. + version: 1.0.0 + +servers: + - url: / + description: Default server + +paths: + # --- Health --- + /health: + get: + summary: Liveness check + operationId: health + tags: [Health] + responses: + '200': + description: Service is alive + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: ok + + /ready: + get: + summary: Readiness check (DB connectivity) + operationId: ready + tags: [Health] + responses: + '200': + description: Service is ready + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: ready + '503': + $ref: '#/components/responses/ServiceUnavailable' + + # --- Gateways --- + /reslevis/getGateways: + get: + summary: List all gateways + operationId: getGateways + tags: [Gateways] + responses: + '200': + description: List of gateways + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Gateway' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/postGateway: + post: + summary: Create a gateway + operationId: postGateway + tags: [Gateways] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Gateway' + responses: + '201': + description: Gateway created + content: + application/json: + schema: + $ref: '#/components/schemas/StatusCreated' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/removeGateway/{id}: + delete: + summary: Delete a gateway by ID + operationId: removeGateway + tags: [Gateways] + parameters: + - $ref: '#/components/parameters/PathId' + responses: + '200': + description: Gateway deleted + content: + application/json: + schema: + $ref: '#/components/schemas/StatusDeleted' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/updateGateway/{id}: + put: + summary: Update a gateway by ID + operationId: updateGateway + tags: [Gateways] + parameters: + - $ref: '#/components/parameters/PathId' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Gateway' + responses: + '200': + description: Gateway updated + content: + application/json: + schema: + $ref: '#/components/schemas/StatusUpdated' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + # --- Zones --- + /reslevis/getZones: + get: + summary: List all zones + operationId: getZones + tags: [Zones] + responses: + '200': + description: List of zones + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Zone' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/postZone: + post: + summary: Create a zone + operationId: postZone + tags: [Zones] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Zone' + responses: + '201': + description: Zone created + content: + application/json: + schema: + $ref: '#/components/schemas/StatusCreated' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/removeZone/{id}: + delete: + summary: Delete a zone by ID + operationId: removeZone + tags: [Zones] + parameters: + - $ref: '#/components/parameters/PathId' + responses: + '200': + description: Zone deleted + content: + application/json: + schema: + $ref: '#/components/schemas/StatusDeleted' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/updateZone: + put: + summary: Update a zone + operationId: updateZone + tags: [Zones] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Zone' + responses: + '200': + description: Zone updated + content: + application/json: + schema: + $ref: '#/components/schemas/StatusUpdated' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + # --- Tracker zones --- + /reslevis/getTrackerZones: + get: + summary: List all tracker zones + operationId: getTrackerZones + tags: [TrackerZones] + responses: + '200': + description: List of tracker zones + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/TrackerZone' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/postTrackerZone: + post: + summary: Create a tracker zone + operationId: postTrackerZone + tags: [TrackerZones] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TrackerZone' + responses: + '201': + description: Tracker zone created + content: + application/json: + schema: + $ref: '#/components/schemas/StatusCreated' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/removeTrackerZone/{id}: + delete: + summary: Delete a tracker zone by ID + operationId: removeTrackerZone + tags: [TrackerZones] + parameters: + - $ref: '#/components/parameters/PathId' + responses: + '200': + description: Tracker zone deleted + content: + application/json: + schema: + $ref: '#/components/schemas/StatusDeleted' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/updateTrackerZone: + put: + summary: Update a tracker zone + operationId: updateTrackerZone + tags: [TrackerZones] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TrackerZone' + responses: + '200': + description: Tracker zone updated + content: + application/json: + schema: + $ref: '#/components/schemas/StatusUpdated' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + # --- Trackers --- + /reslevis/getTrackers: + get: + summary: List all trackers + operationId: getTrackers + tags: [Trackers] + responses: + '200': + description: List of trackers + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Tracker' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/postTracker: + post: + summary: Create a tracker + operationId: postTracker + tags: [Trackers] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Tracker' + responses: + '201': + description: Tracker created + content: + application/json: + schema: + $ref: '#/components/schemas/StatusCreated' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/removeTracker/{id}: + delete: + summary: Delete a tracker by ID + operationId: removeTracker + tags: [Trackers] + parameters: + - $ref: '#/components/parameters/PathId' + responses: + '200': + description: Tracker deleted + content: + application/json: + schema: + $ref: '#/components/schemas/StatusDeleted' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/updateTracker: + put: + summary: Update a tracker + operationId: updateTracker + tags: [Trackers] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Tracker' + responses: + '200': + description: Tracker updated + content: + application/json: + schema: + $ref: '#/components/schemas/StatusUpdated' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + # --- Parser configs (beacons) --- + /configs/beacons: + get: + summary: List all beacon parser configs + operationId: listParserConfigs + tags: [ParserConfigs] + responses: + '200': + description: List of parser configs + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ParserConfig' + '500': + $ref: '#/components/responses/InternalError' + + post: + summary: Create a beacon parser config + operationId: addParserConfig + tags: [ParserConfigs] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ParserConfig' + responses: + '201': + description: Parser config created + content: + application/json: + schema: + $ref: '#/components/schemas/StatusCreated' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalError' + + /configs/beacons/{id}: + put: + summary: Update a beacon parser config by name (id) + operationId: updateParserConfig + tags: [ParserConfigs] + parameters: + - name: id + in: path + required: true + description: Config name (identifier) + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ParserConfig' + responses: + '200': + description: Parser config updated + content: + application/json: + schema: + $ref: '#/components/schemas/StatusUpdated' + '400': + $ref: '#/components/responses/BadRequest' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + delete: + summary: Delete a beacon parser config by name (id) + operationId: deleteParserConfig + tags: [ParserConfigs] + parameters: + - name: id + in: path + required: true + description: Config name (identifier) + schema: + type: string + responses: + '200': + description: Parser config deleted + content: + application/json: + schema: + $ref: '#/components/schemas/StatusDeleted' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + # --- Settings --- + /reslevis/settings: + get: + summary: List settings + operationId: getSettings + tags: [Settings] + responses: + '200': + description: List of settings (typically one row) + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Settings' + '500': + $ref: '#/components/responses/InternalError' + + patch: + summary: Partially update settings + operationId: updateSettings + tags: [Settings] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/SettingsUpdate' + responses: + '200': + description: Settings updated + content: + application/json: + schema: + $ref: '#/components/schemas/StatusUpdated' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalError' + + # --- Alerts --- + /reslevis/alerts: + get: + summary: List all alerts + operationId: listAlerts + tags: [Alerts] + responses: + '200': + description: List of alerts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Alert' + '500': + $ref: '#/components/responses/InternalError' + + /reslevis/alerts/{id}: + get: + summary: Get alert by ID (or tracker ID depending on usage) + operationId: getAlertById + tags: [Alerts] + parameters: + - $ref: '#/components/parameters/PathId' + responses: + '200': + description: Single alert + content: + application/json: + schema: + $ref: '#/components/schemas/Alert' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' + + delete: + summary: Delete alert by ID + operationId: deleteAlert + tags: [Alerts] + parameters: + - $ref: '#/components/parameters/PathId' + responses: + '200': + description: Alert deleted + content: + application/json: + schema: + $ref: '#/components/schemas/StatusDeleted' + '500': + $ref: '#/components/responses/InternalError' + + # --- Tracks --- + /reslevis/getTracks/{id}: + get: + summary: List tracks for a tracker UUID + operationId: getTracks + tags: [Tracks] + parameters: + - name: id + in: path + required: true + description: Tracker UUID + schema: + type: string + - name: limit + in: query + description: Max number of tracks to return (default 10) + schema: + type: integer + default: 10 + - name: from + in: query + description: Start time (RFC3339) + schema: + type: string + format: date-time + - name: to + in: query + description: End time (RFC3339) + schema: + type: string + format: date-time + responses: + '200': + description: List of tracks + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Track' + '500': + $ref: '#/components/responses/InternalError' + +components: + parameters: + PathId: + name: id + in: path + required: true + description: Resource ID + schema: + type: string + + responses: + BadRequest: + description: Invalid request (e.g. invalid JSON) + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: bad_request + message: invalid request body + NotFound: + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: not_found + message: resource not found + InternalError: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: internal_error + message: failed to complete operation + ServiceUnavailable: + description: Service not ready (e.g. DB unavailable) + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: not_ready + message: database not available + + schemas: + Error: + type: object + required: [error] + properties: + error: + type: string + description: Error code (bad_request, not_found, internal_error, not_ready) + message: + type: string + description: Human-readable message + + StatusCreated: + type: object + properties: + status: + type: string + example: created + StatusUpdated: + type: object + properties: + status: + type: string + example: updated + StatusDeleted: + type: object + properties: + status: + type: string + example: deleted + + Gateway: + type: object + properties: + id: { type: string } + name: { type: string } + mac: { type: string } + status: { type: string } + model: { type: string } + ip: { type: string } + position: { type: string } + x: { type: number, format: float } + y: { type: number, format: float } + notes: { type: string } + floor: { type: string } + building: { type: string } + + Zone: + type: object + properties: + id: { type: string } + name: { type: string } + groups: + type: array + items: { type: string } + floor: { type: string } + building: { type: string } + + TrackerZone: + type: object + properties: + id: { type: string } + zoneList: + type: array + items: { type: string } + tracker: { type: string } + days: { type: string } + time: { type: string } + + Tracker: + type: object + properties: + id: { type: string } + name: { type: string } + mac: { type: string } + status: { type: string } + model: { type: string } + ip: { type: string } + position: { type: string } + x: { type: number, format: float } + y: { type: number, format: float } + notes: { type: string } + floor: { type: string } + building: { type: string } + battery: { type: integer } + batteryThreshold: { type: integer } + temperature: { type: integer } + + ParserConfig: + type: object + description: Beacon parser configuration (name is the primary key) + properties: + name: { type: string } + min: { type: integer } + max: { type: integer } + pattern: + type: array + items: { type: string } + configs: + type: object + additionalProperties: + $ref: '#/components/schemas/ParserConfigEntry' + + ParserConfigEntry: + type: object + properties: + length: { type: integer } + offset: { type: integer } + order: { type: string } + + Settings: + type: object + properties: + ID: { type: integer } + current_algorithm: { type: string } + location_confidence: { type: integer } + last_seen_threshold: { type: integer } + beacon_metric_size: { type: integer } + HA_send_interval: { type: integer } + HA_send_changes_only: { type: boolean } + RSSI_enforce_threshold: { type: boolean } + RSSI_min_threshold: { type: integer } + + SettingsUpdate: + type: object + description: Partial update; only provided fields are updated + additionalProperties: true + + Alert: + type: object + properties: + id: { type: string } + tracker_id: { type: string } + type: { type: string } + value: { type: string } + + Track: + type: object + properties: + id: { type: string } + timestamp: { type: string, format: date-time } + type: { type: string } + status: { type: string } + gateway: { type: string } + gatewayMac: { type: string } + tracker: { type: string } + trackerMac: { type: string } + subject: { type: string } + subjectName: { type: string } + floor: { type: string } + signal: { type: integer } + building: { type: string } diff --git a/internal/app/server/routes.go b/internal/app/server/routes.go index 996b934..730c1f1 100644 --- a/internal/app/server/routes.go +++ b/internal/app/server/routes.go @@ -1,3 +1,4 @@ +// Package server registers HTTP routes. API schema: api/openapi.yaml (OpenAPI 3.0). package server import (