diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 80f5092b..895540c5 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -62,6 +62,8 @@ jobs: VERSION: ${{ needs.publish.outputs.version }} run: | docker buildx build db/ --no-cache -t flomp/wanderer-db:$VERSION -t flomp/wanderer-db:latest --platform=linux/amd64,linux/arm64 --push + npm --prefix web ci + npm --prefix web run openapi:generate docker buildx build web/ --no-cache -t flomp/wanderer-web:$VERSION -t flomp/wanderer-web:latest --platform=linux/amd64,linux/arm64 --push npm --prefix docs ci npm --prefix docs run build diff --git a/docs/.gitignore b/docs/.gitignore index 6240da8b..41c087ac 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -3,6 +3,9 @@ dist/ # generated types .astro/ +# generated OpenAPI schema +wanderer.openapi.json + # dependencies node_modules/ diff --git a/docs/package.json b/docs/package.json index 22c059e5..eddf2ff3 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,7 +5,8 @@ "scripts": { "dev": "astro dev", "start": "astro dev", - "build": "astro check && astro build", + "sync-openapi": "node scripts/sync-openapi.js", + "build": "npm run sync-openapi && astro check && astro build", "preview": "astro preview", "astro": "astro" }, diff --git a/docs/scripts/sync-openapi.js b/docs/scripts/sync-openapi.js new file mode 100644 index 00000000..beb34241 --- /dev/null +++ b/docs/scripts/sync-openapi.js @@ -0,0 +1,18 @@ +import { copyFileSync, existsSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const docsDir = resolve(scriptDir, '..'); +const repoDir = resolve(docsDir, '..'); + +const source = resolve(repoDir, 'web/static/docs/api/wanderer.openapi.json'); +const destination = resolve(docsDir, 'wanderer.openapi.json'); + +if (!existsSync(source)) { + console.error(`OpenAPI schema not found at ${source}. Run the web build first so it can be generated.`); + process.exit(1); +} + +copyFileSync(source, destination); +console.log(`Synced OpenAPI schema to ${destination}`); diff --git a/docs/wanderer.openapi.json b/docs/wanderer.openapi.json deleted file mode 100644 index 0f111852..00000000 --- a/docs/wanderer.openapi.json +++ /dev/null @@ -1,6930 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Wanderer API", - "version": "v0.19.0", - "description": "API documentation for wanderer backend" - }, - "servers": [], - "paths": { - "/api/v1/waypoint": { - "get": { - "summary": "List waypoints", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of waypoints", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create waypoint", - "tags": [ - "Waypoints" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WaypointInput" - } - } - } - }, - "responses": { - "201": { - "description": "Waypoint created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Waypoint" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/waypoint/{id}": { - "get": { - "summary": "Get waypoint", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Waypoint details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Waypoint" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update waypoint", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WaypointUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Waypoint updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Waypoint" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete waypoint", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Waypoint deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/waypoint/{id}/file": { - "post": { - "summary": "Upload waypoint file", - "description": "Uploads a file (photo) for a waypoint", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded, waypoint updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Waypoint" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/valhalla/route": { - "post": { - "summary": "Get route data", - "description": "Queries Valhalla service for routing data", - "tags": [ - "Valhalla" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Route data from Valhalla", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/valhalla/height": { - "post": { - "summary": "Get elevation data", - "description": "Queries Valhalla service for elevation data at coordinates", - "tags": [ - "Valhalla" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Elevation data from Valhalla", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/user": { - "put": { - "summary": "Create user (sign up)", - "tags": [ - "Users" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserCreateInput" - } - } - } - }, - "responses": { - "201": { - "description": "User created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "description": "Bad Request - Signup disabled or invalid data" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/user/{id}": { - "get": { - "summary": "Get user by ID", - "tags": [ - "Users" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "User details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update user", - "description": "Updates a user. Handles password changes and email change requests", - "tags": [ - "Users" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "User updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete user", - "tags": [ - "Users" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "User deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/user/{id}/file": { - "post": { - "summary": "Upload user file", - "description": "Uploads a file (avatar) for a user", - "tags": [ - "Users" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded, user updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-share": { - "get": { - "summary": "List trail shares", - "description": "Retrieves a paginated list of trail shares with optional ActivityPub actor resolution", - "tags": [ - "Trail Shares" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListResult" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create trail share", - "description": "Creates a new trail share. Converts ActivityPub actor IRI to ID", - "tags": [ - "Trail Shares" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailShareInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail share created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-share/{id}": { - "get": { - "summary": "Get trail share", - "description": "Retrieves a trail share by ID", - "tags": [ - "Trail Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "TrailShare" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update trail share", - "tags": [ - "Trail Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailShareUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Trail share updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete trail share", - "tags": [ - "Trail Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Trail share deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-merge": { - "post": { - "summary": "Merge trails", - "description": "Merges a source trail into a target trail using the backend merge logic.", - "tags": [ - "Trail Merge" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailMergeExecuteRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Merge acknowledged", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailMergeExecuteResponse" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, - "/api/v1/trail-merge/suggest": { - "post": { - "summary": "Suggest merge target or duplicate groups", - "description": "Returns merge target suggestions for manual selection or auto-discovery, or temporary duplicate groups for maintenance workflows.", - "tags": [ - "Trail Merge" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailMergeSuggestRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Suggestion response", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/TrailMergeSuggestResponse" - }, - { - "$ref": "#/components/schemas/TrailMergeSuggestGroupsResponse" - } - ] - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, - "/api/v1/trail-link-share": { - "get": { - "summary": "List trail link shares", - "tags": [ - "Trail Link Shares" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of trail link shares", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create trail link share", - "tags": [ - "Trail Link Shares" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLinkShareInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail link share created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLinkShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-link-share/{id}": { - "get": { - "summary": "Get trail link share", - "description": "Retrieves a trail link share by ID", - "tags": [ - "Trail Link Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "TrailLinkShare" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update trail link share", - "tags": [ - "Trail Link Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLinkShareUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Trail link share updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLinkShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete trail link share", - "tags": [ - "Trail Link Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Trail link share deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-like": { - "get": { - "summary": "List trail likes", - "tags": [ - "Trail Likes" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of trail likes", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create trail like", - "tags": [ - "Trail Likes" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLikeInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail like created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLike" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-like/delete": { - "post": { - "summary": "Delete trail like by actor and trail", - "description": "Deletes a trail like without needing to know its ID. Uses actor and trail IDs to find and delete", - "tags": [ - "Trail Likes" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLikeInput" - } - } - } - }, - "responses": { - "200": { - "description": "Trail like deleted" - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-like/{id}": { - "get": { - "summary": "Get trail like", - "description": "Retrieves a trail like by ID", - "tags": [ - "Trail Likes" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "TrailLike" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete trail like", - "tags": [ - "Trail Likes" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Trail like deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail": { - "get": { - "summary": "List trails", - "description": "Retrieves a paginated list of trails with optional filtering and sorting", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListResult" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create trail", - "tags": [ - "Trails" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailCreateInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/upload": { - "put": { - "summary": "Upload and parse GPX file as trail", - "description": "Uploads a GPX file, parses it to extract trail data, performs duplicate detection, and indexes in search", - "tags": [ - "Trails" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - }, - "name": { - "type": "string" - }, - "ignoreDuplicates": { - "type": "boolean" - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Trail created from GPX", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request - Invalid or empty GPX file" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/recommend": { - "get": { - "summary": "Get trail recommendations", - "description": "Retrieves random trail recommendations from Meilisearch", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "query", - "name": "size", - "schema": { - "type": "integer", - "default": 10 - } - } - ], - "responses": { - "200": { - "description": "Array of recommended trails", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Trail" - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/form": { - "put": { - "summary": "Create trail with file upload", - "description": "Creates a new trail with file upload (GPX/photos) and date normalization", - "tags": [ - "Trails" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/TrailCreateInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/form/{id}": { - "post": { - "summary": "Update trail with file upload", - "description": "Updates a trail with file upload (GPX/photos) and date normalization", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/TrailUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Trail updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/filter": { - "get": { - "summary": "Get trail filter values", - "description": "Retrieves min/max values for trail filtering (distance, elevation gain/loss)", - "tags": [ - "Trails" - ], - "responses": { - "200": { - "description": "Trail filter values (min/max for distance, elevation)", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "min_distance": { - "type": "number" - }, - "max_distance": { - "type": "number" - }, - "min_elevation_gain": { - "type": "number" - }, - "max_elevation_gain": { - "type": "number" - }, - "min_elevation_loss": { - "type": "number" - }, - "max_elevation_loss": { - "type": "number" - } - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/download": { - "post": { - "summary": "Download file from URL", - "description": "Downloads a file from a URL and returns it as a blob", - "tags": [ - "Trails" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "url" - ], - "properties": { - "url": { - "type": "string", - "format": "uri" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File blob", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/bounding-box": { - "get": { - "summary": "Get trail bounding box", - "description": "Retrieves geographic bounding box (lat/lon bounds) for user's trails", - "tags": [ - "Trails" - ], - "responses": { - "200": { - "description": "Bounding box coordinates", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "max_lat": { - "type": "number" - }, - "min_lat": { - "type": "number" - }, - "max_lon": { - "type": "number" - }, - "min_lon": { - "type": "number" - } - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/{id}": { - "get": { - "summary": "Get trail", - "description": "Retrieves a trail by ID. Supports federated queries via handle parameter, fetching from remote instances and remapping file URLs", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "share", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Trail with optional federated data" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/{id}/file": { - "post": { - "summary": "Upload trail file", - "description": "Uploads a file for a trail", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/tag": { - "get": { - "summary": "List tags", - "tags": [ - "Tags" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of tags", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create tag", - "tags": [ - "Tags" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TagInput" - } - } - } - }, - "responses": { - "201": { - "description": "Tag created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Tag" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/tag/{id}": { - "get": { - "summary": "Get tag", - "tags": [ - "Tags" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Tag details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Tag" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update tag", - "tags": [ - "Tags" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TagUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Tag updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Tag" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete tag", - "tags": [ - "Tags" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Tag deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log": { - "get": { - "summary": "List summit logs", - "description": "Retrieves a paginated list of summit logs with deduplication of federated data", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListResult with local/remote items deduplicated" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create summit log", - "tags": [ - "Summit Logs" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLogInput" - } - } - } - }, - "responses": { - "201": { - "description": "Summit log created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log/form": { - "put": { - "summary": "Create summit log with file upload", - "description": "Creates a new summit log with file upload (photos/GPX) and date normalization", - "tags": [ - "Summit Logs" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SummitLogInput" - } - } - } - }, - "responses": { - "201": { - "description": "Summit log created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log/form/{id}": { - "post": { - "summary": "Update summit log with file upload", - "description": "Updates a summit log with file upload (photos/GPX) and date normalization", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SummitLogUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Summit log updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log/{id}": { - "get": { - "summary": "Get summit log", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Summit log details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update summit log", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLogUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Summit log updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete summit log", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Summit log deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log/{id}/file": { - "post": { - "summary": "Upload summit log file", - "description": "Uploads a file (photo or GPX) for a summit log", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded, summit log updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/settings": { - "put": { - "summary": "Create settings", - "tags": [ - "Settings" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SettingsInput" - } - } - } - }, - "responses": { - "201": { - "description": "Settings created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Settings" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/settings/{id}": { - "get": { - "summary": "Get settings", - "tags": [ - "Settings" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Settings details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Settings" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update settings", - "tags": [ - "Settings" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SettingsInput" - } - } - } - }, - "responses": { - "200": { - "description": "Settings updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Settings" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete settings", - "tags": [ - "Settings" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Settings deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/search/multi": { - "post": { - "summary": "Multi-index search", - "description": "Performs batch searches across multiple Meilisearch indices", - "tags": [ - "Search" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "queries" - ], - "properties": { - "queries": { - "type": "array", - "items": { - "type": "object" - } - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Combined Meilisearch results", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/search/actor": { - "get": { - "summary": "Search actors", - "description": "Searches for ActivityPub actors by username, combining local and federated results", - "tags": [ - "Search" - ], - "parameters": [ - { - "in": "query", - "name": "q", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "includeSelf", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "Array of matching actors", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object" - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/search/{index}": { - "post": { - "summary": "Search Meilisearch index", - "description": "Performs a search on a specific Meilisearch index", - "tags": [ - "Search" - ], - "parameters": [ - { - "in": "path", - "name": "index", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "q" - ], - "properties": { - "q": { - "type": "string" - }, - "options": { - "type": "object" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Meilisearch search results", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}": { - "get": { - "summary": "Get user profile", - "description": "Retrieves a user's profile by handle, with optional federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Profile and actor data", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}/trails": { - "post": { - "summary": "Search user trails", - "description": "Searches a user's trails via Meilisearch, with federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "q" - ], - "properties": { - "q": { - "type": "string" - }, - "options": { - "type": "object" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Meilisearch response with trail results", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}/stats": { - "get": { - "summary": "Get user summit statistics", - "description": "Retrieves summit log statistics for a user, with federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "SummitLog statistics", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}/lists": { - "post": { - "summary": "Search user lists", - "description": "Searches a user's lists via Meilisearch, with federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "q" - ], - "properties": { - "q": { - "type": "string" - }, - "options": { - "type": "object" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Meilisearch response with list results", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}/feed": { - "get": { - "summary": "Get user activity feed", - "description": "Retrieves activity feed for a user, with federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "FeedItem list", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/notification": { - "get": { - "summary": "List notifications", - "tags": [ - "Notifications" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of notifications", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/notification/{id}": { - "post": { - "summary": "Update notification", - "description": "Updates a notification by ID (typically to mark as read)", - "tags": [ - "Notifications" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Notification" - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list-share": { - "get": { - "summary": "List list shares", - "description": "Retrieves a paginated list of list shares with optional ActivityPub actor resolution", - "tags": [ - "List Shares" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListResult" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create list share", - "description": "Creates a new list share. Converts ActivityPub actor IRI to ID", - "tags": [ - "List Shares" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListShareInput" - } - } - } - }, - "responses": { - "201": { - "description": "List share created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list-share/{id}": { - "get": { - "summary": "Get list share", - "description": "Retrieves a list share by ID", - "tags": [ - "List Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListShare" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update list share", - "tags": [ - "List Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListShareUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "List share updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete list share", - "tags": [ - "List Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List share deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list": { - "get": { - "summary": "List all lists", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of lists", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create list", - "tags": [ - "Lists" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListInput" - } - } - } - }, - "responses": { - "201": { - "description": "List created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/List" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list/form": { - "put": { - "summary": "Create list with file upload", - "description": "Creates a new list with file upload (avatar)", - "tags": [ - "Lists" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "201": { - "description": "List" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list/form/{id}": { - "post": { - "summary": "Update list with file upload", - "description": "Updates a list with file upload (avatar)", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "List" - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list/{id}": { - "get": { - "summary": "Get list", - "description": "Retrieves a list by ID. Supports federated queries via handle parameter, fetching from remote instances and remapping file URLs", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List with optional federated data" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update list", - "description": "Updates a list by ID", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "List" - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete list", - "description": "Deletes a list by ID", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Success" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list/{id}/file": { - "post": { - "summary": "Upload list file", - "description": "Uploads a file (cover image) for a list", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded, list updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/List" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration": { - "get": { - "summary": "List integrations", - "tags": [ - "Integrations" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of integrations", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create integration", - "tags": [ - "Integrations" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/IntegrationInput" - } - } - } - }, - "responses": { - "201": { - "description": "Integration created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Integration" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration/komoot/login": { - "get": { - "summary": "Get Komoot login endpoint", - "description": "Proxies to backend to get Komoot login configuration", - "tags": [ - "Integrations" - ], - "responses": { - "200": { - "description": "Komoot login endpoint", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration/hammerhead/upload": { - "post": { - "summary": "Upload via Hammerhead integration", - "description": "Proxies file upload to backend Hammerhead integration", - "tags": [ - "Integrations" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "required": [ - "file" - ], - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Upload result", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration/hammerhead/login": { - "get": { - "summary": "Get Hammerhead login endpoint", - "description": "Proxies to backend to get Hammerhead login configuration", - "tags": [ - "Integrations" - ], - "responses": { - "200": { - "description": "Hammerhead login endpoint", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration/{id}": { - "get": { - "summary": "Get integration", - "tags": [ - "Integrations" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Integration details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Integration" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update integration", - "tags": [ - "Integrations" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/IntegrationUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Integration updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Integration" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete integration", - "tags": [ - "Integrations" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Integration deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/follow": { - "get": { - "summary": "List follows", - "description": "Retrieves follows or ActivityPub follower/following collections. Supports federated queries via handle parameter", - "tags": [ - "Follows" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "type", - "schema": { - "type": "string", - "enum": [ - "followers", - "following" - ] - } - } - ], - "responses": { - "200": { - "description": "List of follows or ActivityPub collection", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/follow/{id}": { - "delete": { - "summary": "Delete follow", - "tags": [ - "Follows" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Follow deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/files/{collection}/{record}/{file}": { - "get": { - "summary": "Download file", - "description": "Downloads a file from a record with optional thumbnail generation", - "tags": [ - "Files" - ], - "parameters": [ - { - "in": "path", - "name": "collection", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "path", - "name": "record", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "path", - "name": "file", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "thumb", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "File download", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/feed": { - "get": { - "summary": "Get activity feed", - "description": "Retrieves the user's activity feed", - "tags": [ - "Feed" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Activity feed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/comment": { - "get": { - "summary": "List comments", - "tags": [ - "Comments" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string", - "description": "Federated query parameter" - } - } - ], - "responses": { - "200": { - "description": "List of comments", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/comment/{id}": { - "get": { - "summary": "Get comment", - "tags": [ - "Comments" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Comment details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Comment" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update comment", - "tags": [ - "Comments" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CommentUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Comment updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Comment" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete comment", - "tags": [ - "Comments" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Comment deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/category": { - "get": { - "summary": "List categories", - "description": "Retrieves a paginated list of activity categories", - "tags": [ - "Categories" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of categories", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/auth/reset": { - "post": { - "summary": "Request password reset", - "description": "Sends a password reset email", - "tags": [ - "Authentication" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "email" - ], - "properties": { - "email": { - "type": "string", - "format": "email" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Password reset email sent" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/auth/oauth": { - "get": { - "summary": "List OAuth providers", - "description": "Lists available OAuth2 authentication methods with provider images", - "tags": [ - "Authentication" - ], - "responses": { - "200": { - "description": "OAuth2 providers and configuration", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/auth/login": { - "post": { - "summary": "Authenticate user", - "description": "Authenticates a user with email or username and password", - "tags": [ - "Authentication" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "password" - ], - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "User authenticated with auth token", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "description": "Bad Request - Invalid credentials" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/auth/confirm-reset": { - "post": { - "summary": "Confirm password reset", - "description": "Confirms and applies a password reset with a valid token", - "tags": [ - "Authentication" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Password reset completed" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/api-token": { - "get": { - "summary": "List API tokens", - "description": "Retrieves a paginated list of API tokens with optional filtering and sorting", - "tags": [ - "API Tokens" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer", - "default": 30 - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "page": { - "type": "integer" - }, - "perPage": { - "type": "integer" - }, - "totalItems": { - "type": "integer" - }, - "totalPages": { - "type": "integer" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/APIToken" - } - } - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create API token", - "description": "Creates a new API token", - "tags": [ - "API Tokens" - ], - "parameters": [ - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APITokenInput" - } - } - } - }, - "responses": { - "201": { - "description": "Created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIToken" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/api-token/{id}": { - "delete": { - "summary": "Delete API token", - "description": "Deletes an API token by ID", - "tags": [ - "API Tokens" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string", - "description": "API token ID (15 chars)" - } - } - ], - "responses": { - "200": { - "description": "Success" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}": { - "get": { - "summary": "Get ActivityPub actor profile", - "description": "Retrieves an ActivityPub Person object for a user with public key", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub Person object with publicKey", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}/outbox": { - "get": { - "summary": "Get ActivityPub outbox collection", - "description": "Retrieves an OrderedCollection paginated list of activities published by this user", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub OrderedCollectionPage", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}/inbox": { - "post": { - "summary": "Receive ActivityPub activities", - "description": "Receives and processes incoming ActivityPub activities (Create, Update, Delete, Follow, etc.)", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Activity processed" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}/following": { - "get": { - "summary": "Get ActivityPub following collection", - "description": "Retrieves an OrderedCollection paginated list of accounts the user follows", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub OrderedCollectionPage", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}/followers": { - "get": { - "summary": "Get ActivityPub followers collection", - "description": "Retrieves an OrderedCollection paginated list of followers for a user", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub OrderedCollectionPage", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/trail/{id}": { - "get": { - "summary": "Get ActivityPub trail", - "description": "Retrieves an ActivityPub Trail object by ID (proxied from backend)", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub Trail object", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/comment/{id}": { - "get": { - "summary": "Get ActivityPub comment", - "description": "Retrieves an ActivityPub Comment object by ID (proxied from backend)", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub Comment object", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/activity/{id}": { - "get": { - "summary": "Get ActivityPub activity", - "description": "Retrieves an ActivityPub Activity object by ID", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub Activity object", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - } - }, - "components": { - "schemas": { - "APIToken": { - "type": "object", - "required": [ - "id", - "name", - "user" - ], - "properties": { - "id": { - "type": "string", - "description": "Token ID (15 chars)" - }, - "name": { - "type": "string", - "description": "Token name" - }, - "user": { - "type": "string", - "description": "User ID that owns this token" - }, - "expiration": { - "type": "string", - "format": "date-time", - "description": "Optional expiration date" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "APITokenInput": { - "type": "object", - "required": [ - "name", - "user" - ], - "properties": { - "name": { - "type": "string", - "minLength": 1, - "description": "Token name" - }, - "expiration": { - "type": "string", - "format": "date-time", - "description": "Optional expiration date (must be in future)" - }, - "user": { - "type": "string", - "description": "User ID (15 chars)" - } - } - }, - "User": { - "type": "object", - "required": [ - "id", - "username", - "email" - ], - "properties": { - "id": { - "type": "string", - "description": "User ID (15 chars)" - }, - "username": { - "type": "string", - "description": "Username (3+ chars, alphanumeric with dots)" - }, - "email": { - "type": "string", - "format": "email", - "description": "User email address" - }, - "avatar": { - "type": "string", - "description": "Avatar file path" - }, - "verified": { - "type": "boolean", - "description": "Email verification status" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "UserCreateInput": { - "type": "object", - "required": [ - "username", - "email", - "password" - ], - "properties": { - "username": { - "type": "string", - "minLength": 3, - "pattern": "^\\w[\\w\\.]*$", - "description": "Username (3+ chars, starts with word char)" - }, - "email": { - "type": "string", - "format": "email", - "description": "User email address" - }, - "password": { - "type": "string", - "minLength": 8, - "maxLength": 72, - "description": "Password (8-72 chars)" - }, - "passwordConfirm": { - "type": "string", - "description": "Password confirmation (must match password)" - } - } - }, - "UserUpdateInput": { - "type": "object", - "properties": { - "username": { - "type": "string", - "minLength": 3, - "pattern": "^\\w[\\w\\.]*$" - }, - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 8, - "maxLength": 72 - }, - "oldPassword": { - "type": "string", - "description": "Required when changing password" - }, - "passwordConfirm": { - "type": "string", - "description": "Must match password" - } - } - }, - "Tag": { - "type": "object", - "required": [ - "id", - "name" - ], - "properties": { - "id": { - "type": "string", - "description": "Tag ID (15 chars)" - }, - "name": { - "type": "string", - "description": "Tag name" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "TagInput": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string", - "description": "Tag name" - } - } - }, - "TagUpdateInput": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Tag name" - } - } - }, - "Trail": { - "type": "object", - "required": [ - "id", - "name", - "author", - "public" - ], - "properties": { - "id": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "name": { - "type": "string", - "description": "Trail name" - }, - "description": { - "type": "string" - }, - "location": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID" - }, - "date": { - "type": "string", - "format": "date" - }, - "public": { - "type": "boolean" - }, - "difficulty": { - "type": "string", - "enum": [ - "easy", - "moderate", - "difficult" - ] - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance": { - "type": "number", - "description": "Distance in meters" - }, - "elevation_gain": { - "type": "number", - "description": "Elevation gain in meters" - }, - "elevation_loss": { - "type": "number", - "description": "Elevation loss in meters" - }, - "duration": { - "type": "number", - "description": "Duration in seconds" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "thumbnail": { - "type": "integer", - "description": "Index of thumbnail photo" - }, - "like_count": { - "type": "integer", - "default": 0 - }, - "category": { - "type": "string", - "description": "Category ID (15 chars)" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "gpx": { - "type": "string", - "description": "GPX file path" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "TrailCreateInput": { - "type": "object", - "required": [ - "name", - "author", - "public" - ], - "properties": { - "id": { - "type": "string", - "description": "Optional custom ID (15 chars)" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "description": { - "type": "string" - }, - "location": { - "type": "string" - }, - "date": { - "type": "string", - "format": "date" - }, - "public": { - "type": "boolean" - }, - "difficulty": { - "type": "string", - "enum": [ - "easy", - "moderate", - "difficult" - ] - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "thumbnail": { - "type": "integer" - }, - "like_count": { - "type": "integer", - "default": 0 - }, - "category": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "gpx": { - "type": "string" - } - } - }, - "TrailUpdateInput": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "location": { - "type": "string" - }, - "date": { - "type": "string", - "format": "date" - }, - "public": { - "type": "boolean" - }, - "difficulty": { - "type": "string", - "enum": [ - "easy", - "moderate", - "difficult" - ] - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "photos-": { - "type": "string", - "description": "Remove photo by name" - }, - "photos+": { - "type": "string", - "description": "Add photo by name" - }, - "thumbnail": { - "type": "integer" - }, - "like_count": { - "type": "integer", - "default": 0 - }, - "category": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "gpx": { - "type": "string" - } - } - }, - "Comment": { - "type": "object", - "required": [ - "id", - "text", - "author", - "trail" - ], - "properties": { - "id": { - "type": "string", - "description": "Comment ID (15 chars)" - }, - "text": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "CommentInput": { - "type": "object", - "required": [ - "text", - "author", - "trail" - ], - "properties": { - "text": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - } - } - }, - "CommentUpdateInput": { - "type": "object", - "properties": { - "text": { - "type": "string" - } - } - }, - "SummitLog": { - "type": "object", - "required": [ - "id", - "date", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Summit log ID (15 chars)" - }, - "date": { - "type": "string", - "format": "date" - }, - "text": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Associated trail ID" - }, - "gpx": { - "type": "string" - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "SummitLogInput": { - "type": "object", - "required": [ - "date", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Optional custom ID (15 chars)" - }, - "date": { - "type": "string", - "format": "date" - }, - "text": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "gpx": { - "type": "string" - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - } - } - }, - "SummitLogUpdateInput": { - "type": "object", - "properties": { - "date": { - "type": "string", - "format": "date" - }, - "text": { - "type": "string" - }, - "gpx": { - "type": "string" - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "photos-": { - "type": "string", - "description": "Remove photo by name" - }, - "photos+": { - "type": "string", - "description": "Add photo by name" - } - } - }, - "Waypoint": { - "type": "object", - "required": [ - "id", - "lat", - "lon", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Waypoint ID (15 chars)" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance_from_start": { - "type": "number", - "description": "Distance from trail start in meters" - }, - "icon": { - "type": "string", - "description": "Icon identifier" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "WaypointInput": { - "type": "object", - "required": [ - "lat", - "lon", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Optional custom ID (15 chars)" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance_from_start": { - "type": "number" - }, - "icon": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "trail": { - "type": "string" - } - } - }, - "WaypointUpdateInput": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance_from_start": { - "type": "number" - }, - "icon": { - "type": "string" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "photos-": { - "type": "string", - "description": "Remove photo by name" - }, - "photos+": { - "type": "string", - "description": "Add photo by name" - } - } - }, - "List": { - "type": "object", - "required": [ - "id", - "name", - "author", - "public" - ], - "properties": { - "id": { - "type": "string", - "description": "List ID (15 chars)" - }, - "name": { - "type": "string" - }, - "public": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID" - }, - "trails": { - "type": "array", - "items": { - "type": "string", - "description": "Trail IDs (15 chars)" - } - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "ListInput": { - "type": "object", - "required": [ - "name", - "public", - "trails", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Optional custom ID (15 chars)" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "public": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "trails": { - "type": "array", - "items": { - "type": "string", - "description": "Trail ID (15 chars)" - } - }, - "author": { - "type": "string" - } - } - }, - "ListUpdateInput": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "public": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "trails": { - "type": "array", - "items": { - "type": "string" - } - }, - "trails-": { - "type": "string", - "description": "Remove trail by ID" - }, - "trails+": { - "type": "string", - "description": "Add trail by ID" - } - } - }, - "TrailShare": { - "type": "object", - "required": [ - "id", - "actor", - "trail", - "permission" - ], - "properties": { - "id": { - "type": "string", - "description": "Share ID (15 chars)" - }, - "actor": { - "type": "string", - "format": "uri", - "description": "ActivityPub actor IRI" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "TrailShareInput": { - "type": "object", - "required": [ - "actor", - "trail", - "permission" - ], - "properties": { - "actor": { - "type": "string", - "format": "uri", - "description": "ActivityPub actor IRI" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "TrailShareUpdateInput": { - "type": "object", - "required": [ - "permission" - ], - "properties": { - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "ListShare": { - "type": "object", - "required": [ - "id", - "actor", - "list", - "permission" - ], - "properties": { - "id": { - "type": "string", - "description": "Share ID (15 chars)" - }, - "actor": { - "type": "string", - "format": "uri", - "description": "ActivityPub actor IRI" - }, - "list": { - "type": "string", - "description": "List ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "ListShareInput": { - "type": "object", - "required": [ - "actor", - "list", - "permission" - ], - "properties": { - "actor": { - "type": "string", - "format": "uri", - "description": "ActivityPub actor IRI" - }, - "list": { - "type": "string", - "description": "List ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "ListShareUpdateInput": { - "type": "object", - "required": [ - "permission" - ], - "properties": { - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "TrailLinkShare": { - "type": "object", - "required": [ - "id", - "trail", - "permission" - ], - "properties": { - "id": { - "type": "string", - "description": "Share ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "TrailLinkShareInput": { - "type": "object", - "required": [ - "trail", - "permission" - ], - "properties": { - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "TrailLinkShareUpdateInput": { - "type": "object", - "required": [ - "permission" - ], - "properties": { - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "TrailLike": { - "type": "object", - "required": [ - "id", - "actor", - "trail" - ], - "properties": { - "id": { - "type": "string", - "description": "Like ID (15 chars)" - }, - "actor": { - "type": "string", - "description": "User ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "created": { - "type": "string", - "format": "date-time" - } - } - }, - "TrailLikeInput": { - "type": "object", - "required": [ - "actor", - "trail" - ], - "properties": { - "actor": { - "type": "string", - "description": "User ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - } - } - }, - "Follow": { - "type": "object", - "required": [ - "id", - "follower", - "followee" - ], - "properties": { - "id": { - "type": "string", - "description": "Follow ID (15 chars)" - }, - "follower": { - "type": "string", - "description": "Follower user ID" - }, - "followee": { - "type": "string", - "description": "Followee user ID (15 chars)" - }, - "created": { - "type": "string", - "format": "date-time" - } - } - }, - "FollowInput": { - "type": "object", - "required": [ - "followee" - ], - "properties": { - "followee": { - "type": "string", - "description": "Followee user ID (15 chars)" - } - } - }, - "Integration": { - "type": "object", - "required": [ - "id", - "user" - ], - "properties": { - "id": { - "type": "string", - "description": "Integration ID (15 chars)" - }, - "user": { - "type": "string", - "description": "User ID (15 chars)" - }, - "strava": { - "type": "object", - "properties": { - "clientId": { - "type": "integer" - }, - "clientSecret": { - "type": "string" - }, - "routes": { - "type": "boolean" - }, - "activities": { - "type": "boolean" - }, - "active": { - "type": "boolean" - }, - "after": { - "type": "string", - "format": "date" - }, - "privacy": { - "type": "string", - "enum": [ - "original", - "settings" - ] - } - } - }, - "komoot": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string" - }, - "completed": { - "type": "boolean" - }, - "planned": { - "type": "boolean" - }, - "active": { - "type": "boolean" - }, - "privacy": { - "type": "string", - "enum": [ - "original", - "settings" - ] - } - } - }, - "hammerhead": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string" - }, - "completed": { - "type": "boolean" - }, - "planned": { - "type": "boolean" - }, - "active": { - "type": "boolean" - }, - "after": { - "type": "string", - "format": "date" - } - } - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "IntegrationInput": { - "type": "object", - "required": [ - "user" - ], - "properties": { - "user": { - "type": "string", - "description": "User ID (15 chars)" - }, - "strava": { - "type": "object" - }, - "komoot": { - "type": "object" - }, - "hammerhead": { - "type": "object" - } - } - }, - "IntegrationUpdateInput": { - "type": "object", - "properties": { - "strava": { - "type": "object", - "nullable": true - }, - "komoot": { - "type": "object", - "nullable": true - }, - "hammerhead": { - "type": "object", - "nullable": true - } - } - }, - "Notification": { - "type": "object", - "required": [ - "id", - "user", - "type", - "seen" - ], - "properties": { - "id": { - "type": "string", - "description": "Notification ID (15 chars)" - }, - "user": { - "type": "string", - "description": "User ID (15 chars)" - }, - "type": { - "type": "string", - "description": "Notification type" - }, - "seen": { - "type": "boolean", - "default": false - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "NotificationUpdateInput": { - "type": "object", - "required": [ - "seen" - ], - "properties": { - "seen": { - "type": "boolean", - "const": true, - "description": "Mark as read" - } - } - }, - "Settings": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "type": "string", - "description": "Settings ID (15 chars)" - }, - "unit": { - "type": "string", - "enum": [ - "metric", - "imperial" - ] - }, - "language": { - "type": "string", - "description": "Language code" - }, - "bio": { - "type": "string", - "nullable": true - }, - "mapFocus": { - "type": "string", - "enum": [ - "trails", - "location" - ] - }, - "location": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "lat": { - "type": "number" - }, - "lon": { - "type": "number" - } - }, - "nullable": true - }, - "category": { - "type": "string" - }, - "tilesets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - } - } - }, - "nullable": true - }, - "terrain": { - "type": "object", - "properties": { - "terrain": { - "type": "string", - "format": "uri" - }, - "hillshading": { - "type": "string", - "format": "uri" - } - }, - "nullable": true - }, - "user": { - "type": "string" - }, - "privacy": { - "type": "object", - "properties": { - "account": { - "type": "string", - "enum": [ - "public", - "private" - ] - }, - "trails": { - "type": "string", - "enum": [ - "public", - "private" - ] - }, - "lists": { - "type": "string", - "enum": [ - "public", - "private" - ] - } - }, - "nullable": true - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "SettingsInput": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "metric", - "imperial" - ] - }, - "language": { - "type": "string" - }, - "bio": { - "type": "string", - "nullable": true - }, - "mapFocus": { - "type": "string", - "enum": [ - "trails", - "location" - ] - }, - "location": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "lat": { - "type": "number" - }, - "lon": { - "type": "number" - } - }, - "nullable": true - }, - "category": { - "type": "string" - }, - "tilesets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - } - } - }, - "nullable": true - }, - "terrain": { - "type": "object", - "properties": { - "terrain": { - "type": "string", - "format": "uri" - }, - "hillshading": { - "type": "string", - "format": "uri" - } - }, - "nullable": true - }, - "user": { - "type": "string" - }, - "privacy": { - "type": "object", - "properties": { - "account": { - "type": "string", - "enum": [ - "public", - "private" - ] - }, - "trails": { - "type": "string", - "enum": [ - "public", - "private" - ] - }, - "lists": { - "type": "string", - "enum": [ - "public", - "private" - ] - } - }, - "nullable": true - } - } - }, - "TrailMergeSettings": { - "type": "object", - "required": [ - "summitLog", - "photos", - "comments", - "delete", - "tags", - "likes" - ], - "properties": { - "summitLog": { - "type": "boolean" - }, - "photos": { - "type": "boolean" - }, - "comments": { - "type": "boolean" - }, - "delete": { - "type": "boolean" - }, - "tags": { - "type": "boolean" - }, - "likes": { - "type": "boolean" - } - } - }, - "TrailMergeSuggestRequest": { - "type": "object", - "required": [ - "mode" - ], - "properties": { - "mode": { - "type": "string", - "enum": [ - "manual-selection", - "auto-discovery", - "maintenance-groups" - ] - }, - "trailIds": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Required for manual-selection" - }, - "sourceTrailId": { - "type": "string", - "description": "Required for auto-discovery" - } - } - }, - "TrailMergeSuggestCandidate": { - "type": "object", - "required": [ - "trailId", - "score", - "reason", - "warnings", - "selectable" - ], - "properties": { - "trailId": { - "type": "string" - }, - "score": { - "type": "number" - }, - "reason": { - "type": "string" - }, - "warnings": { - "type": "array", - "items": { - "type": "string" - } - }, - "selectable": { - "type": "boolean" - } - } - }, - "TrailMergeSuggestResponse": { - "type": "object", - "required": [ - "targetTrailId", - "reason", - "warnings", - "candidates" - ], - "properties": { - "targetTrailId": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "warnings": { - "type": "array", - "items": { - "type": "string" - } - }, - "candidates": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TrailMergeSuggestCandidate" - } - } - } - }, - "TrailMergeSuggestGroup": { - "type": "object", - "required": [ - "groupId", - "trailIds", - "targetTrailId", - "reason", - "score", - "indirect" - ], - "properties": { - "groupId": { - "type": "string" - }, - "trailIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "targetTrailId": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "score": { - "type": "number" - }, - "indirect": { - "type": "boolean" - } - } - }, - "TrailMergeSuggestGroupsResponse": { - "type": "object", - "required": [ - "groups" - ], - "properties": { - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TrailMergeSuggestGroup" - } - } - } - }, - "TrailMergeExecuteRequest": { - "type": "object", - "required": [ - "sourceTrailId", - "targetTrailId", - "settings" - ], - "properties": { - "sourceTrailId": { - "type": "string" - }, - "targetTrailId": { - "type": "string" - }, - "settings": { - "$ref": "#/components/schemas/TrailMergeSettings" - } - } - }, - "TrailMergeExecuteResponse": { - "type": "object", - "required": [ - "acknowledged" - ], - "properties": { - "acknowledged": { - "type": "boolean" - } - } - }, - "ListResult": { - "type": "object", - "properties": { - "page": { - "type": "integer" - }, - "perPage": { - "type": "integer" - }, - "totalItems": { - "type": "integer" - }, - "totalPages": { - "type": "integer" - }, - "items": { - "type": "array", - "items": { - "type": "object" - } - } - } - }, - "Error": { - "type": "object", - "required": [ - "message" - ], - "properties": { - "message": { - "type": "string" - }, - "code": { - "type": "integer" - }, - "data": { - "type": "object" - } - } - } - } - } -} \ No newline at end of file diff --git a/web/.gitignore b/web/.gitignore index 8f46e3f6..6ab129ed 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -3,6 +3,7 @@ node_modules /build /.svelte-kit /package +static/docs/api/wanderer.openapi.json .env .env.* !.env.example diff --git a/web/openapi.config.js b/web/openapi.config.js new file mode 100644 index 00000000..bcb58724 --- /dev/null +++ b/web/openapi.config.js @@ -0,0 +1,12 @@ +export function openapiOptions(version) { + return { + info: { + title: 'Wanderer API', + version, + description: 'API documentation for wanderer backend', + }, + outputPath: 'static/docs/api/wanderer.openapi.json', + include: ['src/routes/api/v1/**/*.{js,ts}'], + baseSchemasPath: 'src/lib/models/api/openapi_schemas.ts', + }; +} diff --git a/web/package.json b/web/package.json index 99353186..864c0252 100644 --- a/web/package.json +++ b/web/package.json @@ -5,6 +5,7 @@ "scripts": { "dev": "vite dev", "build": "vite build", + "openapi:generate": "node scripts/generate-openapi.js", "start": "node watcher.js & node build", "preview": "vite preview", "test": "npm run test:integration && npm run test:unit", diff --git a/web/scripts/generate-openapi.js b/web/scripts/generate-openapi.js new file mode 100644 index 00000000..5dac252b --- /dev/null +++ b/web/scripts/generate-openapi.js @@ -0,0 +1,18 @@ +import { readFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { generateSpec, writeSpec } from '../node_modules/sveltekit-openapi-generator/dist/generator.js'; +import { openapiOptions } from '../openapi.config.js'; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const webDir = resolve(scriptDir, '..'); +const packageJson = JSON.parse(readFileSync(resolve(webDir, 'package.json'), 'utf-8')); +const { outputPath, ...generatorOptions } = openapiOptions(packageJson.version); + +const spec = generateSpec({ + rootDir: webDir, + ...generatorOptions, +}); + +writeSpec(spec, resolve(webDir, outputPath)); diff --git a/web/static/docs/api/wanderer.openapi.json b/web/static/docs/api/wanderer.openapi.json deleted file mode 100644 index 6dd11b4f..00000000 --- a/web/static/docs/api/wanderer.openapi.json +++ /dev/null @@ -1,6930 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "Wanderer API", - "version": "0.18.5", - "description": "API documentation for wanderer backend" - }, - "servers": [], - "paths": { - "/api/v1/waypoint": { - "get": { - "summary": "List waypoints", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of waypoints", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create waypoint", - "tags": [ - "Waypoints" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WaypointInput" - } - } - } - }, - "responses": { - "201": { - "description": "Waypoint created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Waypoint" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/waypoint/{id}": { - "get": { - "summary": "Get waypoint", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Waypoint details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Waypoint" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update waypoint", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WaypointUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Waypoint updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Waypoint" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete waypoint", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Waypoint deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/waypoint/{id}/file": { - "post": { - "summary": "Upload waypoint file", - "description": "Uploads a file (photo) for a waypoint", - "tags": [ - "Waypoints" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded, waypoint updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Waypoint" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/valhalla/route": { - "post": { - "summary": "Get route data", - "description": "Queries Valhalla service for routing data", - "tags": [ - "Valhalla" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Route data from Valhalla", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/valhalla/height": { - "post": { - "summary": "Get elevation data", - "description": "Queries Valhalla service for elevation data at coordinates", - "tags": [ - "Valhalla" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Elevation data from Valhalla", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/user": { - "put": { - "summary": "Create user (sign up)", - "tags": [ - "Users" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserCreateInput" - } - } - } - }, - "responses": { - "201": { - "description": "User created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "description": "Bad Request - Signup disabled or invalid data" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/user/{id}": { - "get": { - "summary": "Get user by ID", - "tags": [ - "Users" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "User details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update user", - "description": "Updates a user. Handles password changes and email change requests", - "tags": [ - "Users" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "User updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete user", - "tags": [ - "Users" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "User deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/user/{id}/file": { - "post": { - "summary": "Upload user file", - "description": "Uploads a file (avatar) for a user", - "tags": [ - "Users" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded, user updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-share": { - "get": { - "summary": "List trail shares", - "description": "Retrieves a paginated list of trail shares with optional ActivityPub actor resolution", - "tags": [ - "Trail Shares" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListResult" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create trail share", - "description": "Creates a new trail share. Converts ActivityPub actor IRI to ID", - "tags": [ - "Trail Shares" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailShareInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail share created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-share/{id}": { - "get": { - "summary": "Get trail share", - "description": "Retrieves a trail share by ID", - "tags": [ - "Trail Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "TrailShare" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update trail share", - "tags": [ - "Trail Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailShareUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Trail share updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete trail share", - "tags": [ - "Trail Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Trail share deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-merge": { - "post": { - "summary": "Merge trails", - "description": "Merges a source trail into a target trail using the backend merge logic.", - "tags": [ - "Trail Merge" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailMergeExecuteRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Merge acknowledged", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailMergeExecuteResponse" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, - "/api/v1/trail-merge/suggest": { - "post": { - "summary": "Suggest merge target or duplicate groups", - "description": "Returns merge target suggestions for manual selection or auto-discovery, or temporary duplicate groups for maintenance workflows.", - "tags": [ - "Trail Merge" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailMergeSuggestRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Suggestion response", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/TrailMergeSuggestResponse" - }, - { - "$ref": "#/components/schemas/TrailMergeSuggestGroupsResponse" - } - ] - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, - "/api/v1/trail-link-share": { - "get": { - "summary": "List trail link shares", - "tags": [ - "Trail Link Shares" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of trail link shares", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create trail link share", - "tags": [ - "Trail Link Shares" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLinkShareInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail link share created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLinkShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-link-share/{id}": { - "get": { - "summary": "Get trail link share", - "description": "Retrieves a trail link share by ID", - "tags": [ - "Trail Link Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "TrailLinkShare" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update trail link share", - "tags": [ - "Trail Link Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLinkShareUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Trail link share updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLinkShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete trail link share", - "tags": [ - "Trail Link Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Trail link share deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-like": { - "get": { - "summary": "List trail likes", - "tags": [ - "Trail Likes" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of trail likes", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create trail like", - "tags": [ - "Trail Likes" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLikeInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail like created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLike" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-like/delete": { - "post": { - "summary": "Delete trail like by actor and trail", - "description": "Deletes a trail like without needing to know its ID. Uses actor and trail IDs to find and delete", - "tags": [ - "Trail Likes" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailLikeInput" - } - } - } - }, - "responses": { - "200": { - "description": "Trail like deleted" - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail-like/{id}": { - "get": { - "summary": "Get trail like", - "description": "Retrieves a trail like by ID", - "tags": [ - "Trail Likes" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "TrailLike" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete trail like", - "tags": [ - "Trail Likes" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Trail like deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail": { - "get": { - "summary": "List trails", - "description": "Retrieves a paginated list of trails with optional filtering and sorting", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListResult" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create trail", - "tags": [ - "Trails" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrailCreateInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/upload": { - "put": { - "summary": "Upload and parse GPX file as trail", - "description": "Uploads a GPX file, parses it to extract trail data, performs duplicate detection, and indexes in search", - "tags": [ - "Trails" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - }, - "name": { - "type": "string" - }, - "ignoreDuplicates": { - "type": "boolean" - } - } - } - } - } - }, - "responses": { - "201": { - "description": "Trail created from GPX", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request - Invalid or empty GPX file" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/recommend": { - "get": { - "summary": "Get trail recommendations", - "description": "Retrieves random trail recommendations from Meilisearch", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "query", - "name": "size", - "schema": { - "type": "integer", - "default": 10 - } - } - ], - "responses": { - "200": { - "description": "Array of recommended trails", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Trail" - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/form": { - "put": { - "summary": "Create trail with file upload", - "description": "Creates a new trail with file upload (GPX/photos) and date normalization", - "tags": [ - "Trails" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/TrailCreateInput" - } - } - } - }, - "responses": { - "201": { - "description": "Trail created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/form/{id}": { - "post": { - "summary": "Update trail with file upload", - "description": "Updates a trail with file upload (GPX/photos) and date normalization", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/TrailUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Trail updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/filter": { - "get": { - "summary": "Get trail filter values", - "description": "Retrieves min/max values for trail filtering (distance, elevation gain/loss)", - "tags": [ - "Trails" - ], - "responses": { - "200": { - "description": "Trail filter values (min/max for distance, elevation)", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "min_distance": { - "type": "number" - }, - "max_distance": { - "type": "number" - }, - "min_elevation_gain": { - "type": "number" - }, - "max_elevation_gain": { - "type": "number" - }, - "min_elevation_loss": { - "type": "number" - }, - "max_elevation_loss": { - "type": "number" - } - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/download": { - "post": { - "summary": "Download file from URL", - "description": "Downloads a file from a URL and returns it as a blob", - "tags": [ - "Trails" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "url" - ], - "properties": { - "url": { - "type": "string", - "format": "uri" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File blob", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/bounding-box": { - "get": { - "summary": "Get trail bounding box", - "description": "Retrieves geographic bounding box (lat/lon bounds) for user's trails", - "tags": [ - "Trails" - ], - "responses": { - "200": { - "description": "Bounding box coordinates", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "max_lat": { - "type": "number" - }, - "min_lat": { - "type": "number" - }, - "max_lon": { - "type": "number" - }, - "min_lon": { - "type": "number" - } - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/{id}": { - "get": { - "summary": "Get trail", - "description": "Retrieves a trail by ID. Supports federated queries via handle parameter, fetching from remote instances and remapping file URLs", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "share", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Trail with optional federated data" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/trail/{id}/file": { - "post": { - "summary": "Upload trail file", - "description": "Uploads a file for a trail", - "tags": [ - "Trails" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Trail" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/tag": { - "get": { - "summary": "List tags", - "tags": [ - "Tags" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of tags", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create tag", - "tags": [ - "Tags" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TagInput" - } - } - } - }, - "responses": { - "201": { - "description": "Tag created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Tag" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/tag/{id}": { - "get": { - "summary": "Get tag", - "tags": [ - "Tags" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Tag details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Tag" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update tag", - "tags": [ - "Tags" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TagUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Tag updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Tag" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete tag", - "tags": [ - "Tags" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Tag deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log": { - "get": { - "summary": "List summit logs", - "description": "Retrieves a paginated list of summit logs with deduplication of federated data", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListResult with local/remote items deduplicated" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create summit log", - "tags": [ - "Summit Logs" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLogInput" - } - } - } - }, - "responses": { - "201": { - "description": "Summit log created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log/form": { - "put": { - "summary": "Create summit log with file upload", - "description": "Creates a new summit log with file upload (photos/GPX) and date normalization", - "tags": [ - "Summit Logs" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SummitLogInput" - } - } - } - }, - "responses": { - "201": { - "description": "Summit log created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log/form/{id}": { - "post": { - "summary": "Update summit log with file upload", - "description": "Updates a summit log with file upload (photos/GPX) and date normalization", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SummitLogUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Summit log updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log/{id}": { - "get": { - "summary": "Get summit log", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Summit log details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update summit log", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLogUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Summit log updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete summit log", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Summit log deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/summit-log/{id}/file": { - "post": { - "summary": "Upload summit log file", - "description": "Uploads a file (photo or GPX) for a summit log", - "tags": [ - "Summit Logs" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded, summit log updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SummitLog" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/settings": { - "put": { - "summary": "Create settings", - "tags": [ - "Settings" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SettingsInput" - } - } - } - }, - "responses": { - "201": { - "description": "Settings created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Settings" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/settings/{id}": { - "get": { - "summary": "Get settings", - "tags": [ - "Settings" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Settings details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Settings" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update settings", - "tags": [ - "Settings" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SettingsInput" - } - } - } - }, - "responses": { - "200": { - "description": "Settings updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Settings" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete settings", - "tags": [ - "Settings" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Settings deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/search/multi": { - "post": { - "summary": "Multi-index search", - "description": "Performs batch searches across multiple Meilisearch indices", - "tags": [ - "Search" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "queries" - ], - "properties": { - "queries": { - "type": "array", - "items": { - "type": "object" - } - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Combined Meilisearch results", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/search/actor": { - "get": { - "summary": "Search actors", - "description": "Searches for ActivityPub actors by username, combining local and federated results", - "tags": [ - "Search" - ], - "parameters": [ - { - "in": "query", - "name": "q", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "includeSelf", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "200": { - "description": "Array of matching actors", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object" - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/search/{index}": { - "post": { - "summary": "Search Meilisearch index", - "description": "Performs a search on a specific Meilisearch index", - "tags": [ - "Search" - ], - "parameters": [ - { - "in": "path", - "name": "index", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "q" - ], - "properties": { - "q": { - "type": "string" - }, - "options": { - "type": "object" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Meilisearch search results", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}": { - "get": { - "summary": "Get user profile", - "description": "Retrieves a user's profile by handle, with optional federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Profile and actor data", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}/trails": { - "post": { - "summary": "Search user trails", - "description": "Searches a user's trails via Meilisearch, with federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "q" - ], - "properties": { - "q": { - "type": "string" - }, - "options": { - "type": "object" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Meilisearch response with trail results", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}/stats": { - "get": { - "summary": "Get user summit statistics", - "description": "Retrieves summit log statistics for a user, with federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "SummitLog statistics", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}/lists": { - "post": { - "summary": "Search user lists", - "description": "Searches a user's lists via Meilisearch, with federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "q" - ], - "properties": { - "q": { - "type": "string" - }, - "options": { - "type": "object" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Meilisearch response with list results", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/profile/{handle}/feed": { - "get": { - "summary": "Get user activity feed", - "description": "Retrieves activity feed for a user, with federation support", - "tags": [ - "Profiles" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "FeedItem list", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/notification": { - "get": { - "summary": "List notifications", - "tags": [ - "Notifications" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of notifications", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/notification/{id}": { - "post": { - "summary": "Update notification", - "description": "Updates a notification by ID (typically to mark as read)", - "tags": [ - "Notifications" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Notification" - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list-share": { - "get": { - "summary": "List list shares", - "description": "Retrieves a paginated list of list shares with optional ActivityPub actor resolution", - "tags": [ - "List Shares" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListResult" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create list share", - "description": "Creates a new list share. Converts ActivityPub actor IRI to ID", - "tags": [ - "List Shares" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListShareInput" - } - } - } - }, - "responses": { - "201": { - "description": "List share created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list-share/{id}": { - "get": { - "summary": "Get list share", - "description": "Retrieves a list share by ID", - "tags": [ - "List Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ListShare" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update list share", - "tags": [ - "List Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListShareUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "List share updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListShare" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete list share", - "tags": [ - "List Shares" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List share deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list": { - "get": { - "summary": "List all lists", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of lists", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create list", - "tags": [ - "Lists" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListInput" - } - } - } - }, - "responses": { - "201": { - "description": "List created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/List" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list/form": { - "put": { - "summary": "Create list with file upload", - "description": "Creates a new list with file upload (avatar)", - "tags": [ - "Lists" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "201": { - "description": "List" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list/form/{id}": { - "post": { - "summary": "Update list with file upload", - "description": "Updates a list with file upload (avatar)", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "List" - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list/{id}": { - "get": { - "summary": "Get list", - "description": "Retrieves a list by ID. Supports federated queries via handle parameter, fetching from remote instances and remapping file URLs", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List with optional federated data" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update list", - "description": "Updates a list by ID", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "List" - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete list", - "description": "Deletes a list by ID", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Success" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/list/{id}/file": { - "post": { - "summary": "Upload list file", - "description": "Uploads a file (cover image) for a list", - "tags": [ - "Lists" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "File uploaded, list updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/List" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration": { - "get": { - "summary": "List integrations", - "tags": [ - "Integrations" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of integrations", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create integration", - "tags": [ - "Integrations" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/IntegrationInput" - } - } - } - }, - "responses": { - "201": { - "description": "Integration created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Integration" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration/komoot/login": { - "get": { - "summary": "Get Komoot login endpoint", - "description": "Proxies to backend to get Komoot login configuration", - "tags": [ - "Integrations" - ], - "responses": { - "200": { - "description": "Komoot login endpoint", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration/hammerhead/upload": { - "post": { - "summary": "Upload via Hammerhead integration", - "description": "Proxies file upload to backend Hammerhead integration", - "tags": [ - "Integrations" - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "type": "object", - "required": [ - "file" - ], - "properties": { - "file": { - "type": "string", - "format": "binary" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Upload result", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration/hammerhead/login": { - "get": { - "summary": "Get Hammerhead login endpoint", - "description": "Proxies to backend to get Hammerhead login configuration", - "tags": [ - "Integrations" - ], - "responses": { - "200": { - "description": "Hammerhead login endpoint", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/integration/{id}": { - "get": { - "summary": "Get integration", - "tags": [ - "Integrations" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Integration details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Integration" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update integration", - "tags": [ - "Integrations" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/IntegrationUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Integration updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Integration" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete integration", - "tags": [ - "Integrations" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Integration deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/follow": { - "get": { - "summary": "List follows", - "description": "Retrieves follows or ActivityPub follower/following collections. Supports federated queries via handle parameter", - "tags": [ - "Follows" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "type", - "schema": { - "type": "string", - "enum": [ - "followers", - "following" - ] - } - } - ], - "responses": { - "200": { - "description": "List of follows or ActivityPub collection", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/follow/{id}": { - "delete": { - "summary": "Delete follow", - "tags": [ - "Follows" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Follow deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/files/{collection}/{record}/{file}": { - "get": { - "summary": "Download file", - "description": "Downloads a file from a record with optional thumbnail generation", - "tags": [ - "Files" - ], - "parameters": [ - { - "in": "path", - "name": "collection", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "path", - "name": "record", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "path", - "name": "file", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "thumb", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "File download", - "content": { - "application/octet-stream": { - "schema": { - "type": "string", - "format": "binary" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/feed": { - "get": { - "summary": "Get activity feed", - "description": "Retrieves the user's activity feed", - "tags": [ - "Feed" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Activity feed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/comment": { - "get": { - "summary": "List comments", - "tags": [ - "Comments" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "handle", - "schema": { - "type": "string", - "description": "Federated query parameter" - } - } - ], - "responses": { - "200": { - "description": "List of comments", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/comment/{id}": { - "get": { - "summary": "Get comment", - "tags": [ - "Comments" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Comment details", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Comment" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "post": { - "summary": "Update comment", - "tags": [ - "Comments" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CommentUpdateInput" - } - } - } - }, - "responses": { - "200": { - "description": "Comment updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Comment" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "delete": { - "summary": "Delete comment", - "tags": [ - "Comments" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Comment deleted" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/category": { - "get": { - "summary": "List categories", - "description": "Retrieves a paginated list of activity categories", - "tags": [ - "Categories" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "List of categories", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListResult" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/auth/reset": { - "post": { - "summary": "Request password reset", - "description": "Sends a password reset email", - "tags": [ - "Authentication" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "email" - ], - "properties": { - "email": { - "type": "string", - "format": "email" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Password reset email sent" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/auth/oauth": { - "get": { - "summary": "List OAuth providers", - "description": "Lists available OAuth2 authentication methods with provider images", - "tags": [ - "Authentication" - ], - "responses": { - "200": { - "description": "OAuth2 providers and configuration", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/auth/login": { - "post": { - "summary": "Authenticate user", - "description": "Authenticates a user with email or username and password", - "tags": [ - "Authentication" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "password" - ], - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "User authenticated with auth token", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User" - } - } - } - }, - "400": { - "description": "Bad Request - Invalid credentials" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/auth/confirm-reset": { - "post": { - "summary": "Confirm password reset", - "description": "Confirms and applies a password reset with a valid token", - "tags": [ - "Authentication" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Password reset completed" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/api-token": { - "get": { - "summary": "List API tokens", - "description": "Retrieves a paginated list of API tokens with optional filtering and sorting", - "tags": [ - "API Tokens" - ], - "parameters": [ - { - "in": "query", - "name": "page", - "schema": { - "type": "integer", - "default": 1 - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer", - "default": 30 - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "filter", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Success", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "page": { - "type": "integer" - }, - "perPage": { - "type": "integer" - }, - "totalItems": { - "type": "integer" - }, - "totalPages": { - "type": "integer" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/APIToken" - } - } - } - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - }, - "put": { - "summary": "Create API token", - "description": "Creates a new API token", - "tags": [ - "API Tokens" - ], - "parameters": [ - { - "in": "query", - "name": "expand", - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APITokenInput" - } - } - } - }, - "responses": { - "201": { - "description": "Created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIToken" - } - } - } - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/api-token/{id}": { - "delete": { - "summary": "Delete API token", - "description": "Deletes an API token by ID", - "tags": [ - "API Tokens" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string", - "description": "API token ID (15 chars)" - } - } - ], - "responses": { - "200": { - "description": "Success" - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}": { - "get": { - "summary": "Get ActivityPub actor profile", - "description": "Retrieves an ActivityPub Person object for a user with public key", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub Person object with publicKey", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}/outbox": { - "get": { - "summary": "Get ActivityPub outbox collection", - "description": "Retrieves an OrderedCollection paginated list of activities published by this user", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "perPage", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub OrderedCollectionPage", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}/inbox": { - "post": { - "summary": "Receive ActivityPub activities", - "description": "Receives and processes incoming ActivityPub activities (Create, Update, Delete, Follow, etc.)", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "Activity processed" - }, - "400": { - "description": "Bad Request" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}/following": { - "get": { - "summary": "Get ActivityPub following collection", - "description": "Retrieves an OrderedCollection paginated list of accounts the user follows", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub OrderedCollectionPage", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/user/{handle}/followers": { - "get": { - "summary": "Get ActivityPub followers collection", - "description": "Retrieves an OrderedCollection paginated list of followers for a user", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "handle", - "required": true, - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub OrderedCollectionPage", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/trail/{id}": { - "get": { - "summary": "Get ActivityPub trail", - "description": "Retrieves an ActivityPub Trail object by ID (proxied from backend)", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub Trail object", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/comment/{id}": { - "get": { - "summary": "Get ActivityPub comment", - "description": "Retrieves an ActivityPub Comment object by ID (proxied from backend)", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub Comment object", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - }, - "/api/v1/activitypub/activity/{id}": { - "get": { - "summary": "Get ActivityPub activity", - "description": "Retrieves an ActivityPub Activity object by ID", - "tags": [ - "ActivityPub" - ], - "parameters": [ - { - "in": "path", - "name": "id", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "ActivityPub Activity object", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "404": { - "description": "Not Found" - }, - "500": { - "description": "Internal Server Error" - } - } - } - } - }, - "components": { - "schemas": { - "APIToken": { - "type": "object", - "required": [ - "id", - "name", - "user" - ], - "properties": { - "id": { - "type": "string", - "description": "Token ID (15 chars)" - }, - "name": { - "type": "string", - "description": "Token name" - }, - "user": { - "type": "string", - "description": "User ID that owns this token" - }, - "expiration": { - "type": "string", - "format": "date-time", - "description": "Optional expiration date" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "APITokenInput": { - "type": "object", - "required": [ - "name", - "user" - ], - "properties": { - "name": { - "type": "string", - "minLength": 1, - "description": "Token name" - }, - "expiration": { - "type": "string", - "format": "date-time", - "description": "Optional expiration date (must be in future)" - }, - "user": { - "type": "string", - "description": "User ID (15 chars)" - } - } - }, - "User": { - "type": "object", - "required": [ - "id", - "username", - "email" - ], - "properties": { - "id": { - "type": "string", - "description": "User ID (15 chars)" - }, - "username": { - "type": "string", - "description": "Username (3+ chars, alphanumeric with dots)" - }, - "email": { - "type": "string", - "format": "email", - "description": "User email address" - }, - "avatar": { - "type": "string", - "description": "Avatar file path" - }, - "verified": { - "type": "boolean", - "description": "Email verification status" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "UserCreateInput": { - "type": "object", - "required": [ - "username", - "email", - "password" - ], - "properties": { - "username": { - "type": "string", - "minLength": 3, - "pattern": "^\\w[\\w\\.]*$", - "description": "Username (3+ chars, starts with word char)" - }, - "email": { - "type": "string", - "format": "email", - "description": "User email address" - }, - "password": { - "type": "string", - "minLength": 8, - "maxLength": 72, - "description": "Password (8-72 chars)" - }, - "passwordConfirm": { - "type": "string", - "description": "Password confirmation (must match password)" - } - } - }, - "UserUpdateInput": { - "type": "object", - "properties": { - "username": { - "type": "string", - "minLength": 3, - "pattern": "^\\w[\\w\\.]*$" - }, - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string", - "minLength": 8, - "maxLength": 72 - }, - "oldPassword": { - "type": "string", - "description": "Required when changing password" - }, - "passwordConfirm": { - "type": "string", - "description": "Must match password" - } - } - }, - "Tag": { - "type": "object", - "required": [ - "id", - "name" - ], - "properties": { - "id": { - "type": "string", - "description": "Tag ID (15 chars)" - }, - "name": { - "type": "string", - "description": "Tag name" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "TagInput": { - "type": "object", - "required": [ - "name" - ], - "properties": { - "name": { - "type": "string", - "description": "Tag name" - } - } - }, - "TagUpdateInput": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Tag name" - } - } - }, - "Trail": { - "type": "object", - "required": [ - "id", - "name", - "author", - "public" - ], - "properties": { - "id": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "name": { - "type": "string", - "description": "Trail name" - }, - "description": { - "type": "string" - }, - "location": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID" - }, - "date": { - "type": "string", - "format": "date" - }, - "public": { - "type": "boolean" - }, - "difficulty": { - "type": "string", - "enum": [ - "easy", - "moderate", - "difficult" - ] - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance": { - "type": "number", - "description": "Distance in meters" - }, - "elevation_gain": { - "type": "number", - "description": "Elevation gain in meters" - }, - "elevation_loss": { - "type": "number", - "description": "Elevation loss in meters" - }, - "duration": { - "type": "number", - "description": "Duration in seconds" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "thumbnail": { - "type": "integer", - "description": "Index of thumbnail photo" - }, - "like_count": { - "type": "integer", - "default": 0 - }, - "category": { - "type": "string", - "description": "Category ID (15 chars)" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "gpx": { - "type": "string", - "description": "GPX file path" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "TrailCreateInput": { - "type": "object", - "required": [ - "name", - "author", - "public" - ], - "properties": { - "id": { - "type": "string", - "description": "Optional custom ID (15 chars)" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "description": { - "type": "string" - }, - "location": { - "type": "string" - }, - "date": { - "type": "string", - "format": "date" - }, - "public": { - "type": "boolean" - }, - "difficulty": { - "type": "string", - "enum": [ - "easy", - "moderate", - "difficult" - ] - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "thumbnail": { - "type": "integer" - }, - "like_count": { - "type": "integer", - "default": 0 - }, - "category": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "gpx": { - "type": "string" - } - } - }, - "TrailUpdateInput": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "location": { - "type": "string" - }, - "date": { - "type": "string", - "format": "date" - }, - "public": { - "type": "boolean" - }, - "difficulty": { - "type": "string", - "enum": [ - "easy", - "moderate", - "difficult" - ] - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "photos-": { - "type": "string", - "description": "Remove photo by name" - }, - "photos+": { - "type": "string", - "description": "Add photo by name" - }, - "thumbnail": { - "type": "integer" - }, - "like_count": { - "type": "integer", - "default": 0 - }, - "category": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "gpx": { - "type": "string" - } - } - }, - "Comment": { - "type": "object", - "required": [ - "id", - "text", - "author", - "trail" - ], - "properties": { - "id": { - "type": "string", - "description": "Comment ID (15 chars)" - }, - "text": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "CommentInput": { - "type": "object", - "required": [ - "text", - "author", - "trail" - ], - "properties": { - "text": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - } - } - }, - "CommentUpdateInput": { - "type": "object", - "properties": { - "text": { - "type": "string" - } - } - }, - "SummitLog": { - "type": "object", - "required": [ - "id", - "date", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Summit log ID (15 chars)" - }, - "date": { - "type": "string", - "format": "date" - }, - "text": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Associated trail ID" - }, - "gpx": { - "type": "string" - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "SummitLogInput": { - "type": "object", - "required": [ - "date", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Optional custom ID (15 chars)" - }, - "date": { - "type": "string", - "format": "date" - }, - "text": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "gpx": { - "type": "string" - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - } - } - }, - "SummitLogUpdateInput": { - "type": "object", - "properties": { - "date": { - "type": "string", - "format": "date" - }, - "text": { - "type": "string" - }, - "gpx": { - "type": "string" - }, - "distance": { - "type": "number" - }, - "elevation_gain": { - "type": "number" - }, - "elevation_loss": { - "type": "number" - }, - "duration": { - "type": "number" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "photos-": { - "type": "string", - "description": "Remove photo by name" - }, - "photos+": { - "type": "string", - "description": "Add photo by name" - } - } - }, - "Waypoint": { - "type": "object", - "required": [ - "id", - "lat", - "lon", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Waypoint ID (15 chars)" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance_from_start": { - "type": "number", - "description": "Distance from trail start in meters" - }, - "icon": { - "type": "string", - "description": "Icon identifier" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "WaypointInput": { - "type": "object", - "required": [ - "lat", - "lon", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Optional custom ID (15 chars)" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance_from_start": { - "type": "number" - }, - "icon": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID (15 chars)" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "trail": { - "type": "string" - } - } - }, - "WaypointUpdateInput": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "lat": { - "type": "number", - "minimum": -90, - "maximum": 90 - }, - "lon": { - "type": "number", - "minimum": -180, - "maximum": 180 - }, - "distance_from_start": { - "type": "number" - }, - "icon": { - "type": "string" - }, - "photos": { - "type": "array", - "items": { - "type": "string" - } - }, - "photos-": { - "type": "string", - "description": "Remove photo by name" - }, - "photos+": { - "type": "string", - "description": "Add photo by name" - } - } - }, - "List": { - "type": "object", - "required": [ - "id", - "name", - "author", - "public" - ], - "properties": { - "id": { - "type": "string", - "description": "List ID (15 chars)" - }, - "name": { - "type": "string" - }, - "public": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "author": { - "type": "string", - "description": "Author user ID" - }, - "trails": { - "type": "array", - "items": { - "type": "string", - "description": "Trail IDs (15 chars)" - } - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "ListInput": { - "type": "object", - "required": [ - "name", - "public", - "trails", - "author" - ], - "properties": { - "id": { - "type": "string", - "description": "Optional custom ID (15 chars)" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "public": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "trails": { - "type": "array", - "items": { - "type": "string", - "description": "Trail ID (15 chars)" - } - }, - "author": { - "type": "string" - } - } - }, - "ListUpdateInput": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "public": { - "type": "boolean" - }, - "description": { - "type": "string" - }, - "trails": { - "type": "array", - "items": { - "type": "string" - } - }, - "trails-": { - "type": "string", - "description": "Remove trail by ID" - }, - "trails+": { - "type": "string", - "description": "Add trail by ID" - } - } - }, - "TrailShare": { - "type": "object", - "required": [ - "id", - "actor", - "trail", - "permission" - ], - "properties": { - "id": { - "type": "string", - "description": "Share ID (15 chars)" - }, - "actor": { - "type": "string", - "format": "uri", - "description": "ActivityPub actor IRI" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "TrailShareInput": { - "type": "object", - "required": [ - "actor", - "trail", - "permission" - ], - "properties": { - "actor": { - "type": "string", - "format": "uri", - "description": "ActivityPub actor IRI" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "TrailShareUpdateInput": { - "type": "object", - "required": [ - "permission" - ], - "properties": { - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "ListShare": { - "type": "object", - "required": [ - "id", - "actor", - "list", - "permission" - ], - "properties": { - "id": { - "type": "string", - "description": "Share ID (15 chars)" - }, - "actor": { - "type": "string", - "format": "uri", - "description": "ActivityPub actor IRI" - }, - "list": { - "type": "string", - "description": "List ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "ListShareInput": { - "type": "object", - "required": [ - "actor", - "list", - "permission" - ], - "properties": { - "actor": { - "type": "string", - "format": "uri", - "description": "ActivityPub actor IRI" - }, - "list": { - "type": "string", - "description": "List ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "ListShareUpdateInput": { - "type": "object", - "required": [ - "permission" - ], - "properties": { - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "TrailLinkShare": { - "type": "object", - "required": [ - "id", - "trail", - "permission" - ], - "properties": { - "id": { - "type": "string", - "description": "Share ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "TrailLinkShareInput": { - "type": "object", - "required": [ - "trail", - "permission" - ], - "properties": { - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "TrailLinkShareUpdateInput": { - "type": "object", - "required": [ - "permission" - ], - "properties": { - "permission": { - "type": "string", - "enum": [ - "view", - "edit" - ] - } - } - }, - "TrailLike": { - "type": "object", - "required": [ - "id", - "actor", - "trail" - ], - "properties": { - "id": { - "type": "string", - "description": "Like ID (15 chars)" - }, - "actor": { - "type": "string", - "description": "User ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - }, - "created": { - "type": "string", - "format": "date-time" - } - } - }, - "TrailLikeInput": { - "type": "object", - "required": [ - "actor", - "trail" - ], - "properties": { - "actor": { - "type": "string", - "description": "User ID (15 chars)" - }, - "trail": { - "type": "string", - "description": "Trail ID (15 chars)" - } - } - }, - "Follow": { - "type": "object", - "required": [ - "id", - "follower", - "followee" - ], - "properties": { - "id": { - "type": "string", - "description": "Follow ID (15 chars)" - }, - "follower": { - "type": "string", - "description": "Follower user ID" - }, - "followee": { - "type": "string", - "description": "Followee user ID (15 chars)" - }, - "created": { - "type": "string", - "format": "date-time" - } - } - }, - "FollowInput": { - "type": "object", - "required": [ - "followee" - ], - "properties": { - "followee": { - "type": "string", - "description": "Followee user ID (15 chars)" - } - } - }, - "Integration": { - "type": "object", - "required": [ - "id", - "user" - ], - "properties": { - "id": { - "type": "string", - "description": "Integration ID (15 chars)" - }, - "user": { - "type": "string", - "description": "User ID (15 chars)" - }, - "strava": { - "type": "object", - "properties": { - "clientId": { - "type": "integer" - }, - "clientSecret": { - "type": "string" - }, - "routes": { - "type": "boolean" - }, - "activities": { - "type": "boolean" - }, - "active": { - "type": "boolean" - }, - "after": { - "type": "string", - "format": "date" - }, - "privacy": { - "type": "string", - "enum": [ - "original", - "settings" - ] - } - } - }, - "komoot": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string" - }, - "completed": { - "type": "boolean" - }, - "planned": { - "type": "boolean" - }, - "active": { - "type": "boolean" - }, - "privacy": { - "type": "string", - "enum": [ - "original", - "settings" - ] - } - } - }, - "hammerhead": { - "type": "object", - "properties": { - "email": { - "type": "string", - "format": "email" - }, - "password": { - "type": "string" - }, - "completed": { - "type": "boolean" - }, - "planned": { - "type": "boolean" - }, - "active": { - "type": "boolean" - }, - "after": { - "type": "string", - "format": "date" - } - } - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "IntegrationInput": { - "type": "object", - "required": [ - "user" - ], - "properties": { - "user": { - "type": "string", - "description": "User ID (15 chars)" - }, - "strava": { - "type": "object" - }, - "komoot": { - "type": "object" - }, - "hammerhead": { - "type": "object" - } - } - }, - "IntegrationUpdateInput": { - "type": "object", - "properties": { - "strava": { - "type": "object", - "nullable": true - }, - "komoot": { - "type": "object", - "nullable": true - }, - "hammerhead": { - "type": "object", - "nullable": true - } - } - }, - "Notification": { - "type": "object", - "required": [ - "id", - "user", - "type", - "seen" - ], - "properties": { - "id": { - "type": "string", - "description": "Notification ID (15 chars)" - }, - "user": { - "type": "string", - "description": "User ID (15 chars)" - }, - "type": { - "type": "string", - "description": "Notification type" - }, - "seen": { - "type": "boolean", - "default": false - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "NotificationUpdateInput": { - "type": "object", - "required": [ - "seen" - ], - "properties": { - "seen": { - "type": "boolean", - "const": true, - "description": "Mark as read" - } - } - }, - "Settings": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "type": "string", - "description": "Settings ID (15 chars)" - }, - "unit": { - "type": "string", - "enum": [ - "metric", - "imperial" - ] - }, - "language": { - "type": "string", - "description": "Language code" - }, - "bio": { - "type": "string", - "nullable": true - }, - "mapFocus": { - "type": "string", - "enum": [ - "trails", - "location" - ] - }, - "location": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "lat": { - "type": "number" - }, - "lon": { - "type": "number" - } - }, - "nullable": true - }, - "category": { - "type": "string" - }, - "tilesets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - } - } - }, - "nullable": true - }, - "terrain": { - "type": "object", - "properties": { - "terrain": { - "type": "string", - "format": "uri" - }, - "hillshading": { - "type": "string", - "format": "uri" - } - }, - "nullable": true - }, - "user": { - "type": "string" - }, - "privacy": { - "type": "object", - "properties": { - "account": { - "type": "string", - "enum": [ - "public", - "private" - ] - }, - "trails": { - "type": "string", - "enum": [ - "public", - "private" - ] - }, - "lists": { - "type": "string", - "enum": [ - "public", - "private" - ] - } - }, - "nullable": true - }, - "created": { - "type": "string", - "format": "date-time" - }, - "updated": { - "type": "string", - "format": "date-time" - } - } - }, - "SettingsInput": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "metric", - "imperial" - ] - }, - "language": { - "type": "string" - }, - "bio": { - "type": "string", - "nullable": true - }, - "mapFocus": { - "type": "string", - "enum": [ - "trails", - "location" - ] - }, - "location": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "lat": { - "type": "number" - }, - "lon": { - "type": "number" - } - }, - "nullable": true - }, - "category": { - "type": "string" - }, - "tilesets": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - } - } - }, - "nullable": true - }, - "terrain": { - "type": "object", - "properties": { - "terrain": { - "type": "string", - "format": "uri" - }, - "hillshading": { - "type": "string", - "format": "uri" - } - }, - "nullable": true - }, - "user": { - "type": "string" - }, - "privacy": { - "type": "object", - "properties": { - "account": { - "type": "string", - "enum": [ - "public", - "private" - ] - }, - "trails": { - "type": "string", - "enum": [ - "public", - "private" - ] - }, - "lists": { - "type": "string", - "enum": [ - "public", - "private" - ] - } - }, - "nullable": true - } - } - }, - "TrailMergeSettings": { - "type": "object", - "required": [ - "summitLog", - "photos", - "comments", - "delete", - "tags", - "likes" - ], - "properties": { - "summitLog": { - "type": "boolean" - }, - "photos": { - "type": "boolean" - }, - "comments": { - "type": "boolean" - }, - "delete": { - "type": "boolean" - }, - "tags": { - "type": "boolean" - }, - "likes": { - "type": "boolean" - } - } - }, - "TrailMergeSuggestRequest": { - "type": "object", - "required": [ - "mode" - ], - "properties": { - "mode": { - "type": "string", - "enum": [ - "manual-selection", - "auto-discovery", - "maintenance-groups" - ] - }, - "trailIds": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Required for manual-selection" - }, - "sourceTrailId": { - "type": "string", - "description": "Required for auto-discovery" - } - } - }, - "TrailMergeSuggestCandidate": { - "type": "object", - "required": [ - "trailId", - "score", - "reason", - "warnings", - "selectable" - ], - "properties": { - "trailId": { - "type": "string" - }, - "score": { - "type": "number" - }, - "reason": { - "type": "string" - }, - "warnings": { - "type": "array", - "items": { - "type": "string" - } - }, - "selectable": { - "type": "boolean" - } - } - }, - "TrailMergeSuggestResponse": { - "type": "object", - "required": [ - "targetTrailId", - "reason", - "warnings", - "candidates" - ], - "properties": { - "targetTrailId": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "warnings": { - "type": "array", - "items": { - "type": "string" - } - }, - "candidates": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TrailMergeSuggestCandidate" - } - } - } - }, - "TrailMergeSuggestGroup": { - "type": "object", - "required": [ - "groupId", - "trailIds", - "targetTrailId", - "reason", - "score", - "indirect" - ], - "properties": { - "groupId": { - "type": "string" - }, - "trailIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "targetTrailId": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "score": { - "type": "number" - }, - "indirect": { - "type": "boolean" - } - } - }, - "TrailMergeSuggestGroupsResponse": { - "type": "object", - "required": [ - "groups" - ], - "properties": { - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TrailMergeSuggestGroup" - } - } - } - }, - "TrailMergeExecuteRequest": { - "type": "object", - "required": [ - "sourceTrailId", - "targetTrailId", - "settings" - ], - "properties": { - "sourceTrailId": { - "type": "string" - }, - "targetTrailId": { - "type": "string" - }, - "settings": { - "$ref": "#/components/schemas/TrailMergeSettings" - } - } - }, - "TrailMergeExecuteResponse": { - "type": "object", - "required": [ - "acknowledged" - ], - "properties": { - "acknowledged": { - "type": "boolean" - } - } - }, - "ListResult": { - "type": "object", - "properties": { - "page": { - "type": "integer" - }, - "perPage": { - "type": "integer" - }, - "totalItems": { - "type": "integer" - }, - "totalPages": { - "type": "integer" - }, - "items": { - "type": "array", - "items": { - "type": "object" - } - } - } - }, - "Error": { - "type": "object", - "required": [ - "message" - ], - "properties": { - "message": { - "type": "string" - }, - "code": { - "type": "integer" - }, - "data": { - "type": "object" - } - } - } - } - } -} \ No newline at end of file diff --git a/web/vite.config.ts b/web/vite.config.ts index 3a7c4132..d33ebc60 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -4,20 +4,12 @@ import tailwindcss from '@tailwindcss/vite'; import fs from 'fs'; import openapiPlugin from 'sveltekit-openapi-generator'; import { defineConfig } from 'vitest/config'; +import { openapiOptions } from './openapi.config.js'; const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); export default defineConfig({ - plugins: [enhancedImages(), openapiPlugin({ - info: { - title: 'Wanderer API', - version: `${packageJson.version}`, - description: 'API documentation for wanderer backend', - }, - outputPath: 'static/docs/api/wanderer.openapi.json', - include: ['src/routes/api/v1/**/*.{js,ts}'], - baseSchemasPath: 'src/lib/models/api/openapi_schemas.ts', - }), tailwindcss(), sveltekit()], + plugins: [enhancedImages(), openapiPlugin(openapiOptions(packageJson.version)), tailwindcss(), sveltekit()], test: { include: ['src/**/*.{test,spec}.{js,ts}'] }, ssr: { noExternal: ['three'] }, ...(process.env.WANDERER_ENV == "dev" ? {