Add OpenAPI 3.1 spec for REST API
All checks were successful
Deploy to NAS / deploy (push) Successful in 39s

Covers all endpoints: health, trackpoint ingest (single + batch),
query (days, trackpoints, stops, suggestions), journal entry creation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christoph K.
2026-04-07 19:03:03 +02:00
parent 45875502f8
commit d1649ddfce

465
openapi.yaml Normal file
View File

@@ -0,0 +1,465 @@
openapi: 3.1.0
info:
title: Pamietnik API
version: 0.1.0
description: Life & travel journal — REST API for trackpoint ingest and data query.
servers:
- url: http://192.168.1.4:9050
description: NAS (local)
- url: http://localhost:9050
description: Local dev
security:
- cookieAuth: []
paths:
/healthz:
get:
summary: Health check
security: []
responses:
'200':
description: Server is up
content:
text/plain:
example: ok
/readyz:
get:
summary: Readiness check
security: []
responses:
'200':
description: Server is ready
# --- Ingest ---
/v1/trackpoints:
post:
summary: Ingest single trackpoint
tags: [Ingest]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TrackpointInput'
responses:
'200':
description: Accepted (or duplicate — idempotent)
content:
application/json:
schema:
$ref: '#/components/schemas/BatchResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/v1/trackpoints:batch:
post:
summary: Ingest batch of trackpoints (max 500)
tags: [Ingest]
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/TrackpointInput'
maxItems: 500
responses:
'200':
description: Processed — check accepted_ids and rejected for details
content:
application/json:
schema:
$ref: '#/components/schemas/BatchResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
# --- Query ---
/v1/days:
get:
summary: List days with trackpoint activity
tags: [Query]
parameters:
- name: from
in: query
required: true
schema:
type: string
format: date
example: '2026-01-01'
- name: to
in: query
required: true
schema:
type: string
format: date
example: '2026-12-31'
responses:
'200':
description: List of days
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DaySummary'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/v1/trackpoints:
get:
summary: List trackpoints for a date
tags: [Query]
parameters:
- name: date
in: query
required: true
schema:
type: string
format: date
example: '2026-04-07'
responses:
'200':
description: List of trackpoints
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Trackpoint'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
/v1/stops:
get:
summary: List stops for a date
tags: [Query]
parameters:
- name: date
in: query
required: true
schema:
type: string
format: date
example: '2026-04-07'
responses:
'200':
description: List of stops
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Stop'
'401':
$ref: '#/components/responses/Unauthorized'
/v1/suggestions:
get:
summary: List suggestions for a date
tags: [Query]
parameters:
- name: date
in: query
required: true
schema:
type: string
format: date
example: '2026-04-07'
responses:
'200':
description: List of suggestions
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Suggestion'
'401':
$ref: '#/components/responses/Unauthorized'
# --- Journal ---
/entries:
post:
summary: Create journal entry with optional images
tags: [Journal]
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [date, time]
properties:
date:
type: string
format: date
example: '2026-04-07'
time:
type: string
example: '14:30'
title:
type: string
description:
type: string
lat:
type: number
format: double
lon:
type: number
format: double
images:
type: array
items:
type: string
format: binary
description: JPEG, PNG, WebP or HEIC — max 10 MB each, 32 MB total
responses:
'201':
description: Entry created
content:
application/json:
schema:
$ref: '#/components/schemas/JournalEntry'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
components:
securitySchemes:
cookieAuth:
type: apiKey
in: cookie
name: session
responses:
BadRequest:
description: Bad request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Unauthorized:
description: Not authenticated
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
schemas:
TrackpointInput:
type: object
required: [event_id, device_id, trip_id, timestamp, lat, lon]
properties:
event_id:
type: string
format: uuid
description: Client-generated UUID — used for idempotency
device_id:
type: string
trip_id:
type: string
timestamp:
type: string
format: date-time
example: '2026-04-07T12:00:00Z'
lat:
type: number
format: double
minimum: -90
maximum: 90
lon:
type: number
format: double
minimum: -180
maximum: 180
source:
type: string
enum: [gps, manual]
default: gps
note:
type: string
accuracy_m:
type: number
format: double
speed_mps:
type: number
format: double
bearing_deg:
type: number
format: double
altitude_m:
type: number
format: double
Trackpoint:
allOf:
- $ref: '#/components/schemas/TrackpointInput'
BatchResponse:
type: object
properties:
server_time:
type: string
format: date-time
accepted_ids:
type: array
items:
type: string
rejected:
type: array
items:
$ref: '#/components/schemas/RejectedItem'
RejectedItem:
type: object
properties:
event_id:
type: string
code:
type: string
enum: [VALIDATION_ERROR, DB_ERROR, INVALID_TIMESTAMP]
message:
type: string
DaySummary:
type: object
properties:
date:
type: string
format: date
count:
type: integer
first_ts:
type: string
format: date-time
last_ts:
type: string
format: date-time
Stop:
type: object
properties:
stop_id:
type: string
device_id:
type: string
trip_id:
type: string
start_ts:
type: string
format: date-time
end_ts:
type: string
format: date-time
center_lat:
type: number
format: double
center_lon:
type: number
format: double
duration_s:
type: integer
place_label:
type: string
place_details:
type: object
Suggestion:
type: object
properties:
suggestion_id:
type: string
stop_id:
type: string
type:
type: string
enum: [highlight, name_place, add_note]
title:
type: string
text:
type: string
created_at:
type: string
format: date-time
dismissed_at:
type: string
format: date-time
JournalEntry:
type: object
properties:
entry_id:
type: string
user_id:
type: string
entry_date:
type: string
format: date
entry_time:
type: string
example: '14:30'
title:
type: string
description:
type: string
lat:
type: number
format: double
lon:
type: number
format: double
created_at:
type: string
format: date-time
images:
type: array
items:
$ref: '#/components/schemas/JournalImage'
JournalImage:
type: object
properties:
image_id:
type: string
entry_id:
type: string
filename:
type: string
original_name:
type: string
mime_type:
type: string
size_bytes:
type: integer
format: int64
created_at:
type: string
format: date-time
Error:
type: object
properties:
code:
type: string
message:
type: string