feat: openapi swagger ui

This commit is contained in:
sjdonado
2025-11-02 10:57:02 +01:00
parent 80a59094f9
commit fef015ce53
3 changed files with 529 additions and 149 deletions
+33
View File
@@ -0,0 +1,33 @@
name: Deploy API Documentation
on:
push:
branches:
- master
paths:
- 'docs/openapi.yaml'
- '.github/workflows/deploy-docs.yml'
workflow_dispatch:
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Generate Swagger UI
uses: Legion2/swagger-ui-action@v1
with:
output: swagger-ui
spec-file: docs/openapi.yaml
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: swagger-ui
-149
View File
@@ -1,149 +0,0 @@
# API Reference
1. **Ping the API**
- Endpoint: `GET /api/ping`
- Payload: None
- Response: 200
- Response Example
```json
{
"data": "pong"
}
```
2. **Redirect by Slug**
- Endpoint: `GET /:slug`
- Payload: None
- Response: 301
3. **List All Links**
- Endpoint: `GET /api/links`
- Headers: `X-Api-Key`
- Query Parameters:
- `limit` (optional): Number of results per page (default: 100)
- `cursor` (optional): Pagination cursor from previous response
- Response: 200
- Response Example
```json
{
"data": [
{
"id": "84f0c7a4-8c4e-4665-b676-cb9c5e40f1db",
"refer": "http://localhost:4000/3wP4BQ",
"origin": "https://monocuco.donado.co"
}
],
"pagination": {
"has_more": true,
"next": "75e0a7f4-9c5e-1235-b546-eb9c5e40f7ac"
}
}
```
4. **List link by ID**
- Endpoint: `GET /api/links/:id`
- Headers: `X-Api-Key`
- Payload: None
- Note: This endpoint returns up to 100 of the most recent clicks. For complete click history, use the `/api/links/:id/clicks` endpoint with pagination.
- Response: 200
- Response Example
```json
{
"data": {
"id": "84f0c7a4-8c4e-4665-b676-cb9c5e40f1db",
"refer": "http://localhost:4000/3wP4BQ",
"origin": "https://monocuco.donado.co",
"clicks": [
{
"id": "730e2202-58f9-478c-a24c-f1c561df6716",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:127.0) Gecko/20100101 Firefox/127.0",
"country": "DE",
"browser": "Firefox",
"os": "Mac OS X",
"referer": "Direct",
"created_at": "2024-07-12T19:25:22Z"
}
]
}
}
```
5. **List Clicks for a Link**
- Endpoint: `GET /api/links/:id/clicks`
- Headers: `X-Api-Key`
- Query Parameters:
- `limit` (optional): Number of results per page (default: 100)
- `cursor` (optional): Pagination cursor from previous response
- Response: 200
- Response Example
```json
{
"data": [
{
"id": "730e2202-58f9-478c-a24c-f1c561df6716",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:127.0) Gecko/20100101 Firefox/127.0",
"country": "DE",
"browser": "Firefox",
"os": "Mac OS X",
"referer": "Direct",
"created_at": "2024-07-12T19:25:22Z"
}
],
"pagination": {
"has_more": true,
"next": "629e3301-47f8-389b-b24c-f1c561df9825"
}
}
```
6. **Create new link**
- Endpoint: `POST /api/links`
- Payload:
```json
{
"url": "https://example.com"
}
```
- Headers: `X-Api-Key`
- Response: 201
- Response Example:
```json
{
"data": {
"id": "84f0c7a4-8c4e-4665-b676-cb9c5e40f1db",
"refer": "http://localhost:4000/3wP4BQ",
"origin": "https://example.com",
"clicks": []
}
}
```
7. **Update an existing link by ID**
- Endpoint: `PUT /api/links/:id`
- Payload:
```json
{
"url": "https://newexample.com"
}
```
- Headers: `X-Api-Key`
- Response: 200
- Response Example:
```json
{
"data": {
"id": "84f0c7a4-8c4e-4665-b676-cb9c5e40f1db",
"refer": "http://localhost:4000/3wP4BQ",
"origin": "https://newexample.com",
"clicks": []
}
}
```
8. **Delete a link by ID**
- Endpoint: `DELETE /api/links/:id`
- Payload: None
- Headers: `X-Api-Key`
- Response: 204
+496
View File
@@ -0,0 +1,496 @@
openapi: 3.1.0
info:
title: Bit - URL Shortener API
description: A high-performance URL shortener service with click tracking and analytics
version: 1.0.0
contact:
name: API Support
url: https://github.com/sjdonado/bit
servers:
- url: http://localhost:4000
description: Development server
- url: http://localhost:4001
description: Benchmark server
security:
- ApiKeyAuth: []
paths:
/api/ping:
get:
summary: Ping the API
description: Health check endpoint to verify the API is running
operationId: ping
tags:
- Health
security: []
responses:
'200':
description: API is healthy
content:
application/json:
schema:
type: object
properties:
data:
type: string
example: pong
/{slug}:
get:
summary: Redirect by slug
description: Redirects to the original URL and tracks the click asynchronously
operationId: redirectBySlug
tags:
- Redirects
security: []
parameters:
- name: slug
in: path
required: true
description: The short URL slug
schema:
type: string
example: 3wP4BQ
- name: utm_source
in: query
required: false
description: UTM source parameter for tracking
schema:
type: string
example: email_campaign
responses:
'301':
description: Redirect to original URL
headers:
Location:
description: The original URL
schema:
type: string
example: https://example.com
X-Forwarded-For:
description: Client IP address
schema:
type: string
User-Agent:
description: User agent string
schema:
type: string
'404':
description: Link not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/links:
get:
summary: List all links
description: Retrieve all links for the authenticated user with pagination support
operationId: listLinks
tags:
- Links
parameters:
- name: limit
in: query
description: Number of results per page
schema:
type: integer
default: 100
minimum: 1
maximum: 1000
- name: cursor
in: query
description: Pagination cursor from previous response
schema:
type: string
responses:
'200':
description: List of links
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/LinkSummary'
pagination:
$ref: '#/components/schemas/Pagination'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
summary: Create new link
description: Create a new shortened link
operationId: createLink
tags:
- Links
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- url
properties:
url:
type: string
format: uri
description: The URL to shorten
example: https://example.com
responses:
'201':
description: Link created successfully
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/Link'
'400':
description: Bad request - invalid URL or missing field
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missingField:
value:
error: "url: Required field"
invalidUrl:
value:
errors:
url:
- is invalid
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/links/{id}:
get:
summary: Get link by ID
description: Retrieve a specific link with up to 100 most recent clicks. For complete click history, use /api/links/{id}/clicks
operationId: getLink
tags:
- Links
parameters:
- name: id
in: path
required: true
description: Link ID
schema:
type: integer
format: int64
responses:
'200':
description: Link details
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/Link'
'404':
description: Link not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
put:
summary: Update link
description: Update the URL of an existing link
operationId: updateLink
tags:
- Links
parameters:
- name: id
in: path
required: true
description: Link ID
schema:
type: integer
format: int64
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- url
properties:
url:
type: string
format: uri
description: The new URL
example: https://newexample.com
responses:
'200':
description: Link updated successfully
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/Link'
'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 - link belongs to another user
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Link not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
delete:
summary: Delete link
description: Delete a link and all its associated clicks
operationId: deleteLink
tags:
- Links
parameters:
- name: id
in: path
required: true
description: Link ID
schema:
type: integer
format: int64
responses:
'204':
description: Link deleted successfully
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'403':
description: Forbidden - link belongs to another user
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Link not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/links/{id}/clicks:
get:
summary: List clicks for a link
description: Retrieve all clicks for a specific link with pagination support
operationId: listClicks
tags:
- Clicks
parameters:
- name: id
in: path
required: true
description: Link ID
schema:
type: integer
format: int64
- name: limit
in: query
description: Number of results per page
schema:
type: integer
default: 100
minimum: 1
maximum: 1000
- name: cursor
in: query
description: Pagination cursor from previous response
schema:
type: string
responses:
'200':
description: List of clicks
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Click'
pagination:
$ref: '#/components/schemas/Pagination'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Link not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-Api-Key
description: API key for authentication
schemas:
LinkSummary:
type: object
properties:
id:
type: integer
format: int64
description: Unique link identifier
example: 1
refer:
type: string
format: uri
description: The shortened URL
example: http://localhost:4000/3wP4BQ
origin:
type: string
format: uri
description: The original URL
example: https://monocuco.donado.co
Link:
allOf:
- $ref: '#/components/schemas/LinkSummary'
- type: object
properties:
clicks:
type: array
description: Array of click records (up to 100 most recent)
items:
$ref: '#/components/schemas/Click'
Click:
type: object
properties:
id:
type: integer
format: int64
description: Unique click identifier
example: 1
user_agent:
type: string
description: User agent string
example: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:127.0) Gecko/20100101 Firefox/127.0
country:
type: string
nullable: true
description: Country code (ISO 3166-1 alpha-2)
example: US
browser:
type: string
nullable: true
description: Browser name
example: Firefox
os:
type: string
nullable: true
description: Operating system
example: Mac OS X
referer:
type: string
nullable: true
description: Referer domain or utm_source
example: Direct
created_at:
type: string
format: date-time
description: Click timestamp
example: 2024-07-12T19:25:22Z
Pagination:
type: object
properties:
has_more:
type: boolean
description: Whether there are more results
example: true
next:
type: integer
format: int64
nullable: true
description: Cursor for next page (link/click ID)
example: 12
Error:
type: object
properties:
error:
type: string
description: Error message
example: Resource not found
required:
- error
ValidationErrors:
type: object
properties:
errors:
type: object
additionalProperties:
type: array
items:
type: string
description: Field-level validation errors
example:
url:
- is invalid
tags:
- name: Health
description: Health check endpoints
- name: Redirects
description: URL redirection and click tracking
- name: Links
description: Link management operations
- name: Clicks
description: Click analytics and tracking