You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

480 lines
14 KiB
YAML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

openapi: "3.1.0"
info:
title: QuotesDB API
description: A simple quotes database with passphrase-based quote ownership.
version: "0.1.0"
license:
name: MIT OR Apache-2.0
servers:
- url: http://localhost:8787
description: Local development
- url: https://api.quotesdb.example.com
description: Production (Cloudflare Workers)
# ---------------------------------------------------------------------------
# Security
# ---------------------------------------------------------------------------
# Auth is per-quote: each quote has a 4-word passphrase (auth_code) that was
# returned at creation time. Callers supply it via the X-Auth-Code header for
# mutating operations (POST /:id, DELETE /:id).
components:
securitySchemes:
AuthCode:
type: apiKey
in: header
name: X-Auth-Code
description: >
4-word passphrase returned when the quote was created
(e.g. ocean-table-purple-storm). Required for update and delete.
# -------------------------------------------------------------------------
# Schemas
# -------------------------------------------------------------------------
schemas:
# Returned for all GET responses — auth_code intentionally omitted.
Quote:
type: object
required:
- id
- text
- author
- tags
- created_at
- updated_at
properties:
id:
type: string
description: NanoID (~21 characters).
example: "V1StGXR8_Z5jdHi6B-myT"
text:
type: string
description: The quote text.
example: "The only way to do great work is to love what you do."
author:
type: string
description: The person attributed with the quote.
example: "Steve Jobs"
source:
type: ["string", "null"]
description: Optional source (book, speech, etc.).
example: "Stanford Commencement Address, 2005"
date:
type: ["string", "null"]
format: date
description: Optional ISO 8601 date (YYYY-MM-DD) associated with the quote.
example: "2005-06-12"
tags:
type: array
items:
type: string
description: Zero or more tags attached to the quote.
example: ["work", "inspiration"]
created_at:
type: string
format: date-time
description: When the quote was first stored (UTC).
updated_at:
type: string
format: date-time
description: When the quote was last modified (UTC).
# Returned only from the create (PUT) endpoint — includes auth_code.
QuoteCreated:
allOf:
- $ref: "#/components/schemas/Quote"
- type: object
required:
- auth_code
properties:
auth_code:
type: string
description: >
4-word passphrase that authorises future edits and deletes.
Store this — it cannot be recovered later.
example: "ocean-table-purple-storm"
# Request body for PUT /api/quotes (create).
QuoteCreateRequest:
type: object
required:
- text
- author
properties:
text:
type: string
description: The quote text.
author:
type: string
description: The person attributed with the quote.
source:
type: string
description: Optional source (book, speech, etc.).
date:
type: string
format: date
description: Optional ISO 8601 date (YYYY-MM-DD).
tags:
type: array
items:
type: string
description: Zero or more tags.
default: []
auth_code:
type: string
description: >
Optional custom auth code. If omitted, a 4-word passphrase is
auto-generated by the server and returned in the response.
cf_turnstile_token:
type: string
description: Cloudflare Turnstile CAPTCHA token. Required when the server has TURNSTILE_SECRET_KEY configured.
nullable: true
# Request body for POST /api/quotes/:id (update — all fields optional).
QuoteUpdateRequest:
type: object
properties:
text:
type: string
description: Replacement quote text.
author:
type: string
description: Replacement author name.
source:
type: ["string", "null"]
description: Replacement source. Pass null to clear.
date:
type: ["string", "null"]
format: date
description: Replacement date. Pass null to clear.
tags:
type: array
items:
type: string
description: Replacement tag list. Replaces all existing tags.
# Paginated list of quotes.
QuoteList:
type: object
required:
- quotes
- page
- total_pages
- total_count
properties:
quotes:
type: array
items:
$ref: "#/components/schemas/Quote"
page:
type: integer
minimum: 1
description: Current page number.
total_pages:
type: integer
description: Total number of pages given the current filters.
total_count:
type: integer
description: Total number of quotes matching the current filters.
# Response body for GET /api/status.
StatusResponse:
type: object
required:
- submissions_locked
properties:
submissions_locked:
type: boolean
description: Whether new quote submissions are currently disabled.
example: false
# Standard error envelope used by all error responses.
Error:
type: object
required:
- error
properties:
error:
type: string
description: Human-readable error message.
example: "quote not found"
# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------
# IMPORTANT — router registration order for the Rust implementation:
# GET /api/quotes/random must be registered BEFORE GET /api/quotes/{id}
# to prevent "random" being matched as an id parameter.
paths:
/api/:
get:
operationId: getOpenApiSpec
summary: OpenAPI specification
description: Returns this OpenAPI specification as JSON.
tags: [meta]
responses:
"200":
description: The OpenAPI spec in JSON format.
content:
application/json:
schema:
type: object
description: Raw OpenAPI 3.1 document.
/api/status:
get:
operationId: getStatus
summary: API status
description: >
Returns the current operational status of the API. Callers can check
submissions_locked to determine whether the PUT /api/quotes endpoint
is accepting new submissions.
tags: [meta]
responses:
"200":
description: Current API status.
content:
application/json:
schema:
$ref: "#/components/schemas/StatusResponse"
"500":
description: Internal server error.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/api/quotes:
get:
operationId: listQuotes
summary: List quotes
description: Returns a paginated list of quotes, optionally filtered by author or tag.
tags: [quotes]
parameters:
- name: page
in: query
description: Page number (1-based).
required: false
schema:
type: integer
minimum: 1
default: 1
- name: author
in: query
description: Filter by exact author name (case-insensitive).
required: false
schema:
type: string
- name: tag
in: query
description: Filter to quotes that have this tag.
required: false
schema:
type: string
- name: date_after_year
in: query
description: Only include quotes dated on or after this year.
required: false
schema:
type: integer
minimum: 0
maximum: 9999
- name: date_after_month
in: query
description: Narrows after-bound to this month (112). Requires date_after_year.
required: false
schema:
type: integer
minimum: 1
maximum: 12
- name: date_after_day
in: query
description: Narrows after-bound to this day (131). Requires date_after_year and date_after_month.
required: false
schema:
type: integer
minimum: 1
maximum: 31
- name: date_before_year
in: query
description: Only include quotes dated on or before this year.
required: false
schema:
type: integer
minimum: 0
maximum: 9999
- name: date_before_month
in: query
description: Narrows before-bound to this month (112). Requires date_before_year.
required: false
schema:
type: integer
minimum: 1
maximum: 12
- name: date_before_day
in: query
description: Narrows before-bound to this day (131). Requires date_before_year and date_before_month.
required: false
schema:
type: integer
minimum: 1
maximum: 31
responses:
"200":
description: Paginated list of quotes.
content:
application/json:
schema:
$ref: "#/components/schemas/QuoteList"
put:
operationId: createQuote
summary: Create a quote
description: >
Creates a new quote. If auth_code is omitted from the request body,
the server auto-generates a 4-word passphrase and returns it in the
response. Store the auth_code — it cannot be recovered later.
tags: [quotes]
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/QuoteCreateRequest"
responses:
"201":
description: Quote created successfully.
content:
application/json:
schema:
$ref: "#/components/schemas/QuoteCreated"
"422":
description: Validation error (e.g. missing required fields).
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
# NOTE: registered before /api/quotes/{id} in the Rust router.
/api/quotes/random:
get:
operationId: getRandomQuote
summary: Random quote
description: Returns a single randomly selected quote.
tags: [quotes]
responses:
"200":
description: A random quote.
content:
application/json:
schema:
$ref: "#/components/schemas/Quote"
"404":
description: No quotes exist in the database.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/api/quotes/{id}:
parameters:
- name: id
in: path
required: true
description: NanoID of the quote (~21 characters).
schema:
type: string
example: "V1StGXR8_Z5jdHi6B-myT"
get:
operationId: getQuote
summary: Get a quote by ID
description: Returns a single quote identified by its NanoID.
tags: [quotes]
responses:
"200":
description: The requested quote.
content:
application/json:
schema:
$ref: "#/components/schemas/Quote"
"404":
description: Quote not found.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
operationId: updateQuote
summary: Update a quote
description: >
Partially updates an existing quote. Only the fields included in the
request body are modified. Requires the quote's auth_code via the
X-Auth-Code header.
tags: [quotes]
security:
- AuthCode: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/QuoteUpdateRequest"
responses:
"200":
description: The updated quote.
content:
application/json:
schema:
$ref: "#/components/schemas/Quote"
"403":
description: Auth code missing or incorrect.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"404":
description: Quote not found.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
delete:
operationId: deleteQuote
summary: Delete a quote
description: >
Permanently deletes a quote. Requires the quote's auth_code via the
X-Auth-Code header.
tags: [quotes]
security:
- AuthCode: []
responses:
"204":
description: Quote deleted successfully. No response body.
"403":
description: Auth code missing or incorrect.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"404":
description: Quote not found.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
# ---------------------------------------------------------------------------
# Tags (for grouping in generated docs)
# ---------------------------------------------------------------------------
tags:
- name: meta
description: API metadata endpoints.
- name: quotes
description: CRUD operations on quotes.