Add open api

This commit is contained in:
2024-11-17 22:08:58 +11:00
parent 7b993296ba
commit 5cfa2f40f7
7 changed files with 880 additions and 224 deletions
+24
View File
@@ -0,0 +1,24 @@
{% extends 'base_blank.html' %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "API Documentation" %} - GoLinks{% endblock %}
{% block extra_css %}
<style>
body {
margin: 0;
padding: 0;
}
redoc {
display: block;
height: 100vh;
}
</style>
{% endblock %}
{% block content %}
<redoc spec-url="{% static 'openapi.yaml' %}"></redoc>
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
{% endblock %}
-212
View File
@@ -138,219 +138,7 @@ Custom pages allow you to create markdown-based web pages that can serve as docu
**Example Use Cases:**
Check http://go.junv.cc/custom/today/
## API Usage
GoLinks provides RESTful APIs for programmatic access to pages. Here are the available endpoints and their usage:
### List Pages
Retrieve a paginated list of all pages:
```
GET /api/pages/
GET /api/pages/?page=2
GET /api/pages/?page_size=20
```
Response examples:
```
{
"count": 100,
"next": "http://localhost:8000/api/pages/?page=2",
"previous": null,
"results": [
{
"id": 1,
"url": "https://example.com",
"title": "Example Page",
"summary": "This is a summary",
"content": "This is the content",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
},
// ... more pages
]
}
```
### Create a new Page
```
POST /api/pages/
Content-Type: application/json
{
"url": "https://example.com",
"title": "Example Page",
"summary": "This is a summary",
"content": "This is the content"
}
```
Response example:
```
{
"id": 1,
"url": "https://example.com",
"title": "Example Page",
"summary": "This is a summary",
"content": "This is the content",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
```
### List newsletters:
```
GET /api/newsletters/
```
### Create a newsletter:
```
curl -X POST http://localhost:8000/api/newsletters \
-H "Content-Type: application/json" \
-d '{
"title": "Test Newsletter",
"type": "technology",
"content": "# Test Content\n\nThis is a test newsletter."
}'
```
## Collection API Usage
GoLinks provides RESTful APIs for managing image collections. Here are the available endpoints and their usage:
### List Collections
Retrieve a paginated list of all collections:
```
GET /api/collections
GET /api/collections?page=2
GET /api/collections?page_size=20
```
Response example:
```json
{
"count": 10,
"next": "http://localhost:8000/api/collections?page=2",
"previous": null,
"results": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Collection",
"description": "A collection of images",
"image_count": 5,
"created_at": "2024-01-01T00:00:00Z"
},
// ... more collections
]
}
```
### Create a Collection
Create a new image collection:
```
POST /api/collections
Content-Type: application/json
{
"name": "My Collection",
"description": "A collection of images"
}
```
Response example:
```json
{
"status": "success",
"message": "Collection created successfully",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Collection",
"description": "A collection of images",
"image_count": 0,
"created_at": "2024-01-01T00:00:00Z"
}
}
```
### Upload Images to Collection
Upload one or multiple images to a collection:
```
POST /api/collections/{collection_id}/upload_images
Content-Type: multipart/form-data
file: image1.jpg
file: image2.jpg
```
Response example:
```json
{
"status": "success",
"message": "Successfully uploaded 2 images",
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"title": "image1.jpg",
"content_type": "image/jpeg",
"size": 1024000,
"url": "https://your-r2-domain.com/images/...",
"created_at": "2024-01-01T00:00:00Z"
},
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"title": "image2.jpg",
"content_type": "image/jpeg",
"size": 2048000,
"url": "https://your-r2-domain.com/images/...",
"created_at": "2024-01-01T00:00:00Z"
}
]
}
```
### Delete an Image
Delete a specific image from a collection:
```
DELETE /api/images/{image_id}
```
Response example:
```json
{
"status": "success",
"message": "Image deleted successfully"
}
```
### Delete a Collection
Delete a collection and all its images:
```
DELETE /api/collections/{collection_id}
```
Response example:
```json
{
"status": "success",
"message": "Collection deleted successfully"
}
```
### Error Responses
In case of errors, the API will return appropriate status codes and error messages:
```json
{
"status": "error",
"message": "Error description here"
}
```
Common status codes:
- 200: Success
- 201: Created
- 400: Bad Request
- 404: Not Found
- 500: Internal Server Error
{% endfilter %}
</div>
+10 -4
View File
@@ -4,6 +4,7 @@ from . import page_views
from . import search_views
from . import newsletter_views
from . import collection_views
from django.views.generic import TemplateView
urlpatterns = [
# Regular UI URLs
@@ -39,6 +40,15 @@ urlpatterns = [
path('ui/newsletters/<int:pk>/edit/', newsletter_views.NewsletterUpdateView.as_view(), name='newsletter-update'),
path('ui/newsletters/<int:pk>/delete/', newsletter_views.NewsletterDeleteView.as_view(), name='newsletter-delete'),
# Collection slideshow
path('ui/collections/<uuid:pk>/slideshow/',
collection_views.CollectionSlideshowView.as_view(),
name='collection-slideshow'),
# API Documentation
path('ui/api-docs/',
TemplateView.as_view(template_name='links/api_docs.html'),
name='api-docs'),
# Public newsletter URL
path('public/newsletters/<int:pk>/',
newsletter_views.PublicNewsletterView.as_view(),
@@ -52,8 +62,4 @@ urlpatterns = [
path('<str:alias>/<str:param>/', views.redirect_to_original, name='redirect_to_original_with_param'),
path('alias/<str:alias>/', views.LinkDetailView.as_view(), name='link_detail_by_alias'),
# Collection slideshow
path('ui/collections/<uuid:pk>/slideshow/',
collection_views.CollectionSlideshowView.as_view(),
name='collection-slideshow'),
]
+20
View File
@@ -1388,6 +1388,11 @@ video {
background-color: rgb(254 249 195 / var(--tw-bg-opacity));
}
.bg-gray-900 {
--tw-bg-opacity: 1;
background-color: rgb(17 24 39 / var(--tw-bg-opacity));
}
.bg-opacity-50 {
--tw-bg-opacity: 0.5;
}
@@ -1784,6 +1789,11 @@ video {
color: rgb(133 77 14 / var(--tw-text-opacity));
}
.text-gray-300 {
--tw-text-opacity: 1;
color: rgb(209 213 219 / var(--tw-text-opacity));
}
.underline {
text-decoration-line: underline;
}
@@ -1998,6 +2008,11 @@ video {
background-color: rgb(185 28 28 / var(--tw-bg-opacity));
}
.hover\:bg-gray-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
}
.hover\:text-blue-200:hover {
--tw-text-opacity: 1;
color: rgb(191 219 254 / var(--tw-text-opacity));
@@ -2068,6 +2083,11 @@ video {
color: rgb(153 27 27 / var(--tw-text-opacity));
}
.hover\:text-white:hover {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.hover\:underline:hover {
text-decoration-line: underline;
}
+404
View File
@@ -0,0 +1,404 @@
openapi: 3.1.0
info:
title: GoLinks API
description: API documentation for GoLinks service
version: 1.0.0
servers:
- url: http://localhost:8000
description: Local development server
paths:
/api/pages:
get:
summary: List all pages
description: Retrieve a paginated list of all 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:
summary: Create a new page
description: Create a new page with the provided data
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/newsletters:
get:
summary: List all newsletters
description: Retrieve a paginated list of all newsletters
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/NewsletterList'
post:
summary: Create a new newsletter
description: Create a new newsletter with the provided data
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewsletterCreate'
responses:
'201':
description: Newsletter created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Newsletter'
/api/collections:
get:
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:
summary: Create a new collection
description: Create a new 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:
summary: Delete a collection
description: 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:
summary: Upload images to collection
description: Upload one or multiple images to a collection
parameters:
- in: path
name: collection_id
required: true
schema:
type: string
format: uuid
description: The ID of the collection
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: array
items:
type: string
format: binary
responses:
'201':
description: Images uploaded successfully
content:
application/json:
schema:
$ref: '#/components/schemas/ImageUploadResponse'
/api/images/{image_id}:
delete:
summary: Delete an image
description: Delete a specific image from a 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'
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'
Newsletter:
type: object
properties:
id:
type: integer
title:
type: string
type:
type: string
enum: [technology, world_news]
content:
type: string
created_at:
type: string
format: date-time
NewsletterCreate:
type: object
required:
- title
- type
- content
properties:
title:
type: string
type:
type: string
enum: [technology, world_news]
content:
type: string
NewsletterList:
type: object
properties:
count:
type: integer
next:
type: string
nullable: true
previous:
type: string
nullable: true
results:
type: array
items:
$ref: '#/components/schemas/Newsletter'
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
title:
type: string
content_type:
type: string
size:
type: integer
url:
type: string
created_at:
type: string
format: date-time
ImageUploadResponse:
type: object
properties:
status:
type: string
enum: [success]
message:
type: string
data:
type: array
items:
$ref: '#/components/schemas/Image'
SuccessResponse:
type: object
properties:
status:
type: string
enum: [success]
message:
type: string
ErrorResponse:
type: object
properties:
status:
type: string
enum: [error]
message:
type: string
+404
View File
@@ -0,0 +1,404 @@
openapi: 3.1.0
info:
title: GoLinks API
description: API documentation for GoLinks service
version: 1.0.0
servers:
- url: http://localhost:8000
description: Local development server
paths:
/api/pages:
get:
summary: List all pages
description: Retrieve a paginated list of all 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:
summary: Create a new page
description: Create a new page with the provided data
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/newsletters:
get:
summary: List all newsletters
description: Retrieve a paginated list of all newsletters
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/NewsletterList'
post:
summary: Create a new newsletter
description: Create a new newsletter with the provided data
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewsletterCreate'
responses:
'201':
description: Newsletter created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/Newsletter'
/api/collections:
get:
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:
summary: Create a new collection
description: Create a new 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:
summary: Delete a collection
description: 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:
summary: Upload images to collection
description: Upload one or multiple images to a collection
parameters:
- in: path
name: collection_id
required: true
schema:
type: string
format: uuid
description: The ID of the collection
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: array
items:
type: string
format: binary
responses:
'201':
description: Images uploaded successfully
content:
application/json:
schema:
$ref: '#/components/schemas/ImageUploadResponse'
/api/images/{image_id}:
delete:
summary: Delete an image
description: Delete a specific image from a 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'
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'
Newsletter:
type: object
properties:
id:
type: integer
title:
type: string
type:
type: string
enum: [technology, world_news]
content:
type: string
created_at:
type: string
format: date-time
NewsletterCreate:
type: object
required:
- title
- type
- content
properties:
title:
type: string
type:
type: string
enum: [technology, world_news]
content:
type: string
NewsletterList:
type: object
properties:
count:
type: integer
next:
type: string
nullable: true
previous:
type: string
nullable: true
results:
type: array
items:
$ref: '#/components/schemas/Newsletter'
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
title:
type: string
content_type:
type: string
size:
type: integer
url:
type: string
created_at:
type: string
format: date-time
ImageUploadResponse:
type: object
properties:
status:
type: string
enum: [success]
message:
type: string
data:
type: array
items:
$ref: '#/components/schemas/Image'
SuccessResponse:
type: object
properties:
status:
type: string
enum: [success]
message:
type: string
ErrorResponse:
type: object
properties:
status:
type: string
enum: [error]
message:
type: string
+18 -8
View File
@@ -113,14 +113,6 @@
{% trans "Screenshots" %}
</div>
</a>
<a href="{% url 'help' %}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<div class="flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
{% trans "Help" %}
</div>
</a>
<a href="{% url 'collection-list' %}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<div class="flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -130,6 +122,24 @@
{% trans "Images" %}
</div>
</a>
<a href="{% url 'api-docs' %}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<div class="flex items-center">
<svg class="w-5 h-5 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg>
{% trans "APIs" %}
</div>
</a>
<a href="{% url 'help' %}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<div class="flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
{% trans "Help" %}
</div>
</a>
</div>
</div>
<!-- 语言选择器 -->