Files
links/static/openapi.yaml
2026-03-22 16:15:53 +11:00

1093 lines
31 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
openapi: 3.0.3
info:
title: GoLinks API
description: |
# GoLinks — Link & Content Management API
GoLinks is a self-hosted link management system. It lets you create short links,
bookmark pages with auto-extracted metadata, manage image collections, upload files,
write blog posts, and serve random images from a personal library.
## Key concepts
| Resource | What it is |
|---|---|
| **Page** | A bookmarked URL. When created, the server asynchronously fetches its title, description, and a screenshot. |
| **Post** | A Markdown blog post, optionally tagged. |
| **Collection** | A named group of uploaded images (e.g. a photo album). |
| **Image** | An image file belonging to a Collection. |
| **File** | A generic uploaded file. Files can be made public via a shareable token URL. |
| **Random image** | Serves a random image from the server's local image library, resized on the fly. Great for placeholder images. |
## Common patterns
### Pagination
All list endpoints accept `?page=` (1-based) and `?page_size=` query parameters.
Responses follow the envelope `{ count, next, previous, results[] }`.
### Image embedding
Use the random image endpoint as a live placeholder:
```
![placeholder](https://your-domain/api/images/random/400/300/?fit=crop&v=1234567890)
```
Or import and self-host an external image:
```
![photo](https://your-domain/import/images/example.com/path/photo.jpg)
```
### File sharing
Upload a file, call `POST /api/files/{id}/toggle-public` to get a public token,
then share `/public/files/{token}/` — no authentication required for the recipient.
## Authentication
No authentication is currently required on any endpoint.
version: 1.0.0
x-logo:
url: /static/images/logo.png
servers:
- url: http://localhost:8000
description: Local development server
tags:
- name: Links
description: Short links with click tracking. Supports listing all links and retrieving the most visited ones sorted by click count.
- name: Pages
description: |
Bookmarked web pages. Creating a page triggers an async background job that fetches
the page title, description, and screenshot automatically.
- name: Posts
description: Markdown blog posts with optional tag categorisation.
- name: Collections
description: Named groups of images. Each collection can hold many uploaded image files.
- name: Images
description: |
Image management and random image serving.
The random image endpoint is useful as a live placeholder — pass `?v={timestamp}`
to bust the browser cache on each render.
- name: Files
description: |
Generic file uploads with optional public sharing via tokenised URLs.
Files can be made public/private and given an expiry date.
paths:
/api/links:
get:
operationId: listLinks
tags: [Links]
summary: List all links
description: Retrieve a paginated list of all short links ordered by creation date (newest first).
parameters:
- in: query
name: page
schema:
type: integer
default: 1
description: Page number (1-based).
- in: query
name: page_size
schema:
type: integer
default: 10
maximum: 100
description: Number of results per page.
responses:
'200':
description: Paginated list of links.
content:
application/json:
schema:
$ref: '#/components/schemas/LinkList'
/api/links/most-visited:
get:
operationId: listMostVisitedLinks
tags: [Links]
summary: Most visited links
description: Return links that have at least one click, sorted by click count descending.
parameters:
- in: query
name: page
schema:
type: integer
default: 1
description: Page number (1-based).
- in: query
name: page_size
schema:
type: integer
default: 10
maximum: 100
description: Number of results per page.
responses:
'200':
description: Paginated list of links sorted by click_count descending.
content:
application/json:
schema:
$ref: '#/components/schemas/LinkList'
/api/pages:
get:
operationId: listPages
tags: [Pages]
summary: List all pages
description: Retrieve a paginated list of all bookmarked pages.
parameters:
- in: query
name: page
schema:
type: integer
description: Page number for pagination
- in: query
name: page_size
schema:
type: integer
description: Number of items per page
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/PageList'
post:
operationId: createPage
tags: [Pages]
summary: Create a new page
description: |
Bookmark a URL. After creation the server starts an async background job that fetches
the page title, description, content, and a screenshot.
Poll `GET /api/pages` or check `processing_status` to see when it completes.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PageCreate'
responses:
'201':
description: Page created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Page'
/api/posts:
get:
operationId: listPosts
tags: [Posts]
summary: List all posts
description: Retrieve a paginated list of all Markdown blog posts.
parameters:
- in: query
name: page
schema:
type: integer
description: Page number for pagination
- in: query
name: page_size
schema:
type: integer
description: Number of items per page
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/PostList'
post:
operationId: createPost
tags: [Posts]
summary: Create a new post
description: Create a Markdown blog post. Pass tag slugs in the `tags` array to categorise it.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PostCreate'
responses:
'201':
description: Post created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Post'
/api/collections:
get:
operationId: listCollections
tags: [Collections]
summary: List all collections
description: Retrieve a paginated list of all image collections.
parameters:
- in: query
name: page
schema:
type: integer
description: Page number for pagination
- in: query
name: page_size
schema:
type: integer
description: Number of items per page
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/CollectionList'
post:
operationId: createCollection
tags: [Collections]
summary: Create a new collection
description: Create a new named image collection.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CollectionCreate'
responses:
'201':
description: Collection created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/CollectionResponse'
/api/collections/{collection_id}:
delete:
operationId: deleteCollection
tags: [Collections]
summary: Delete a collection
description: Permanently delete a collection and all its images.
parameters:
- in: path
name: collection_id
required: true
schema:
type: string
format: uuid
description: The ID of the collection to delete
responses:
'200':
description: Collection deleted successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'
/api/collections/{collection_id}/upload_images:
post:
operationId: uploadImagesToCollection
tags: [Collections]
summary: Upload images to a collection
description: |
Upload one or more images to a collection. Only `image/*` content types are accepted.
Optionally provide a parallel `descriptions[]` array — each entry maps to the image at the same index.
parameters:
- in: path
name: collection_id
required: true
schema:
type: string
format: uuid
description: The ID of the collection to upload images to
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: array
items:
type: string
format: binary
description: List of image files to upload. Only image/* content types are allowed.
descriptions:
type: array
items:
type: string
description: Optional list of descriptions for the uploaded images. Each description corresponds to the image at the same index.
required:
- file
responses:
'201':
description: Images uploaded successfully
content:
application/json:
schema:
type: object
properties:
status:
type: string
enum: [success]
example: success
message:
type: string
example: "Successfully uploaded 2 images"
data:
type: array
items:
$ref: '#/components/schemas/Image'
/api/images/{image_id}:
delete:
operationId: deleteImage
tags: [Images]
summary: Delete an image
description: Permanently delete a single image from its collection.
parameters:
- in: path
name: image_id
required: true
schema:
type: string
format: uuid
description: The ID of the image to delete
responses:
'200':
description: Image deleted successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'
/api/files:
get:
operationId: listFiles
tags: [Files]
summary: List all uploaded files
description: Retrieve a paginated list of all uploaded files.
parameters:
- in: query
name: page
schema:
type: integer
description: Page number for pagination
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/FileList'
post:
operationId: uploadFiles
tags: [Files]
summary: Upload one or more files
description: |
Upload one or multiple files via `multipart/form-data`. Files are stored server-side
in the configured `FILE_UPLOADS_FOLDER`. Returns an array of `FileUpload` objects.
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- files
properties:
files:
type: array
items:
type: string
format: binary
description: One or more files to upload
responses:
'201':
description: Files uploaded successfully
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/FileUpload'
'400':
description: No files provided
/api/files/{id}:
get:
operationId: getFile
tags: [Files]
summary: Get file details
description: Fetch metadata for a single file by its UUID.
parameters:
- in: path
name: id
required: true
schema:
type: string
format: uuid
responses:
'200':
description: File details
content:
application/json:
schema:
$ref: '#/components/schemas/FileUpload'
'404':
description: File not found
delete:
operationId: deleteFile
tags: [Files]
summary: Delete a file
description: Permanently delete the file record and remove it from disk.
parameters:
- in: path
name: id
required: true
schema:
type: string
format: uuid
responses:
'204':
description: File deleted
'404':
description: File not found
/api/files/{id}/download:
get:
operationId: downloadFile
tags: [Files]
summary: Download or view a file
description: |
Serve the raw file bytes. Images are returned with `Content-Disposition: inline`
(viewable in browser); all other files use `attachment` (triggers download).
parameters:
- in: path
name: id
required: true
schema:
type: string
format: uuid
responses:
'200':
description: File content
'404':
description: File not found
/api/files/{id}/toggle-public:
post:
operationId: toggleFilePublic
tags: [Files]
summary: Toggle public/private visibility
description: |
Toggle a file between public and private:
- **Private → Public**: generates a `public_token` and returns a `public_url`
(`/public/files/{token}/`) that anyone can access without authentication.
- **Public → Private**: immediately revokes the token; existing `public_url` links stop working.
parameters:
- in: path
name: id
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Updated visibility state
content:
application/json:
schema:
type: object
properties:
is_public:
type: boolean
public_token:
type: string
nullable: true
public_url:
type: string
nullable: true
example: /public/files/<token>/
/api/files/{id}/set-expiry:
post:
operationId: setFileExpiry
tags: [Files]
summary: Set or clear expiry date for a public file
description: |
Set an expiry datetime for a public file. After the expiry the `public_url` returns 404.
Send `expires_at: null` (or omit the field) to remove the expiry and make the link permanent.
parameters:
- in: path
name: id
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
expires_at:
type: string
format: date-time
nullable: true
description: ISO 8601 datetime. Send null or omit to clear the expiry.
responses:
'200':
description: Updated file details
content:
application/json:
schema:
$ref: '#/components/schemas/FileUpload'
/public/files/{token}/:
get:
operationId: getPublicFile
tags: [Files]
summary: Access a public file by token
description: |
Unauthenticated endpoint. Serves a publicly shared file by its opaque token.
Returns 404 if the file has been made private or if the expiry date has passed.
parameters:
- in: path
name: token
required: true
schema:
type: string
responses:
'200':
description: File content (inline for images, attachment for others)
'404':
description: File not found, is private, or link has expired
/api/images/random/:
get:
operationId: getRandomImage
tags: [Images]
summary: Get a random image
description: |
Returns a randomly selected image from the server's image library, resized to the
default dimensions. Each request returns a different image.
**Cache busting:** Add a `?v={timestamp}` query parameter to prevent browsers from
caching repeated calls (the server ignores this parameter).
parameters:
- in: query
name: fit
schema:
type: string
enum: [clip, crop, fill, scale]
default: scale
description: |
How to fit the image into the requested dimensions.
- `scale` — scale to fit, maintaining aspect ratio (may add letterbox)
- `crop` — crop to exact dimensions
- `clip` — clip to fit, no upscaling
- `fill` — fill exact dimensions, may distort
- in: query
name: v
schema:
type: integer
description: Cache-busting value (e.g. Unix timestamp). Ignored by the server.
example: 1774152070
responses:
'200':
description: Image file (JPEG or PNG)
headers:
Cache-Control:
schema:
type: string
description: Caching directives (e.g. `public, max-age=3600`)
X-Image-Source:
schema:
type: string
description: Relative path of the source image that was served
content:
image/jpeg:
schema:
type: string
format: binary
image/png:
schema:
type: string
format: binary
'404':
description: |
No images available, folder not configured, or invalid parameters.
/api/images/random/{width}/{height}/:
get:
operationId: getRandomImageSized
tags: [Images]
summary: Get a random image at specific dimensions
description: |
Returns a randomly selected image resized to `{width}×{height}` pixels.
Minimum 300 px and maximum 4096 px for each dimension.
**Cache busting:** Add a `?v={timestamp}` query parameter to prevent browsers from
caching repeated calls (the server ignores this parameter).
**Use in Markdown / posts:**
```
![Random image](http://your-domain/api/images/random/300/450/?fit=crop&v=1234567890)
```
parameters:
- in: path
name: width
required: true
schema:
type: integer
minimum: 300
maximum: 4096
description: Target width in pixels
example: 300
- in: path
name: height
required: true
schema:
type: integer
minimum: 300
maximum: 4096
description: Target height in pixels
example: 450
- in: query
name: fit
schema:
type: string
enum: [clip, crop, fill, scale]
default: scale
description: |
How to fit the image into the requested dimensions.
- `scale` — scale to fit, maintaining aspect ratio (may add letterbox)
- `crop` — crop to exact dimensions
- `clip` — clip to fit, no upscaling
- `fill` — fill exact dimensions, may distort
- in: query
name: v
schema:
type: integer
description: Cache-busting value (e.g. Unix timestamp). Ignored by the server.
example: 1774152070
responses:
'200':
description: Image file (JPEG or PNG) at the requested dimensions
headers:
Cache-Control:
schema:
type: string
X-Image-Source:
schema:
type: string
description: Relative path of the source image that was served
content:
image/jpeg:
schema:
type: string
format: binary
image/png:
schema:
type: string
format: binary
'404':
description: |
No images available, folder not configured, invalid dimensions, or invalid fit mode.
/import/images/{image_url}:
get:
operationId: importExternalImage
tags: [Files]
summary: Import and cache an external image
description: |
Imports an external image by URL and saves it to the local file store.
The `image_url` path parameter is the image URL **without** the `https://` scheme prefix
(e.g. `example.com/path/to/image.jpg`). The server always tries `https://` first.
**Worker mode** — this endpoint is non-blocking:
- On the **first request** for a given URL the server creates a `FileUpload` record
(marked `is_public: true`) and kicks off a background thread to download and save the file.
The response immediately redirects (HTTP 302) to the original `https://` source URL so the
image is visible right away.
- On **subsequent requests**, once the background download has completed, the response
redirects to the locally-saved public file URL (`/public/files/{uuid}-{filename}`) so the
original host is no longer needed.
**Idempotent** — the same external URL always resolves to the same `FileUpload` record;
the file is only downloaded once.
**Use in Markdown / posts:**
```
![Alt text](http://your-domain/import/images/example.com/path/to/image.jpg)
```
parameters:
- in: path
name: image_url
required: true
schema:
type: string
description: |
External image URL without the scheme prefix.
Example: `graziamagazine.com/wp-content/uploads/2024/12/Elle-Fanning-Pigtails-scaled.jpg`
example: graziamagazine.com/wp-content/uploads/2024/12/Elle-Fanning-Pigtails-scaled.jpg
responses:
'302':
description: |
Redirect to either the original source URL (while background download is in progress)
or the locally-saved public file URL (once download has completed).
headers:
Location:
schema:
type: string
description: URL to redirect to (original source or local `/public/files/…`)
components:
schemas:
Page:
type: object
properties:
id:
type: integer
url:
type: string
title:
type: string
summary:
type: string
content:
type: string
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
PageCreate:
type: object
required:
- url
- title
properties:
url:
type: string
title:
type: string
summary:
type: string
content:
type: string
PageList:
type: object
properties:
count:
type: integer
next:
type: string
nullable: true
previous:
type: string
nullable: true
results:
type: array
items:
$ref: '#/components/schemas/Page'
Post:
type: object
properties:
id:
type: integer
title:
type: string
content:
type: string
tag_details:
type: array
items:
$ref: '#/components/schemas/Tag'
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
PostCreate:
type: object
required:
- title
- content
properties:
title:
type: string
content:
type: string
tags:
type: array
items:
type: string
description: List of tag slugs to associate with the post
PostList:
type: object
properties:
count:
type: integer
next:
type: string
nullable: true
previous:
type: string
nullable: true
results:
type: array
items:
$ref: '#/components/schemas/Post'
Tag:
type: object
properties:
id:
type: integer
name:
type: string
slug:
type: string
description:
type: string
Collection:
type: object
properties:
id:
type: string
format: uuid
name:
type: string
description:
type: string
image_count:
type: integer
created_at:
type: string
format: date-time
CollectionCreate:
type: object
required:
- name
properties:
name:
type: string
description:
type: string
CollectionList:
type: object
properties:
count:
type: integer
next:
type: string
nullable: true
previous:
type: string
nullable: true
results:
type: array
items:
$ref: '#/components/schemas/Collection'
CollectionResponse:
type: object
properties:
status:
type: string
enum: [success]
message:
type: string
data:
$ref: '#/components/schemas/Collection'
Image:
type: object
properties:
id:
type: string
format: uuid
collection:
type: string
format: uuid
title:
type: string
description:
type: string
content_type:
type: string
size:
type: integer
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
url:
type: string
FileUpload:
type: object
properties:
id:
type: string
format: uuid
readOnly: true
name:
type: string
description: Original filename
mime_type:
type: string
readOnly: true
size:
type: integer
readOnly: true
description: File size in bytes
formatted_size:
type: string
readOnly: true
description: Human-readable size (e.g. "1.4 MB")
is_public:
type: boolean
public_token:
type: string
nullable: true
readOnly: true
public_url:
type: string
nullable: true
readOnly: true
example: /public/files/<token>/
expires_at:
type: string
format: date-time
nullable: true
is_expired:
type: boolean
readOnly: true
download_count:
type: integer
readOnly: true
source_url:
type: string
format: uri
nullable: true
description: |
Original external URL this file was imported from via `/import/images/…`.
`null` for files uploaded directly.
example: https://example.com/path/to/image.jpg
created_at:
type: string
format: date-time
readOnly: true
updated_at:
type: string
format: date-time
readOnly: true
FileList:
type: object
properties:
count:
type: integer
next:
type: string
nullable: true
previous:
type: string
nullable: true
results:
type: array
items:
$ref: '#/components/schemas/FileUpload'
SuccessResponse:
type: object
properties:
status:
type: string
enum: [success]
message:
type: string
TagSummary:
type: object
properties:
id:
type: integer
name:
type: string
slug:
type: string
Link:
type: object
properties:
id:
type: integer
readOnly: true
alias:
type: string
description: Unique slug used as the short-link identifier (e.g. /gh → github.com).
original_url:
type: string
description: Target URL. May contain template parameters like `{param,default=value}`.
description:
type: string
nullable: true
link_type:
type: string
enum: [LINK, CUSTOM]
description: LINK redirects to original_url; CUSTOM renders Markdown content.
click_count:
type: integer
readOnly: true
description: Number of times the short link has been visited.
tags:
type: array
readOnly: true
items:
$ref: '#/components/schemas/TagSummary'
created_at:
type: string
format: date-time
readOnly: true
updated_at:
type: string
format: date-time
readOnly: true
LinkList:
type: object
properties:
count:
type: integer
next:
type: string
nullable: true
previous:
type: string
nullable: true
results:
type: array
items:
$ref: '#/components/schemas/Link'