mirror of
https://github.com/Benzauber/convert-commander.git
synced 2025-12-20 09:49:31 -06:00
all files uploaded
This commit is contained in:
201
api.py
Normal file
201
api.py
Normal file
@@ -0,0 +1,201 @@
|
||||
from flask import Flask, request, jsonify, send_file, make_response
|
||||
from flask_swagger_ui import get_swaggerui_blueprint
|
||||
import os
|
||||
import chnage # Dein Konvertierungsskript
|
||||
from flask_cors import CORS
|
||||
import shutil
|
||||
import logging
|
||||
from werkzeug.utils import secure_filename
|
||||
import mimetypes
|
||||
import secrets
|
||||
import hashlib
|
||||
from functools import wraps
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app, resources={r"/*": {"origins": "*"}})
|
||||
|
||||
UPLOAD_FOLDER = 'uploads'
|
||||
CONVERT_FOLDER = 'convert'
|
||||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||
app.config['CONVERT_FOLDER'] = CONVERT_FOLDER
|
||||
|
||||
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
||||
os.makedirs(CONVERT_FOLDER, exist_ok=True)
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
SWAGGER_URL = '/docs'
|
||||
API_URL = '/static/swagger.json'
|
||||
|
||||
swaggerui_blueprint = get_swaggerui_blueprint(
|
||||
SWAGGER_URL,
|
||||
API_URL,
|
||||
config={
|
||||
'app_name': "Convert-Commander API"
|
||||
}
|
||||
)
|
||||
|
||||
app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL)
|
||||
|
||||
hashed_tokens = set()
|
||||
|
||||
def generate_token():
|
||||
return secrets.token_urlsafe(32)
|
||||
|
||||
def hash_token(token):
|
||||
"""Generiere einen SHA-256 Hash des Tokens."""
|
||||
return hashlib.sha256(token.encode()).hexdigest()
|
||||
|
||||
def token_required(f):
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
token = request.headers.get('X-API-Token')
|
||||
if not token:
|
||||
return jsonify({'error': 'API token is missing'}), 401
|
||||
hashed_token = hash_token(token)
|
||||
if hashed_token not in hashed_tokens:
|
||||
return jsonify({'error': 'Invalid API token'}), 401
|
||||
return f(*args, **kwargs)
|
||||
return decorated
|
||||
|
||||
@app.route('/generate_token', methods=['POST'])
|
||||
def create_token():
|
||||
"""
|
||||
Generiere ein neues API-Token
|
||||
---
|
||||
tags:
|
||||
- Authentication
|
||||
responses:
|
||||
200:
|
||||
description: New API token generated
|
||||
"""
|
||||
token = generate_token()
|
||||
hashed_token = hash_token(token)
|
||||
hashed_tokens.add(hashed_token) # Speichere das gehashte Token
|
||||
return jsonify({'token': token}), 200 # Gebe das einfache Token zurück
|
||||
|
||||
def delete_files_in_folder(folder_path):
|
||||
if os.path.exists(folder_path):
|
||||
for filename in os.listdir(folder_path):
|
||||
file_path = os.path.join(folder_path, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path) or os.path.islink(file_path):
|
||||
os.unlink(file_path)
|
||||
elif os.path.isdir(file_path):
|
||||
shutil.rmtree(file_path)
|
||||
except Exception as e:
|
||||
logging.error(f'Error deleting {file_path}. Reason: {e}')
|
||||
else:
|
||||
logging.warning(f'Folder {folder_path} does not exist')
|
||||
|
||||
@app.route('/upload', methods=['POST'])
|
||||
@token_required
|
||||
def upload_file():
|
||||
"""
|
||||
Lade eine Datei hoch und konvertiere sie in das angegebene Format
|
||||
---
|
||||
tags:
|
||||
- File Conversion
|
||||
consumes:
|
||||
- multipart/form-data
|
||||
parameters:
|
||||
- in: header
|
||||
name: X-API-Token
|
||||
type: string
|
||||
required: true
|
||||
description: API token for authentication
|
||||
- in: formData
|
||||
name: file
|
||||
type: file
|
||||
required: true
|
||||
description: The file to upload
|
||||
- in: formData
|
||||
name: format
|
||||
type: string
|
||||
required: true
|
||||
description: The target format for conversion
|
||||
responses:
|
||||
200:
|
||||
description: Successfully converted file
|
||||
400:
|
||||
description: Invalid request
|
||||
401:
|
||||
description: Unauthorized
|
||||
500:
|
||||
description: Server error
|
||||
"""
|
||||
if 'file' not in request.files or 'format' not in request.form:
|
||||
return jsonify({'error': 'No file or format specified'}), 400
|
||||
|
||||
file = request.files['file']
|
||||
target_format = request.form['format']
|
||||
|
||||
if file.filename == '':
|
||||
return jsonify({'error': 'No file selected'}), 400
|
||||
|
||||
filename = secure_filename(file.filename)
|
||||
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||
file.save(filepath)
|
||||
|
||||
logging.info(f"File uploaded: {filepath}")
|
||||
|
||||
try:
|
||||
logging.debug(f"Starting conversion from {filepath} to {target_format}")
|
||||
chnage.start(filepath, target_format)
|
||||
|
||||
converted_filename = os.path.splitext(filename)[0] + f'.{target_format}'
|
||||
converted_filepath = os.path.join(app.config['CONVERT_FOLDER'], converted_filename)
|
||||
|
||||
if not os.path.exists(converted_filepath):
|
||||
return jsonify({'error': 'Converted file not found'}), 500
|
||||
|
||||
logging.debug(f"Sending converted file: {converted_filepath}")
|
||||
|
||||
mime_type, _ = mimetypes.guess_type(converted_filepath)
|
||||
if mime_type is None:
|
||||
mime_type = 'application/octet-stream'
|
||||
|
||||
with open(converted_filepath, 'rb') as f:
|
||||
file_data = f.read()
|
||||
|
||||
response = make_response(file_data)
|
||||
response.headers.set('Content-Type', mime_type)
|
||||
response.headers.set('Content-Disposition', f'attachment; filename="{converted_filename}"')
|
||||
response.headers.set('Content-Length', str(os.path.getsize(converted_filepath)))
|
||||
|
||||
logging.debug(f"Sent headers: {response.headers}")
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error during conversion: {str(e)}", exc_info=True)
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@app.route('/clear', methods=['POST'])
|
||||
@token_required
|
||||
def clear_folders():
|
||||
"""
|
||||
Leere alle Dateien in den Upload- und Konvertierungsordnern
|
||||
---
|
||||
tags:
|
||||
- Maintenance
|
||||
parameters:
|
||||
- in: header
|
||||
name: X-API-Token
|
||||
type: string
|
||||
required: true
|
||||
description: API token for authentication
|
||||
responses:
|
||||
200:
|
||||
description: Folders successfully cleared
|
||||
401:
|
||||
description: Unauthorized
|
||||
"""
|
||||
delete_files_in_folder(app.config['UPLOAD_FOLDER'])
|
||||
delete_files_in_folder(app.config['CONVERT_FOLDER'])
|
||||
return jsonify({'message': 'Folders cleared'}), 200
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, host="0.0.0.0", port="9596")
|
||||
45
chnage.py
Normal file
45
chnage.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import subprocess
|
||||
import shutil
|
||||
import os
|
||||
|
||||
def start(input_file, output_extension):
|
||||
def convert_file(input_file, output_file):
|
||||
if not os.path.exists(input_file):
|
||||
print(f"Die Eingabedatei '{input_file}' existiert nicht.")
|
||||
return
|
||||
|
||||
output_dir = os.path.dirname(output_file)
|
||||
if not os.path.exists(output_dir):
|
||||
print(f"Der Ausgabeordner '{output_dir}' existiert nicht.")
|
||||
return
|
||||
|
||||
# Der LibreOffice-Befehl zum Konvertieren der Datei
|
||||
command = [
|
||||
'libreoffice',
|
||||
'--headless',
|
||||
'--convert-to', output_extension,
|
||||
'--outdir', output_dir,
|
||||
input_file
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(command, check=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
print(f"Konvertierung erfolgreich abgeschlossen: {result.stdout.decode()}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Fehler beim Konvertieren: {e.stderr.decode()}")
|
||||
print(f"Ausgabe: {e.stdout.decode()}")
|
||||
|
||||
# Dynamischer Zielpfad für das konvertierte Dokument
|
||||
input_ext = os.path.splitext(input_file)[1].lstrip('.')
|
||||
file_name = os.path.basename(input_file).replace(f'.{input_ext}', '')
|
||||
output_file = os.path.join('convert', f'{file_name}.{output_extension}')
|
||||
|
||||
# Debug-Ausgabe für das Arbeitsverzeichnis
|
||||
print(f"Arbeitsverzeichnis: {os.getcwd()}")
|
||||
print(f"Ausgabeordner: {os.path.dirname(output_file)}")
|
||||
|
||||
convert_file(input_file, output_file)
|
||||
print(f"Die Datei '{input_file}' wurde erfolgreich in '{output_file}' konvertiert.")
|
||||
|
||||
|
||||
|
||||
53
create-alias.sh
Normal file
53
create-alias.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start-Skript ausführbar machen
|
||||
echo "Setting executable permission for start.sh..."
|
||||
chmod +x start.sh
|
||||
|
||||
# Alias für Convert-Commander erstellen und laden
|
||||
echo "Creating alias for Convert-Commander..."
|
||||
echo "alias ccommander='./start.sh'" >> ~/.bash_aliases
|
||||
source ~/.bash_aliases
|
||||
|
||||
# Bash-Completion für ccommander erstellen
|
||||
echo "Setting up bash completion for ccommander..."
|
||||
sudo mkdir -p /etc/bash_completion.d
|
||||
|
||||
# Completion-Skript erstellen
|
||||
sudo tee /etc/bash_completion.d/ccommander-completion.bash > /dev/null << 'EOF'
|
||||
_ccommander_completion() {
|
||||
local cur prev opts sub_opts
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
# Hauptoptionen
|
||||
opts="web api"
|
||||
|
||||
# Unteroptionen basierend auf dem ersten Argument
|
||||
case "${prev}" in
|
||||
"web")
|
||||
sub_opts="start stop status"
|
||||
COMPREPLY=( $(compgen -W "${sub_opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
"api")
|
||||
sub_opts="start stop status token"
|
||||
COMPREPLY=( $(compgen -W "${sub_opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
"ccommander")
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
complete -F _ccommander_completion ccommander
|
||||
EOF
|
||||
|
||||
# Completion-Skript ausführbar machen und aktivieren
|
||||
sudo chmod +x /etc/bash_completion.d/ccommander-completion.bash
|
||||
source /etc/bash_completion.d/ccommander-completion.bash
|
||||
|
||||
echo "Installation completed. Bash completion for ccommander is now active."
|
||||
109
index.py
Normal file
109
index.py
Normal file
@@ -0,0 +1,109 @@
|
||||
from flask import Flask, request, render_template, redirect, url_for, jsonify, send_file
|
||||
import os
|
||||
import chnage
|
||||
from flask_cors import CORS
|
||||
import shutil
|
||||
from threading import Timer
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app, resources={r"/*": {"origins": "*"}})
|
||||
|
||||
UPLOAD_FOLDER = 'uploads'
|
||||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||
|
||||
# Stelle sicher, dass der Upload-Ordner existiert
|
||||
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
|
||||
|
||||
# Globale Variable zur Speicherung von filetest
|
||||
global_filetest = None
|
||||
folder_path_1 = 'uploads'
|
||||
folder_path_2 = 'convert'
|
||||
|
||||
def delete_files_in_folder(folder_path):
|
||||
# Überprüfen, ob der Ordner existiert
|
||||
if os.path.exists(folder_path):
|
||||
# Durch alle Dateien und Unterordner im Ordner iterieren
|
||||
for filename in os.listdir(folder_path):
|
||||
file_path = os.path.join(folder_path, filename)
|
||||
try:
|
||||
# Überprüfen, ob es eine Datei oder ein Ordner ist
|
||||
if os.path.isfile(file_path) or os.path.islink(file_path):
|
||||
os.unlink(file_path) # Datei oder symbolischen Link löschen
|
||||
elif os.path.isdir(file_path):
|
||||
shutil.rmtree(file_path) # Ordner und dessen Inhalt löschen
|
||||
except Exception as e:
|
||||
print(f'Fehler beim Löschen {file_path}. Grund: {e}')
|
||||
else:
|
||||
print(f'Ordner {folder_path} existiert nicht')
|
||||
|
||||
def download_file(filepath, global_filetest):
|
||||
filename = os.path.splitext(os.path.basename(filepath))[0]
|
||||
filethepath = f'convert/{filename}.{global_filetest}'
|
||||
try:
|
||||
print(f"Bereit zum Download: {filethepath}")
|
||||
return send_file(filethepath, as_attachment=True)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
def delete_files_after_delay():
|
||||
delete_files_in_folder(folder_path_1)
|
||||
delete_files_in_folder(folder_path_2)
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def index():
|
||||
global global_filetest
|
||||
|
||||
|
||||
if request.method == 'POST':
|
||||
if 'file' not in request.files:
|
||||
return redirect(url_for('index', status='Keine Datei ausgewählt'))
|
||||
|
||||
file = request.files['file']
|
||||
|
||||
if file.filename == '':
|
||||
return redirect(url_for('index', status='Keine Datei ausgewählt'))
|
||||
|
||||
if file and global_filetest is not None:
|
||||
filepath = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
|
||||
file.save(filepath)
|
||||
|
||||
chnage.start(filepath, global_filetest)
|
||||
|
||||
response = redirect(url_for('download', filename=file.filename))
|
||||
|
||||
Timer(5, delete_files_after_delay).start()
|
||||
|
||||
return response
|
||||
|
||||
|
||||
elif file:
|
||||
return redirect(url_for('index', status='Datei hochgeladen, aber Dateityp nicht ausgewählt'))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return render_template('index.html', status=request.args.get('status'))
|
||||
|
||||
@app.route('/download/<filename>', methods=['GET'])
|
||||
def download(filename):
|
||||
global global_filetest
|
||||
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||
return download_file(filepath, global_filetest)
|
||||
|
||||
|
||||
@app.route('/empfange_daten', methods=['POST'])
|
||||
def empfange_daten():
|
||||
global global_filetest
|
||||
daten = request.json['daten']
|
||||
global_filetest = daten
|
||||
print(f"Empfangene Daten: {daten}")
|
||||
|
||||
return jsonify({"status": "erfolgreich empfangen", "message": "Bitte laden Sie jetzt eine Datei hoch"})
|
||||
|
||||
@app.route('/docs')
|
||||
def doc():
|
||||
return render_template("docs.html")
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, host="0.0.0.0")
|
||||
121
install.sh
Normal file
121
install.sh
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Logo anzeigen
|
||||
cat << "EOF"
|
||||
_____ _ _____ _
|
||||
/ ____| | | / ____| | |
|
||||
| | ___ _ __ __ __ ___ _ __ | |_ ______ | | ___ _ __ ___ _ __ ___ __ _ _ __ __| | ___ _ __
|
||||
| | / _ \ | '_ \ \ / // _ \| '__|| __||______|| | / _ \ | '_ ` _ \ | '_ ` _ \ / _` || '_ \ / _` | / _ \| '__|
|
||||
| |____| (_) || | | |\ V /| __/| | | |_ | |____| (_) || | | | | || | | | | || (_| || | | || (_| || __/| |
|
||||
\_____|\___/ |_| |_| \_/ \___||_| \__| \_____|\___/ |_| |_| |_||_| |_| |_| \__,_||_| |_| \__,_| \___||_|
|
||||
EOF
|
||||
|
||||
echo "Starting Convert-Commander installation..."
|
||||
|
||||
# Funktion zur Fortschrittsanzeige
|
||||
progress_bar() {
|
||||
local progress=$1
|
||||
local total=12
|
||||
local percent=$(( progress * 100 / total ))
|
||||
local completed=$(( percent / 5 ))
|
||||
local remaining=$(( 20 - completed ))
|
||||
|
||||
# Erzeuge den Fortschrittsbalken
|
||||
bar="["
|
||||
for ((i=0; i<$completed; i++)); do
|
||||
bar+="#"
|
||||
done
|
||||
for ((i=0; i<$remaining; i++)); do
|
||||
bar+=" "
|
||||
done
|
||||
bar+="] $percent%"
|
||||
|
||||
# Zeige den Fortschrittsbalken an
|
||||
echo -ne "$bar\r"
|
||||
sleep 1
|
||||
if [ "$progress" -lt "$total" ]; then
|
||||
tput el
|
||||
fi
|
||||
}
|
||||
|
||||
total_steps=11
|
||||
current_step=0
|
||||
|
||||
# Python installieren
|
||||
echo "Installing Python..."
|
||||
sudo apt-get install -y python3.6
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
# pip installieren
|
||||
echo "Installing pip..."
|
||||
sudo apt install -y python3-pip
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
# Virtuelle Umgebung einrichten
|
||||
echo "Setting up virtual environment..."
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
# Flask installieren
|
||||
echo "Installing Flask..."
|
||||
pip install flask
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
# API-Flask installieren
|
||||
echo "Installing API-Flask..."
|
||||
pip install apiflask
|
||||
pip install flask-cors
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
# Swagger UI installieren
|
||||
pip install flask-swagger-ui
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
# LibreOffice installieren
|
||||
echo "Installing LibreOffice..."
|
||||
sudo apt-get install -y libreoffice
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
# PyOO installieren
|
||||
echo "Installing pyoo..."
|
||||
pip install pyoo
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
# gunicorn installieren
|
||||
echo "Installing gunicorn..."
|
||||
pip install gunicorn
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
# jp installieren
|
||||
echo "Installing jp..."
|
||||
sudo apt-get install jq
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
|
||||
# Ordner erstellen
|
||||
echo "Creating folders..."
|
||||
mkdir -p ./convert ./uploads
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
chmod +x create-alias.sh
|
||||
bash create-alias.sh
|
||||
sleep 5
|
||||
source ~/.bashrc
|
||||
((current_step++))
|
||||
progress_bar $current_step
|
||||
|
||||
|
||||
# Fertigstellung anzeigen
|
||||
echo -e "\nConvert-Commander installation completed successfully!"
|
||||
120
start.sh
Normal file
120
start.sh
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Virtuelle Umgebung aktivieren
|
||||
source "$(dirname "$0")/venv/bin/activate"
|
||||
|
||||
# Pfad zu den Python-Skripten
|
||||
PYTHON_SCRIPT_WEB="index:app" # Format: "dateiname:app_name"
|
||||
PYTHON_SCRIPT_API="api:app" # Format: "dateiname:app_name"
|
||||
|
||||
# PID-Dateien für die Gunicorn-Prozesse
|
||||
PID_FILE_WEB="/tmp/gunicorn_web.pid"
|
||||
PID_FILE_API="/tmp/gunicorn_api.pid"
|
||||
|
||||
# Gunicorn-Befehl zum Starten des Prozesses, inklusive Ports und Fehlerprotokollierung
|
||||
GUNICORN_CMD_WEB="gunicorn --bind 0.0.0.0:9595 --daemon --pid $PID_FILE_WEB --error-logfile /tmp/gunicorn_web_error.log"
|
||||
GUNICORN_CMD_API="gunicorn --bind 0.0.0.0:9596 --daemon --pid $PID_FILE_API --error-logfile /tmp/gunicorn_api_error.log"
|
||||
|
||||
# Funktion zum Überprüfen, ob ein Prozess läuft
|
||||
check_status() {
|
||||
local PID_FILE=$1
|
||||
local SERVICE_NAME=$2
|
||||
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
PID=$(cat "$PID_FILE")
|
||||
if ps -p "$PID" > /dev/null; then
|
||||
echo "$SERVICE_NAME läuft (PID: $PID)"
|
||||
else
|
||||
echo "$SERVICE_NAME läuft nicht, PID-Datei gefunden, aber Prozess nicht aktiv"
|
||||
rm "$PID_FILE"
|
||||
fi
|
||||
else
|
||||
echo "$SERVICE_NAME läuft nicht"
|
||||
fi
|
||||
}
|
||||
|
||||
# Starten des Gunicorn-Prozesses
|
||||
start_service() {
|
||||
local SCRIPT_NAME=$1
|
||||
local PID_FILE=$2
|
||||
local GUNICORN_CMD=$3
|
||||
|
||||
if [ -f "$PID_FILE" ] && ps -p "$(cat $PID_FILE)" > /dev/null; then
|
||||
echo "$SCRIPT_NAME läuft bereits"
|
||||
else
|
||||
$GUNICORN_CMD "$SCRIPT_NAME" &
|
||||
echo "$SCRIPT_NAME wurde gestartet"
|
||||
fi
|
||||
}
|
||||
|
||||
# Stoppen des Gunicorn-Prozesses
|
||||
stop_service() {
|
||||
local PID_FILE=$1
|
||||
local SERVICE_NAME=$2
|
||||
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
PID=$(cat "$PID_FILE")
|
||||
kill "$PID" 2>/dev/null
|
||||
rm "$PID_FILE"
|
||||
echo "$SERVICE_NAME wurde gestoppt"
|
||||
else
|
||||
echo "$SERVICE_NAME läuft nicht"
|
||||
fi
|
||||
}
|
||||
|
||||
# Funktion zum Ausführen eines Bash-Skripts
|
||||
run_bash_script() {
|
||||
local SCRIPT_NAME=$1
|
||||
if [ -x "$SCRIPT_NAME" ]; then
|
||||
./"$SCRIPT_NAME"
|
||||
else
|
||||
echo "Das Skript '$SCRIPT_NAME' ist nicht ausführbar oder nicht gefunden."
|
||||
fi
|
||||
}
|
||||
|
||||
# Hauptlogik zur Auswahl des Dienstes und der Aktion
|
||||
case "$1" in
|
||||
web)
|
||||
case "$2" in
|
||||
start)
|
||||
start_service "$PYTHON_SCRIPT_WEB" "$PID_FILE_WEB" "$GUNICORN_CMD_WEB"
|
||||
;;
|
||||
stop)
|
||||
stop_service "$PID_FILE_WEB" "Web-Dienst"
|
||||
;;
|
||||
status)
|
||||
check_status "$PID_FILE_WEB" "Web-Dienst"
|
||||
;;
|
||||
*)
|
||||
echo "Verwendung: $0 {web|api} {start|stop|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
api)
|
||||
case "$2" in
|
||||
start)
|
||||
start_service "$PYTHON_SCRIPT_API" "$PID_FILE_API" "$GUNICORN_CMD_API"
|
||||
;;
|
||||
stop)
|
||||
stop_service "$PID_FILE_API" "API-Dienst"
|
||||
;;
|
||||
status)
|
||||
check_status "$PID_FILE_API" "API-Dienst"
|
||||
;;
|
||||
token)
|
||||
run_bash_script "tokenapi.sh"
|
||||
;;
|
||||
*)
|
||||
echo "Verwendung: $0 {web|api} {start|stop|status|token}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Verwendung: $0 {web|api} {start|stop|status|token}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
BIN
static/favicon.ico
Normal file
BIN
static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
146
static/script.js
Normal file
146
static/script.js
Normal file
@@ -0,0 +1,146 @@
|
||||
var elementsDown2 = document.getElementsByClassName("dropdown-content2");
|
||||
var value = "";
|
||||
var valuename = "";
|
||||
var textGuppe = [".docx", ".txt", ".odt", ".html", ".htm", ".doc", ".epub"];
|
||||
var tabelleGruppe = [".xls", ".xlsx", ".ods"];
|
||||
var persentGruppe = [".ppt", ".pptx", ".odp"];
|
||||
|
||||
function filterFunction(dropdownClass) {
|
||||
const input = document.querySelector(`.${dropdownClass} .suche`);
|
||||
const filter = input.value.toLowerCase();
|
||||
const pElements = document.querySelectorAll(`.${dropdownClass} p`);
|
||||
|
||||
// Alle p-Elemente zuerst wieder sichtbar machen
|
||||
pElements.forEach(function (item) {
|
||||
item.style.display = "flex";
|
||||
});
|
||||
|
||||
// Dann nur die Elemente filtern, die der Suchabfrage entsprechen
|
||||
pElements.forEach(function (item) {
|
||||
const textValue = item.textContent.toLowerCase(); // Verwenden nur von textContent für bessere Kompatibilität
|
||||
if (textValue.indexOf(filter) == -1) {
|
||||
item.style.display = "none";
|
||||
} else {
|
||||
item.style.display = "flex";
|
||||
}
|
||||
});
|
||||
|
||||
// Führe 'meineFunktion' nur aus, wenn das Suchfeld leer ist
|
||||
if (elementsDown2.length > 0 && window.getComputedStyle(elementsDown2[0]).display === "flex" && input.value.trim() === "") {
|
||||
meineFunktion();
|
||||
}
|
||||
}
|
||||
|
||||
function setFileFunction(name, filename) {
|
||||
value = name;
|
||||
valuename = filename;
|
||||
console.log(value, valuename);
|
||||
|
||||
if (
|
||||
elementsDown2.length > 0 &&
|
||||
window.getComputedStyle(elementsDown2[0]).display === "flex"
|
||||
) {
|
||||
var dropbtn2 = document.getElementsByClassName("dropbtn2");
|
||||
if (dropbtn2.length > 0) {
|
||||
dropbtn2[0].innerHTML = valuename;
|
||||
}
|
||||
}
|
||||
|
||||
return [value, valuename];
|
||||
}
|
||||
|
||||
document.getElementById('fileInput').addEventListener('change', function(event) {
|
||||
const file = event.target.files[0];
|
||||
|
||||
if (file) {
|
||||
const fileName = file.name; // Holt den Dateinamen
|
||||
const fileExtension = '.' + fileName.split('.').pop(); // Holt die Dateiendung mit Punkt
|
||||
|
||||
console.log(`Dateiendung: ${fileExtension}`);
|
||||
meineFunktion(fileExtension); // Funktion zum Steuern der Anzeige aufrufen
|
||||
} else {
|
||||
console.log('Keine Datei ausgewählt.');
|
||||
}
|
||||
});
|
||||
|
||||
function meineFunktion(name) {
|
||||
var elementsText = document.getElementsByClassName("text");
|
||||
var elementsExel = document.getElementsByClassName("exel");
|
||||
var elementsPPT = document.getElementsByClassName("ppt");
|
||||
|
||||
if (textGuppe.includes(name)) {
|
||||
for (var i = 0; i < elementsExel.length; i++) {
|
||||
elementsExel[i].style.display = "none";
|
||||
}
|
||||
for (var i = 0; i < elementsPPT.length; i++) {
|
||||
elementsPPT[i].style.display = "none";
|
||||
}
|
||||
} else if (tabelleGruppe.includes(name)) {
|
||||
for (var i = 0; i < elementsPPT.length; i++) {
|
||||
elementsPPT[i].style.display = "none";
|
||||
}
|
||||
for (var i = 0; i < elementsText.length; i++) {
|
||||
elementsText[i].style.display = "none";
|
||||
}
|
||||
} else if (persentGruppe.includes(name)) {
|
||||
for (var i = 0; i < elementsText.length; i++) {
|
||||
elementsText[i].style.display = "none";
|
||||
}
|
||||
for (var i = 0; i < elementsExel.length; i++) {
|
||||
elementsExel[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sendData() {
|
||||
fetch('/empfange_daten', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ daten: valuename })
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Netzwerkantwort war nicht ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Erfolg:', data);
|
||||
// Hier können Sie das Formular manuell absenden
|
||||
document.querySelector('form').submit();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Fehler:', error);
|
||||
// Hier können Sie dem Benutzer eine Fehlermeldung anzeigen
|
||||
});
|
||||
}
|
||||
document.querySelectorAll('.dropdown2').forEach(dropdown => {
|
||||
dropdown.addEventListener('mouseenter', () => {
|
||||
const dropdownContent = dropdown.querySelector('.dropdown-content2');
|
||||
dropdownContent.style.maxHeight = '300px';
|
||||
dropdownContent.style.opacity = '1';
|
||||
// Breite wird jetzt durch CSS-Transition geregelt
|
||||
});
|
||||
|
||||
dropdown.addEventListener('mouseleave', () => {
|
||||
const dropdownContent = dropdown.querySelector('.dropdown-content2');
|
||||
dropdownContent.style.maxHeight = '0';
|
||||
dropdownContent.style.opacity = '0';
|
||||
// Breite wird jetzt durch CSS-Transition geregelt
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
function updateFileName() {
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const fileLabel = document.getElementById('fileLabel');
|
||||
|
||||
if (fileInput.files.length > 0) {
|
||||
fileLabel.textContent = fileInput.files[0].name;
|
||||
} else {
|
||||
fileLabel.textContent = 'Datei auswählen';
|
||||
}
|
||||
}
|
||||
265
static/style.css
Normal file
265
static/style.css
Normal file
@@ -0,0 +1,265 @@
|
||||
body {
|
||||
background-color: #1B1B1B;
|
||||
color: white;
|
||||
font-family: Arial, sans-serif;
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.logo,
|
||||
.link {
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
text-align: left;
|
||||
color: #94EBEB;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-align: right;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.link a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.link a:hover {
|
||||
color: #a0a0a0;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.button-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: inline-block;
|
||||
padding: 12px 24px;
|
||||
background-color: #94EBEB;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
color: #1B1B1B;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
min-width: 150px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.buttons:hover {
|
||||
background-color: #7CD1D1;
|
||||
transform: translateY(0px);
|
||||
box-shadow: 0 4px 8px rgba(148, 235, 235, 0.2);
|
||||
}
|
||||
|
||||
.dropdown2 {
|
||||
position: relative;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
width: 120%;
|
||||
}
|
||||
|
||||
.dropdown-content2 {
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
top: 115%;
|
||||
left: 0;
|
||||
background-color: #2A2A2A;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.3);
|
||||
z-index: 1;
|
||||
width: 90%;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-out, top 0.2s ease-out;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #94EBEB #2A2A2A;
|
||||
}
|
||||
|
||||
.dropdown2:hover .dropdown-content2 {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
top: 100%;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.dropbtn2 {
|
||||
background-color: #b0f1f1;
|
||||
}
|
||||
|
||||
.dropdown2:hover .dropbtn2 {
|
||||
background-color: #7CD1D1;
|
||||
transform: translateY(0px);
|
||||
box-shadow: 0 4px 8px rgba(148, 235, 235, 0.2);
|
||||
}
|
||||
|
||||
.dropdown-content2::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.dropdown-content2::-webkit-scrollbar-track {
|
||||
background: #2A2A2A;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.dropdown-content2::-webkit-scrollbar-thumb {
|
||||
background-color: #94EBEB;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.dropdown-content2 p {
|
||||
color: #FFFFFF;
|
||||
padding: 8px 12px;
|
||||
margin: 4px;
|
||||
text-decoration: none;
|
||||
background-color: #3A3A3A;
|
||||
border-radius: 5px;
|
||||
flex: 1 0 calc(33.33% - 8px);
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
transition: background-color 0.3s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.dropdown-content2 p:hover {
|
||||
background-color: #4A4A4A;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
#file {
|
||||
border-radius: 10px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
#fileInput {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.suche {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 8px;
|
||||
background-color: #2A2A2A;
|
||||
border: 1px solid #000000;
|
||||
color: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.suche::placeholder {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
@media (min-width: 600px){
|
||||
.buttons:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.dropdown2:hover .dropbtn2 {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
body{
|
||||
width: 95%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.main {
|
||||
padding: 20px 10px;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button-wrapper {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
width: 95%;
|
||||
min-width: unset;
|
||||
font-size: 16px;
|
||||
padding: 15px 10px;
|
||||
}
|
||||
|
||||
.dropdown-content2 {
|
||||
width: 95%;
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.dropdown-content2 p {
|
||||
flex: 1 0 calc(50% - 8px);
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
#submitButton {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
.dropdown2:hover .dropdown-content2 {
|
||||
overflow-y: auto;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (max-width: 415px) {
|
||||
#submitButton {
|
||||
width: 101%;
|
||||
}
|
||||
|
||||
.dropdown-content2 p {
|
||||
flex: 1 0 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 364px) {
|
||||
#submitButton {
|
||||
width: 102%;
|
||||
}
|
||||
}
|
||||
75
static/swagger.json
Normal file
75
static/swagger.json
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "Convert-Commander API",
|
||||
"version": "1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/upload": {
|
||||
"post": {
|
||||
"tags": ["File Conversion"],
|
||||
"summary": "Upload a file and convert it to the specified format",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "API token for authentication"
|
||||
},
|
||||
{
|
||||
"name": "file",
|
||||
"in": "formData",
|
||||
"required": true,
|
||||
"type": "file",
|
||||
"description": "The file to upload"
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"in": "formData",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "The target format for conversion"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successfully converted file"
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request"
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
},
|
||||
"500": {
|
||||
"description": "Server error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/clear": {
|
||||
"post": {
|
||||
"tags": ["Maintenance"],
|
||||
"summary": "Clear all files in the upload and convert folders",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-API-Token",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "API token for authentication"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Folders successfully cleared"
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
190
templates/docs.html
Normal file
190
templates/docs.html
Normal file
@@ -0,0 +1,190 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Docs</title>
|
||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<h3>Convert-Commander</h3>
|
||||
</div>
|
||||
<div class="link">
|
||||
<a href="/">Convert</a>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<table>
|
||||
<h5>Which file can you convert?</h5>
|
||||
<tr>
|
||||
<td>
|
||||
<p>.docs/.doc</p>
|
||||
<p>.txt</p>
|
||||
<p>.odt</p>
|
||||
<p>.html/.htm</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>to</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>.docs/.doc</p>
|
||||
<p>.txt</p>
|
||||
<p>.odt</p>
|
||||
<p>.html/.htm</p>
|
||||
<p>.epub</p>
|
||||
<p>.pdf</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<p>.xlsx/.xls</p>
|
||||
<p>.ods</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>to</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>.xlsx/.xls</p>
|
||||
<p>.ods</p>
|
||||
<p>.pdf</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<p>.pptx/.ppt</p>
|
||||
<p>.odp</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>to</p>
|
||||
</td>
|
||||
<td>
|
||||
<p>.pptx/.ppt</p>
|
||||
<p>.odp</p>
|
||||
<p>.pdf</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<div class="exp">
|
||||
<h5>What are the files?</h5>
|
||||
<p><b>.docs:</b> This is not a standard file extension, likely a typo for .docx, which is a Microsoft Word document format.</p>
|
||||
<p><b>.doc:</b> A Microsoft Word document format, commonly used for word processing.</p>
|
||||
<p><b>.txt:</b> A plain text file that contains unformatted text.</p>
|
||||
<p><b>.odt:</b> An OpenDocument Text file, typically used by OpenOffice and LibreOffice for word processing.</p>
|
||||
<p><b>.html:</b> A HyperText Markup Language file, used to structure content on the web.</p>
|
||||
<p><b>.htm:</b> A variant of the .html extension, also used for web pages.</p>
|
||||
<p><b>.epub:</b> An electronic publication (ePub) file, commonly used for eBooks and supported by many eReader devices and applications.</p>
|
||||
<p><b>.xlsx:</b> A Microsoft Excel file format, used for spreadsheets.</p>
|
||||
<p><b>.xls:</b> An older Microsoft Excel file format, also used for spreadsheets.</p>
|
||||
<p><b>.pdf:</b> A Portable Document Format, widely used for sharing formatted documents.</p>
|
||||
<p><b>.ods:</b> An OpenDocument Spreadsheet file, used by OpenOffice and LibreOffice for spreadsheets.</p>
|
||||
<p><b>.odp:</b> An OpenDocument Presentation file, used by OpenOffice and LibreOffice for presentations.</p>
|
||||
<p><b>.pptx:</b> A Microsoft PowerPoint presentation format, used for creating slideshows.</p>
|
||||
<p><b>.ppt:</b> An older Microsoft PowerPoint format, also used for presentations.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
<style>
|
||||
body {
|
||||
background-color: #1B1B1B;
|
||||
color: white;
|
||||
font-family: Arial, sans-serif;
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
/* Zentriert den Inhalt */
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.logo,
|
||||
.link {
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
font-size: 20px;
|
||||
/* Einheitliche Schriftgröße */
|
||||
}
|
||||
|
||||
.logo {
|
||||
text-align: left;
|
||||
color: #94EBEB;
|
||||
}
|
||||
|
||||
.link {
|
||||
text-align: right;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.link a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.link a:hover {
|
||||
color: #a0a0a0;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
/* Entfernt Lücken zwischen den Zellen */
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
h5{
|
||||
font-size: 25px;
|
||||
color: #94EBEB;
|
||||
text-align: center;
|
||||
}
|
||||
table td,
|
||||
table tr {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid #94EBEB;
|
||||
/* Fügt eine weiße Linie unter jeder Zelle hinzu */
|
||||
}
|
||||
|
||||
tr p {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.exp {
|
||||
margin-top: 50px;
|
||||
margin-left: 11%;
|
||||
margin-right: 11%;
|
||||
}
|
||||
|
||||
.exp p {
|
||||
font-size: 20px;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
body{
|
||||
width: 95%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.exp {
|
||||
margin-left: 5%;
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
||||
62
templates/index.html
Normal file
62
templates/index.html
Normal file
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Dropdown Menu</title>
|
||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<h3>Convert-Commander</h3>
|
||||
</div>
|
||||
<div class="link">
|
||||
<a href="/docs">docs</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<h1>Convert-Commander</h1>
|
||||
<div class="button-container">
|
||||
<div class="button-wrapper">
|
||||
<form action="/" method="post" enctype="multipart/form-data">
|
||||
<label class="buttons" for="fileInput" id="fileLabel">Select File</label>
|
||||
<input type="file" name="file" id="fileInput" required onchange="updateFileName()">
|
||||
</div>
|
||||
|
||||
<div class="button-wrapper dropdown2" onmouseover="meineFunktion()">
|
||||
<div class="buttons dropbtn2" onclick="event.preventDefault(); meineFunktion()" id="file">File Type</div>
|
||||
<div class="dropdown-content2">
|
||||
<input class="suche" type="text" onkeyup="filterFunction('dropdown-content2')" placeholder="Search...">
|
||||
<p class="text" onclick="setFileFunction('text', 'txt')">txt</p>
|
||||
<p class="text" onclick="setFileFunction('text', 'odt')">odt</p>
|
||||
<p class="text" onclick="setFileFunction('text', 'html')">html</p>
|
||||
<p class="text" onclick="setFileFunction('text', 'htm')">htm</p>
|
||||
<p class="text" onclick="setFileFunction('text', 'doc')">doc</p>
|
||||
<p class="text" onclick="setFileFunction('text', 'docx')">docx</p>
|
||||
<p class="text" onclick="setFileFunction('text', 'epub')">epub</p>
|
||||
<p class="none" onclick="setFileFunction('none', 'pdf')">pdf</p>
|
||||
<p class="ppt" onclick="setFileFunction('ppt', 'pptx')">pptx</p>
|
||||
<p class="ppt" onclick="setFileFunction('ppt', 'ppt')">ppt</p>
|
||||
<p class="ppt" onclick="setFileFunction('ppt', 'odp')">odp</p>
|
||||
<p class="exel" onclick="setFileFunction('exel', 'xls')">xls</p>
|
||||
<p class="exel" onclick="setFileFunction('exel', 'xlsx')">xlsx</p>
|
||||
<p class="exel" onclick="setFileFunction('exel', 'ods')">ods</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-wrapper">
|
||||
<input class="buttons" type="button" value="Upload" id="submitButton" onclick="sendData()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p id="upload-status">
|
||||
{% if status %}
|
||||
{{ status }}
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
25
tokenapi.sh
Normal file
25
tokenapi.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# URL of your Flask application
|
||||
API_URL="http://localhost:5001"
|
||||
|
||||
# Make a POST request to the /generate_token endpoint
|
||||
response=$(curl -s -X POST "${API_URL}/generate_token")
|
||||
|
||||
# Check if the request was successful
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error: Unable to connect to the API."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract the token from the JSON response using jq
|
||||
token=$(echo "$response" | jq -r '.token')
|
||||
|
||||
# Check if the token was extracted successfully
|
||||
if [[ $token == "null" ]]; then
|
||||
echo "Error: Token not found in the response."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Output the token
|
||||
echo "Token: $token"
|
||||
Reference in New Issue
Block a user