Add 6 new tools: Hasher, Base64, JWT Decoder, Password Gen, Timestamp, Text Diff
- backend/tools/hasher.py: POST /api/hash/sha256 and /api/hash/bcrypt (bcrypt added to requirements) - backend/tools/base64tool.py: POST /api/base64/encode and /api/base64/decode - backend/tools/jwtdecoder.py: POST /api/jwt/decode (signature verification disabled) - backend/tools/passwordgen.py: POST /api/password/generate with charset and length options - backend/tools/timestamp.py: POST /api/timestamp/convert (unix<->date, ISO 8601 + German format) - backend/tools/textdiff.py: POST /api/text/diff returning structured added/removed/unchanged lines - All blueprints registered in app.py and tools/__init__.py - React components with copy button, dark/light mode support via CSS variables - ToolOverview rebuilt as card grid; App.jsx routes added for all tools Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-1
@@ -10,7 +10,15 @@ from util.db_config import is_configured, load_config, test_connection
|
||||
from util.setup_routes import setup_blueprint
|
||||
from util.limiter import limiter
|
||||
from auth import auth_bp
|
||||
from tools import md5_blueprint
|
||||
from tools import (
|
||||
md5_blueprint,
|
||||
hasher_blueprint,
|
||||
base64_blueprint,
|
||||
jwt_decoder_blueprint,
|
||||
passwordgen_blueprint,
|
||||
timestamp_blueprint,
|
||||
textdiff_blueprint,
|
||||
)
|
||||
from admin import admin_bp
|
||||
|
||||
app = Flask(__name__, template_folder="templates")
|
||||
@@ -20,6 +28,12 @@ limiter.init_app(app)
|
||||
app.register_blueprint(setup_blueprint)
|
||||
app.register_blueprint(auth_bp)
|
||||
app.register_blueprint(md5_blueprint)
|
||||
app.register_blueprint(hasher_blueprint)
|
||||
app.register_blueprint(base64_blueprint)
|
||||
app.register_blueprint(jwt_decoder_blueprint)
|
||||
app.register_blueprint(passwordgen_blueprint)
|
||||
app.register_blueprint(timestamp_blueprint)
|
||||
app.register_blueprint(textdiff_blueprint)
|
||||
app.register_blueprint(admin_bp)
|
||||
|
||||
# 🌐 React-Frontend ausliefern
|
||||
|
||||
@@ -4,4 +4,5 @@ flask-limiter
|
||||
mysql-connector-python
|
||||
werkzeug>=2.3
|
||||
PyJWT
|
||||
bcrypt
|
||||
python-dotenv
|
||||
@@ -1 +1,7 @@
|
||||
from .md5 import md5_blueprint
|
||||
from .hasher import hasher_blueprint
|
||||
from .base64tool import base64_blueprint
|
||||
from .jwtdecoder import jwt_decoder_blueprint
|
||||
from .passwordgen import passwordgen_blueprint
|
||||
from .timestamp import timestamp_blueprint
|
||||
from .textdiff import textdiff_blueprint
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
import base64
|
||||
from util.logger import logger
|
||||
from auth.token import verify_token
|
||||
|
||||
base64_blueprint = Blueprint('base64_tool', __name__)
|
||||
|
||||
|
||||
@base64_blueprint.route('/api/base64/encode', methods=['POST'])
|
||||
def encode():
|
||||
user = verify_token()
|
||||
if not user:
|
||||
return jsonify({"message": "Nicht autorisiert"}), 401
|
||||
try:
|
||||
data = request.get_json()
|
||||
text = data.get("text", "")
|
||||
result = base64.b64encode(text.encode()).decode()
|
||||
logger.info(f"Base64 kodiert von {user['username']}")
|
||||
return jsonify({"result": result})
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler Base64 encode: {e}")
|
||||
return jsonify({"message": "Fehler beim Kodieren"}), 500
|
||||
|
||||
|
||||
@base64_blueprint.route('/api/base64/decode', methods=['POST'])
|
||||
def decode():
|
||||
user = verify_token()
|
||||
if not user:
|
||||
return jsonify({"message": "Nicht autorisiert"}), 401
|
||||
try:
|
||||
data = request.get_json()
|
||||
text = data.get("text", "").strip()
|
||||
result = base64.b64decode(text.encode()).decode('utf-8')
|
||||
logger.info(f"Base64 dekodiert von {user['username']}")
|
||||
return jsonify({"result": result})
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler Base64 decode: {e}")
|
||||
return jsonify({"message": "Ungültiger Base64-Input"}), 400
|
||||
@@ -0,0 +1,39 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
import hashlib
|
||||
import bcrypt
|
||||
from util.logger import logger
|
||||
from auth.token import verify_token
|
||||
|
||||
hasher_blueprint = Blueprint('hasher_tool', __name__)
|
||||
|
||||
|
||||
@hasher_blueprint.route('/api/hash/sha256', methods=['POST'])
|
||||
def hash_sha256():
|
||||
user = verify_token()
|
||||
if not user:
|
||||
return jsonify({"message": "Nicht autorisiert"}), 401
|
||||
try:
|
||||
data = request.get_json()
|
||||
text = data.get("text", "")
|
||||
result = hashlib.sha256(text.encode()).hexdigest()
|
||||
logger.info(f"SHA256 erstellt von {user['username']}")
|
||||
return jsonify({"sha256": result, "by": user['username']})
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler SHA256: {e}")
|
||||
return jsonify({"message": "Fehler beim Hashen"}), 500
|
||||
|
||||
|
||||
@hasher_blueprint.route('/api/hash/bcrypt', methods=['POST'])
|
||||
def hash_bcrypt():
|
||||
user = verify_token()
|
||||
if not user:
|
||||
return jsonify({"message": "Nicht autorisiert"}), 401
|
||||
try:
|
||||
data = request.get_json()
|
||||
text = data.get("text", "")
|
||||
hashed = bcrypt.hashpw(text.encode(), bcrypt.gensalt()).decode()
|
||||
logger.info(f"bcrypt erstellt von {user['username']}")
|
||||
return jsonify({"bcrypt": hashed, "by": user['username']})
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler bcrypt: {e}")
|
||||
return jsonify({"message": "Fehler beim Hashen"}), 500
|
||||
@@ -0,0 +1,29 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
import jwt
|
||||
from datetime import datetime, timezone
|
||||
from util.logger import logger
|
||||
from auth.token import verify_token
|
||||
|
||||
jwt_decoder_blueprint = Blueprint('jwt_decoder_tool', __name__)
|
||||
|
||||
|
||||
@jwt_decoder_blueprint.route('/api/jwt/decode', methods=['POST'])
|
||||
def decode_jwt():
|
||||
user = verify_token()
|
||||
if not user:
|
||||
return jsonify({"message": "Nicht autorisiert"}), 401
|
||||
try:
|
||||
data = request.get_json()
|
||||
token = data.get("token", "").strip()
|
||||
header = jwt.get_unverified_header(token)
|
||||
payload = jwt.decode(token, options={"verify_signature": False})
|
||||
|
||||
expired = False
|
||||
if "exp" in payload:
|
||||
expired = payload["exp"] < datetime.now(timezone.utc).timestamp()
|
||||
|
||||
logger.info(f"JWT dekodiert von {user['username']}")
|
||||
return jsonify({"header": header, "payload": payload, "expired": expired})
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler JWT decode: {e}")
|
||||
return jsonify({"message": "Ungültiger JWT Token"}), 400
|
||||
@@ -0,0 +1,41 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
import secrets
|
||||
import string
|
||||
from util.logger import logger
|
||||
from auth.token import verify_token
|
||||
|
||||
passwordgen_blueprint = Blueprint('passwordgen_tool', __name__)
|
||||
|
||||
|
||||
@passwordgen_blueprint.route('/api/password/generate', methods=['POST'])
|
||||
def generate_password():
|
||||
user = verify_token()
|
||||
if not user:
|
||||
return jsonify({"message": "Nicht autorisiert"}), 401
|
||||
try:
|
||||
data = request.get_json()
|
||||
length = min(max(int(data.get("length", 16)), 8), 64)
|
||||
use_uppercase = data.get("uppercase", True)
|
||||
use_lowercase = data.get("lowercase", True)
|
||||
use_numbers = data.get("numbers", True)
|
||||
use_symbols = data.get("symbols", False)
|
||||
|
||||
charset = ""
|
||||
if use_uppercase:
|
||||
charset += string.ascii_uppercase
|
||||
if use_lowercase:
|
||||
charset += string.ascii_lowercase
|
||||
if use_numbers:
|
||||
charset += string.digits
|
||||
if use_symbols:
|
||||
charset += string.punctuation
|
||||
|
||||
if not charset:
|
||||
return jsonify({"message": "Mindestens ein Zeichensatz muss ausgewählt sein"}), 400
|
||||
|
||||
password = ''.join(secrets.choice(charset) for _ in range(length))
|
||||
logger.info(f"Passwort generiert von {user['username']}")
|
||||
return jsonify({"password": password})
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler Passwortgenerator: {e}")
|
||||
return jsonify({"message": "Fehler beim Generieren"}), 500
|
||||
@@ -0,0 +1,37 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
import difflib
|
||||
from util.logger import logger
|
||||
from auth.token import verify_token
|
||||
|
||||
textdiff_blueprint = Blueprint('textdiff_tool', __name__)
|
||||
|
||||
|
||||
@textdiff_blueprint.route('/api/text/diff', methods=['POST'])
|
||||
def text_diff():
|
||||
user = verify_token()
|
||||
if not user:
|
||||
return jsonify({"message": "Nicht autorisiert"}), 401
|
||||
try:
|
||||
data = request.get_json()
|
||||
text1 = data.get("text1", "")
|
||||
text2 = data.get("text2", "")
|
||||
|
||||
lines1 = text1.splitlines(keepends=True)
|
||||
lines2 = text2.splitlines(keepends=True)
|
||||
|
||||
result = []
|
||||
for line in difflib.unified_diff(lines1, lines2, lineterm=''):
|
||||
if line.startswith('+++') or line.startswith('---') or line.startswith('@@'):
|
||||
continue
|
||||
if line.startswith('+'):
|
||||
result.append({"type": "added", "text": line[1:]})
|
||||
elif line.startswith('-'):
|
||||
result.append({"type": "removed", "text": line[1:]})
|
||||
else:
|
||||
result.append({"type": "unchanged", "text": line[1:] if line.startswith(' ') else line})
|
||||
|
||||
logger.info(f"Text-Diff erstellt von {user['username']}")
|
||||
return jsonify({"diff": result})
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler Text-Diff: {e}")
|
||||
return jsonify({"message": "Fehler beim Vergleich"}), 500
|
||||
@@ -0,0 +1,46 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from datetime import datetime, timezone
|
||||
from util.logger import logger
|
||||
from auth.token import verify_token
|
||||
|
||||
timestamp_blueprint = Blueprint('timestamp_tool', __name__)
|
||||
|
||||
_DATE_FORMATS = [
|
||||
"%Y-%m-%dT%H:%M:%S",
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
"%Y-%m-%d",
|
||||
"%d.%m.%Y %H:%M:%S",
|
||||
"%d.%m.%Y",
|
||||
]
|
||||
|
||||
|
||||
@timestamp_blueprint.route('/api/timestamp/convert', methods=['POST'])
|
||||
def convert_timestamp():
|
||||
user = verify_token()
|
||||
if not user:
|
||||
return jsonify({"message": "Nicht autorisiert"}), 401
|
||||
try:
|
||||
data = request.get_json()
|
||||
value = data.get("value", "").strip()
|
||||
direction = data.get("direction", "unix_to_date")
|
||||
|
||||
if direction == "unix_to_date":
|
||||
ts = float(value)
|
||||
dt_utc = datetime.fromtimestamp(ts, tz=timezone.utc)
|
||||
return jsonify({"utc": dt_utc.isoformat(), "unix": int(ts)})
|
||||
|
||||
dt = None
|
||||
for fmt in _DATE_FORMATS:
|
||||
try:
|
||||
dt = datetime.strptime(value, fmt).replace(tzinfo=timezone.utc)
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
if dt is None:
|
||||
return jsonify({"message": "Ungültiges Datumsformat"}), 400
|
||||
|
||||
logger.info(f"Timestamp konvertiert von {user['username']}")
|
||||
return jsonify({"unix": int(dt.timestamp()), "utc": dt.isoformat()})
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler Timestamp: {e}")
|
||||
return jsonify({"message": "Ungültiger Wert"}), 400
|
||||
Reference in New Issue
Block a user