Compare commits
140 commits
Author | SHA1 | Date | |
---|---|---|---|
9b634c91d7 | |||
1473a01e64 | |||
886a568e5e | |||
8099838cf9 | |||
133f530e29 | |||
49b3121b01 | |||
e1905be377 | |||
0b9f351d03 | |||
f7d8fb21a7 | |||
349eef7ddf | |||
97052bbe45 | |||
dd3ebedf95 | |||
4be274b653 | |||
21e3ae6398 | |||
e4994209a2 | |||
78a081854a | |||
f42e95459b | |||
4d7278a379 | |||
25c53dbe0c | |||
b36053c424 | |||
098eadb4ae | |||
b3febccb36 | |||
c76fce8253 | |||
92ca07a60f | |||
5bfdd75e26 | |||
f812f9e3d1 | |||
33004e23a8 | |||
cdc929da7b | |||
25c9f80bb7 | |||
801d0362cb | |||
519e4d0121 | |||
b4e6908edf | |||
ea5a184552 | |||
f60e4c0b60 | |||
4d2b4e0b2d | |||
208e41ee2b | |||
9c90091625 | |||
547d6865a7 | |||
f0db0112b2 | |||
1ca21a7f88 | |||
92df72f00d | |||
41a847d8b2 | |||
cdc2cac036 | |||
20c3fe2d65 | |||
89986ade9b | |||
d939f634f0 | |||
aba4d82544 | |||
f758ec9d2a | |||
a9b40d7474 | |||
11bdf42e4d | |||
e2d0c76519 | |||
ba4a62268c | |||
ed8abb8a5c | |||
642ab7579c | |||
d9b5a1b49e | |||
cef3235b47 | |||
42db5d5a88 | |||
b6cf68d1ab | |||
984b9469fd | |||
c8fb52fcca | |||
3efa0fcf17 | |||
c63a9e2b12 | |||
99ea8afd61 | |||
59e5b83d78 | |||
59801b2d43 | |||
b1d2e4bec5 | |||
10ba8b02e5 | |||
cb399f1ae1 | |||
0fa5ba29ac | |||
9af694e1df | |||
074dad265a | |||
d9d5392a84 | |||
5abf99bf95 | |||
1f6a452f74 | |||
99d693e2a9 | |||
59f5dccfd0 | |||
376de2ed54 | |||
a59ae4da20 | |||
981bf4f756 | |||
a904997096 | |||
9f9a5e41cc | |||
805ec0c6d7 | |||
8ec7178dd3 | |||
88c488106f | |||
e25ad9470c | |||
5c6a06ba6c | |||
c26c79bcdf | |||
247343453a | |||
f116aaffae | |||
1605588186 | |||
2f8c002f77 | |||
12aca9e18e | |||
86d3fafc50 | |||
6918842303 | |||
d3c4cc91a9 | |||
5be74d414a | |||
0504877ebf | |||
9796ed488b | |||
47a7e6e3d2 | |||
e9e7b2dd60 | |||
8348fa7134 | |||
23e828c481 | |||
f50b01e3ce | |||
05aaf3960c | |||
c9cd4a913d | |||
15eec18863 | |||
b31d4279f2 | |||
408ed58199 | |||
017fef9b52 | |||
d573f0af84 | |||
070e57fed7 | |||
5ee4473e77 | |||
e27473ead4 | |||
e6dceb6ddc | |||
ae6275331e | |||
6e1c0d79dd | |||
d5f022fd1e | |||
ce52548d69 | |||
9f82084cc2 | |||
7c3f608839 | |||
7428e1a21f | |||
f4919dca39 | |||
c6435f879b | |||
51f3a12f52 | |||
7300cbaeea | |||
310a6540b6 | |||
07826d05c4 | |||
e2f2f49a13 | |||
8262d4de49 | |||
b1acba27b6 | |||
4f6176fcb5 | |||
bfb4f42353 | |||
8ad005c568 | |||
47ab99f891 | |||
288816341e | |||
ed793f95f6 | |||
db6ee904de | |||
13a3ab0076 | |||
ef96c7167a | |||
777847c4ad |
11 changed files with 300 additions and 203 deletions
70
.drone.yml
70
.drone.yml
|
@ -1,70 +0,0 @@
|
|||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
steps:
|
||||
|
||||
|
||||
- name: docker_build_and_push_selfhosted
|
||||
image: plugins/docker
|
||||
settings:
|
||||
dockerfile: Dockerfile
|
||||
repo: registry.mgrote.net/python-api-server
|
||||
registry: registry.mgrote.net
|
||||
tags:
|
||||
- ${DRONE_COMMIT_SHA:0:8}
|
||||
- ${DRONE_COMMIT_BRANCH}
|
||||
- latest
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
- tag
|
||||
|
||||
|
||||
- name: docker_build_and_push_dockerhub
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: DOCKERHUB_USER
|
||||
password:
|
||||
from_secret: DOCKERHUB_PASS
|
||||
dockerfile: Dockerfile
|
||||
repo: quotengrote/python-api-server
|
||||
tags:
|
||||
- ${DRONE_COMMIT_SHA:0:8}
|
||||
- ${DRONE_COMMIT_BRANCH}
|
||||
- latest
|
||||
when:
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
- tag
|
||||
|
||||
|
||||
- name: docker_build_and_push_selfhosted_tag
|
||||
image: plugins/docker
|
||||
settings:
|
||||
dockerfile: Dockerfile
|
||||
repo: registry.mgrote.net/python-api-server
|
||||
registry: registry.mgrote.net
|
||||
tags:
|
||||
- ${DRONE_TAG{
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
|
||||
- name: docker_build_and_push_dockerhub_tag
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username:
|
||||
from_secret: DOCKERHUB_USER
|
||||
password:
|
||||
from_secret: DOCKERHUB_PASS
|
||||
dockerfile: Dockerfile
|
||||
repo: quotengrote/python-api-server
|
||||
tags:
|
||||
- ${DRONE_TAG{
|
||||
when:
|
||||
event:
|
||||
- tag
|
76
.gitea/workflows/build.yaml
Normal file
76
.gitea/workflows/build.yaml
Normal file
|
@ -0,0 +1,76 @@
|
|||
name: "lint, build and push"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "49 9 * * SUN"
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
|
||||
# Variables: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables + https://forgejo.org/docs/latest/user/actions/#env-1
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Run Gitleaks
|
||||
uses: docker://zricethezav/gitleaks:v8.19.2
|
||||
with:
|
||||
args: detect --no-git --verbose --source ${{ github.workspace }}
|
||||
- name: Run hadolint
|
||||
uses: docker://pipelinecomponents/hadolint:0.26.4
|
||||
with:
|
||||
args: hadolint Dockerfile
|
||||
# - name: Run ShellCheck
|
||||
# uses: sudo-bot/action-shellcheck@latest
|
||||
# with:
|
||||
# cli-args: "shell.sh"
|
||||
- name: Send notification on error
|
||||
uses: dawidd6/action-send-mail@v3
|
||||
if: failure()
|
||||
with:
|
||||
connection_url: smtp://docker10.mgrote.net:1025
|
||||
secure: false
|
||||
ignore_cert: true
|
||||
to: michael.grote@posteo.de
|
||||
from: Gitea Actions <gitea@mgrote.net>
|
||||
subject: "CI Error in ${{ env.GITHUB_REPOSITORY }}"
|
||||
body: "Job of ${{ env.github.repository }} failed!"
|
||||
|
||||
build:
|
||||
needs: [gitleaks, hadolint] # shellcheck
|
||||
steps:
|
||||
- uses: https://github.com/actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
# - name: Login to Docker Hub
|
||||
# uses: docker/login-action@v3
|
||||
# with:
|
||||
# username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
# password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Extract repository name
|
||||
id: meta
|
||||
run: |
|
||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
||||
- name: Extract branch name
|
||||
shell: bash
|
||||
run: echo "REPO_BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
|
||||
id: extract_branch
|
||||
- name: Build and push Docker image
|
||||
uses: https://github.com/docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: "registry.mgrote.net/${{ steps.meta.outputs.REPO_NAME }}:latest,registry.mgrote.net/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.extract_branch.outputs.REPO_BRANCH }}"
|
||||
- name: Send notification on error
|
||||
uses: dawidd6/action-send-mail@v3
|
||||
if: failure()
|
||||
with:
|
||||
connection_url: smtp://docker10.mgrote.net:1025
|
||||
secure: false
|
||||
ignore_cert: true
|
||||
to: michael.grote@posteo.de
|
||||
from: Gitea Actions <gitea@mgrote.net>
|
||||
subject: "CI Error in ${{ env.GITHUB_REPOSITORY }}"
|
||||
body: Job of ${{ env.github.repository }} failed!
|
20
Dockerfile
20
Dockerfile
|
@ -1,27 +1,23 @@
|
|||
FROM ubuntu:focal
|
||||
FROM alpine:3.20.3
|
||||
|
||||
# deaktiviert Nachfragen beim installieren von Paketen
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# hadolint ignore=DL3008,DL3028,DL4006
|
||||
RUN apt-get update && \
|
||||
apt-get -y --no-install-recommends install \
|
||||
python3-pip curl && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /var/tmp/* /tmp/*
|
||||
# hadolint ignore=DL3018
|
||||
RUN apk add --no-cache \
|
||||
py3-pip \
|
||||
curl
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY templates/file_list.html ./templates/
|
||||
COPY requirements.txt .
|
||||
COPY app.py .
|
||||
COPY gunicorn_config.py .
|
||||
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
RUN pip install --no-cache-dir --break-system-packages -r requirements.txt
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
HEALTHCHECK --interval=5s --timeout=3s \
|
||||
CMD curl -f http://localhost:5000/health || exit 1
|
||||
CMD curl -s -f http://localhost:5000/health || exit 1
|
||||
|
||||
#CMD [ "python3", "-m" , "flask", "run", "--port", "5000", "--host", "0.0.0.0"]
|
||||
CMD [ "python3", "-m" , "gunicorn", "app:app", "-c", "gunicorn_config.py"]
|
||||
|
|
117
README.md
117
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,34 @@ 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
|
||||
ENABLE_WEBSERVER: true # if enabled a list of files can be viewed in a webbrowser (see screenshot)
|
||||
|
||||
volumes:
|
||||
uploads:
|
||||
|
||||
```
|
||||
|
||||
### ENABLE_WEBSERVER Screenshot
|
||||
|
||||
|
||||
![](./assets/screenshot_webui.png)
|
||||
|
||||
## 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/``
|
||||
|
|
129
app.py
129
app.py
|
@ -1,26 +1,82 @@
|
|||
# pylint: disable=C0114
|
||||
import os
|
||||
import re
|
||||
import uuid
|
||||
from flask import Flask, request, jsonify, send_from_directory
|
||||
import datetime
|
||||
from flask import Flask, request, jsonify, send_from_directory, render_template
|
||||
from flasgger import Swagger
|
||||
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__, template_folder='templates')
|
||||
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
|
||||
app.config['MAX_CONTENT_LENGTH'] = int(os.environ.get('MAX_CONTENT_LENGTH', '5')) * 1024 * 1024 # in MB # pylint: disable=C0301
|
||||
app.config['ENABLE_WEBSERVER'] = os.getenv('ENABLE_WEBSERVER', 'True').lower() == 'true'
|
||||
|
||||
|
||||
VALID_FILENAME_REGEX = r'^[a-zA-Z0-9\-_\.]+$'
|
||||
|
||||
AUTH_TOKEN = os.environ.get('AUTH_TOKEN', 'myuploadtoken')
|
||||
|
||||
def is_valid_filename(filename):
|
||||
def is_valid_filename(filename): # pylint: disable=C0116
|
||||
return bool(re.match(VALID_FILENAME_REGEX, filename))
|
||||
|
||||
if app.config['ENABLE_WEBSERVER']:
|
||||
@app.route('/', methods=['GET'])
|
||||
def file_list():
|
||||
"""
|
||||
Endpoint for displaying a list of files in the upload directory.
|
||||
"""
|
||||
files = []
|
||||
for filename in os.listdir(app.config['UPLOAD_DIRECTORY']):
|
||||
file_path = os.path.join(app.config['UPLOAD_DIRECTORY'], filename)
|
||||
if os.path.isfile(file_path):
|
||||
stats = os.stat(file_path)
|
||||
size = stats.st_size
|
||||
last_modified = datetime.datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S') # pylint: disable=C0301
|
||||
files.append({
|
||||
'name': filename,
|
||||
'size': size,
|
||||
'last_modified': last_modified
|
||||
})
|
||||
|
||||
return render_template('file_list.html', files=files)
|
||||
|
||||
@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
|
||||
|
||||
|
@ -35,14 +91,29 @@ def upload_file():
|
|||
return jsonify({'error': 'No file selected for upload'}), 400
|
||||
|
||||
if not is_valid_filename(file.filename):
|
||||
return jsonify({'error': 'Invalid filename. Only alphanumeric characters, hyphens, underscores, and periods are allowed.'}), 400
|
||||
return jsonify({'error': 'Invalid filename. Only alphanumeric characters, hyphens, underscores, and periods are allowed.'}), 400 # pylint: disable=C0301
|
||||
|
||||
filename = file.filename
|
||||
file.save(os.path.join(app.config['UPLOAD_DIRECTORY'], filename))
|
||||
return jsonify({'success': 'File \'{}\' successfully uploaded'.format(filename)})
|
||||
return jsonify({'success': 'File \'{}\' successfully uploaded'.format(filename)}) # pylint: disable=C0209
|
||||
|
||||
@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 +121,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
|
||||
|
||||
|
@ -61,10 +154,28 @@ def delete_file(filename):
|
|||
return jsonify({'error': 'File not found'}), 404
|
||||
|
||||
os.remove(file_path)
|
||||
return jsonify({'success': 'File \'{}\' successfully deleted'.format(filename)})
|
||||
return jsonify({'success': 'File \'{}\' successfully deleted'.format(filename)}) # pylint: disable=C0209
|
||||
|
||||
|
||||
@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
|
||||
|
||||
|
@ -77,7 +188,7 @@ def list_files():
|
|||
if os.path.isfile(file_path):
|
||||
stats = os.stat(file_path)
|
||||
size = stats.st_size
|
||||
last_modified = datetime.datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
|
||||
last_modified = datetime.datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S') # pylint: disable=C0301
|
||||
files.append({
|
||||
'name': filename,
|
||||
'size': size,
|
||||
|
|
BIN
assets/screenshot_webui.png
Normal file
BIN
assets/screenshot_webui.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
|
@ -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:
|
13
renovate.json
Normal file
13
renovate.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
":dependencyDashboard",
|
||||
":semanticPrefixFixDepsChoreOthers",
|
||||
":ignoreModulesAndTests",
|
||||
"group:monorepos",
|
||||
"group:recommended",
|
||||
"replacements:all",
|
||||
"workarounds:all",
|
||||
"mergeConfidence:all-badges"
|
||||
]
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
Flask==2.0.1
|
||||
Flask-Cors==3.0.10
|
||||
gunicorn
|
||||
requests
|
||||
Flask==3.0.3
|
||||
Flask-Cors==5.0.0
|
||||
gunicorn==23.0.0
|
||||
requests==2.32.3
|
||||
flasgger==0.9.7.1
|
||||
|
|
48
templates/file_list.html
Normal file
48
templates/file_list.html
Normal file
|
@ -0,0 +1,48 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>File List</title>
|
||||
<style>
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>File List</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Size (KB)</th>
|
||||
<th>Last Modified</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for file in files %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('download_file', filename=file.name) }}">{{ file.name }}</a>
|
||||
<td>{{ '%.2f' % (file.size / 1024) }} KB</td>
|
||||
<td>{{ file.last_modified }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
3
tests.sh
3
tests.sh
|
@ -1,4 +1,5 @@
|
|||
#!/bin/bash
|
||||
# shellcheck disable=all
|
||||
|
||||
# führe Script nach Fehlern weiter aus
|
||||
set +e
|
||||
|
@ -7,7 +8,7 @@ RED="\e[31m"
|
|||
GREEN="\e[32m"
|
||||
ENDCOLOR="\e[0m"
|
||||
export TOKEN=myuploadtoken
|
||||
export URL="http://docker10.grote.lan:5040"
|
||||
export URL="http://docker10.mgrote.net:5040"
|
||||
|
||||
mkdir -p tests
|
||||
|
||||
|
|
Loading…
Reference in a new issue