add swagger (#1)
Co-authored-by: Michael Grote <michael.grote@posteo.de> Reviewed-on: mg/python-api-server#1
This commit is contained in:
parent
ef96c7167a
commit
13a3ab0076
4 changed files with 111 additions and 107 deletions
111
README.md
111
README.md
|
@ -2,7 +2,6 @@
|
|||
|
||||
a small flask-application for storing and downloading stuff like small binaries
|
||||
|
||||
|
||||
## Variables
|
||||
|
||||
- ``MAX_CONTENT_LENGTH``: maximal Filesize in MB; defaults to 5MB
|
||||
|
@ -11,94 +10,28 @@ a small flask-application for storing and downloading stuff like small binaries
|
|||
|
||||
## Example Docker-Compose
|
||||
|
||||
see [docker-compose.yml](./docker-compose.yml)
|
||||
```yaml
|
||||
version: '3'
|
||||
services:
|
||||
python-api-server:
|
||||
container_name: httpd-api
|
||||
image: quotengrote/python-api-server:v2
|
||||
ports:
|
||||
- "5040:5000"
|
||||
volumes:
|
||||
- uploads:/uploads
|
||||
environment:
|
||||
# FLASK_DEBUG: 1 # for debugging
|
||||
# FLASK_APP: app # for debugging
|
||||
MAX_CONTENT_LENGTH: 10
|
||||
UPLOAD_DIRECTORY: /uploads
|
||||
AUTH_TOKEN: myuploadtoken
|
||||
|
||||
volumes:
|
||||
uploads:
|
||||
|
||||
```
|
||||
|
||||
## API-Endpoints
|
||||
|
||||
### /list
|
||||
|
||||
#### input
|
||||
|
||||
```bash
|
||||
curl -H "token: myuploadtoken" http://docker10.host.lan:5040/list | jq
|
||||
```
|
||||
|
||||
#### output
|
||||
|
||||
```bash
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"last_modified": "2023-04-13 11:43:51",
|
||||
"name": "file1",
|
||||
"size": 1034
|
||||
},
|
||||
{
|
||||
"last_modified": "2023-04-13 11:53:59",
|
||||
"name": "file2",
|
||||
"size": 5
|
||||
},
|
||||
{
|
||||
"last_modified": "2023-04-13 12:41:18",
|
||||
"name": "file3",
|
||||
"size": 3478
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### /upload
|
||||
|
||||
If a existing file has the same name as the newly uploaded file, it will be overwritten.
|
||||
|
||||
#### input
|
||||
|
||||
```bash
|
||||
curl -X POST -H "token: myuploadtoken" -F "file=@tests/file" http://docker10.host.lan:5040/upload | jq
|
||||
```
|
||||
|
||||
#### output
|
||||
|
||||
```bash
|
||||
{
|
||||
"success": "File 'file' successfully uploaded"
|
||||
}
|
||||
```
|
||||
|
||||
### /download
|
||||
|
||||
#### input
|
||||
|
||||
```bash
|
||||
wget http://docker10.host.lan:5040/download/file
|
||||
```
|
||||
|
||||
### /delete
|
||||
|
||||
#### input
|
||||
|
||||
```bash
|
||||
curl -X DELETE -H "token: myuploadtoken" http://docker10.host.lan:5040/delete/file | jq
|
||||
```
|
||||
|
||||
#### output
|
||||
|
||||
```bash
|
||||
{
|
||||
"success": "File 'file' successfully deleted"
|
||||
}
|
||||
```
|
||||
|
||||
### /health
|
||||
|
||||
#### input
|
||||
|
||||
```bash
|
||||
curl http://docker10.host.lan:5040/health
|
||||
```
|
||||
|
||||
#### output
|
||||
|
||||
```bash
|
||||
OK
|
||||
```
|
||||
- see [Flasgger](https://github.com/flasgger/flasgger): ``http://<host>:5040/apidocs/``
|
||||
|
|
88
app.py
88
app.py
|
@ -3,8 +3,10 @@ import re
|
|||
import uuid
|
||||
from flask import Flask, request, jsonify, send_from_directory
|
||||
import datetime
|
||||
from flasgger import Swagger, swag_from
|
||||
|
||||
app = Flask(__name__)
|
||||
swagger = Swagger(app)
|
||||
app.config['UPLOAD_DIRECTORY'] = os.environ.get('UPLOAD_DIRECTORY', '/uploads')
|
||||
app.config['MAX_CONTENT_LENGTH'] = int(os.environ.get('MAX_CONTENT_LENGTH', '5')) * 1024 * 1024 # in MB
|
||||
|
||||
|
@ -17,10 +19,41 @@ def is_valid_filename(filename):
|
|||
|
||||
@app.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
"""
|
||||
Endpoint for health check.
|
||||
---
|
||||
responses:
|
||||
200:
|
||||
description: OK
|
||||
"""
|
||||
return 'OK'
|
||||
|
||||
@app.route('/upload', methods=['POST'])
|
||||
def upload_file():
|
||||
"""
|
||||
Endpoint for uploading files.
|
||||
Filename can only contain alphanumeric characters, hyphens, underscores, and periods.
|
||||
If the filename is the same as an existing file, this file will be overwritten.
|
||||
---
|
||||
parameters:
|
||||
- name: file
|
||||
in: formData
|
||||
type: file
|
||||
required: true
|
||||
description: The file to upload.
|
||||
- name: token
|
||||
in: header
|
||||
type: string
|
||||
required: true
|
||||
description: Authentication token.
|
||||
responses:
|
||||
200:
|
||||
description: File uploaded successfully.
|
||||
400:
|
||||
description: Bad request.
|
||||
401:
|
||||
description: Unauthorized.
|
||||
"""
|
||||
if 'file' not in request.files:
|
||||
return jsonify({'error': 'No file part in the request'}), 400
|
||||
|
||||
|
@ -43,6 +76,21 @@ def upload_file():
|
|||
|
||||
@app.route('/download/<filename>', methods=['GET'])
|
||||
def download_file(filename):
|
||||
"""
|
||||
Endpoint for downloading files.
|
||||
---
|
||||
parameters:
|
||||
- name: filename
|
||||
in: path
|
||||
type: string
|
||||
required: true
|
||||
description: The name of the file to download.
|
||||
responses:
|
||||
200:
|
||||
description: File downloaded successfully.
|
||||
404:
|
||||
description: File not found.
|
||||
"""
|
||||
try:
|
||||
return send_from_directory(app.config['UPLOAD_DIRECTORY'], filename)
|
||||
except FileNotFoundError:
|
||||
|
@ -50,6 +98,28 @@ def download_file(filename):
|
|||
|
||||
@app.route('/delete/<filename>', methods=['DELETE'])
|
||||
def delete_file(filename):
|
||||
"""
|
||||
Endpoint for deleting files.
|
||||
---
|
||||
parameters:
|
||||
- name: filename
|
||||
in: path
|
||||
type: string
|
||||
required: true
|
||||
description: The name of the file to delete.
|
||||
- name: token
|
||||
in: header
|
||||
type: string
|
||||
required: true
|
||||
description: Authentication token.
|
||||
responses:
|
||||
200:
|
||||
description: File deleted successfully.
|
||||
401:
|
||||
description: Unauthorized.
|
||||
404:
|
||||
description: File not found.
|
||||
"""
|
||||
if 'token' not in request.headers:
|
||||
return jsonify({'error': 'No token supplied'}), 401
|
||||
|
||||
|
@ -63,8 +133,26 @@ def delete_file(filename):
|
|||
os.remove(file_path)
|
||||
return jsonify({'success': 'File \'{}\' successfully deleted'.format(filename)})
|
||||
|
||||
|
||||
@app.route('/list', methods=['GET'])
|
||||
def list_files():
|
||||
"""
|
||||
Endpoint for listing files.
|
||||
---
|
||||
parameters:
|
||||
- name: token
|
||||
in: header
|
||||
type: string
|
||||
required: true
|
||||
description: Authentication token.
|
||||
responses:
|
||||
200:
|
||||
description: Listed files successfully.
|
||||
400:
|
||||
description: Bad request.
|
||||
401:
|
||||
description: Unauthorized.
|
||||
"""
|
||||
if 'token' not in request.headers:
|
||||
return jsonify({'error': 'No token supplied'}), 401
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
version: '3'
|
||||
services:
|
||||
python-api-server:
|
||||
container_name: httpd-api
|
||||
image: quotengrote/python-api-server:latest
|
||||
ports:
|
||||
- "5040:5000"
|
||||
volumes:
|
||||
- uploads:/uploads
|
||||
environment:
|
||||
# FLASK_DEBUG: 1 # for debugging
|
||||
# FLASK_APP: app # for debugging
|
||||
MAX_CONTENT_LENGTH: 10
|
||||
UPLOAD_DIRECTORY: /uploads
|
||||
AUTH_TOKEN: myuploadtoken
|
||||
|
||||
volumes:
|
||||
uploads:
|
|
@ -2,3 +2,4 @@ Flask==2.0.1
|
|||
Flask-Cors==3.0.10
|
||||
gunicorn
|
||||
requests
|
||||
flasgger
|
||||
|
|
Reference in a new issue