Initial commit
This commit is contained in:
parent
387bc056b9
commit
df8c2313a9
275 changed files with 12939 additions and 263 deletions
230
app/routes.py
230
app/routes.py
|
|
@ -1,18 +1,16 @@
|
|||
import time
|
||||
import csv
|
||||
import os
|
||||
import threading
|
||||
from flask import Blueprint, request, redirect, url_for, flash, render_template, send_file, current_app
|
||||
from flask import Blueprint, request, redirect, url_for, flash, render_template, send_file, jsonify, current_app
|
||||
from flask_login import login_user, logout_user, login_required, current_user
|
||||
from werkzeug.utils import secure_filename
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from .models import db, User, Job
|
||||
from .webcrawler import process_file # Importiere die Funktion für das Webscraping
|
||||
from .models import db, User, Job, AppConfig
|
||||
from .webcrawler import process_file
|
||||
|
||||
UPLOAD_FOLDER = 'uploads'
|
||||
RESULT_FOLDER = 'results'
|
||||
UPLOAD_FOLDER = '/app/uploads'
|
||||
RESULT_FOLDER = '/app/results'
|
||||
|
||||
# Blueprint für auth erstellen
|
||||
bp = Blueprint('auth', __name__)
|
||||
|
||||
@bp.route('/login', methods=['GET', 'POST'])
|
||||
|
|
@ -29,19 +27,19 @@ def login():
|
|||
|
||||
@bp.route('/signup', methods=['GET', 'POST'])
|
||||
def signup():
|
||||
if not current_app.config['ALLOW_USER_SIGNUP']:
|
||||
cfg = AppConfig.query.filter_by(key='allow_signup').first()
|
||||
if not cfg or cfg.value != 'true':
|
||||
flash("Registrierung ist derzeit deaktiviert.")
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
password = generate_password_hash(request.form['password'], method='sha256')
|
||||
password = generate_password_hash(request.form['password']) # ✅ Fix
|
||||
new_user = User(username=username, password=password)
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
flash('Benutzer erfolgreich erstellt! Sie können sich jetzt einloggen.')
|
||||
flash('Benutzer erfolgreich erstellt!')
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
return render_template('signup.html')
|
||||
|
||||
@bp.route('/logout')
|
||||
|
|
@ -53,171 +51,203 @@ def logout():
|
|||
@bp.route('/jobs')
|
||||
@login_required
|
||||
def job_status():
|
||||
jobs = Job.query.filter_by(user_id=current_user.id).all()
|
||||
jobs = Job.query.filter_by(user_id=current_user.id).order_by(Job.created_at.desc()).all()
|
||||
return render_template('jobs.html', jobs=jobs)
|
||||
|
||||
@bp.route('/upload', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def upload():
|
||||
if request.method == 'POST':
|
||||
file = request.files['file']
|
||||
filename = secure_filename(file.filename)
|
||||
|
||||
# Überprüfen, ob eine Datei mit dem gleichen Namen bereits existiert
|
||||
file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
|
||||
if os.path.exists(file_path):
|
||||
# Wenn eine Datei mit dem gleichen Namen existiert, einen Zeitstempel hinzufügen
|
||||
name, ext = os.path.splitext(filename)
|
||||
timestamp = time.strftime("%Y%m%d-%H%M%S") # Zeitstempel im Format JahrMonatTag-StundenMinutenSekunden
|
||||
filename = f"{name}_{timestamp}{ext}"
|
||||
file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
|
||||
flash(f"Eine Datei mit gleichem Namen existierte bereits. Die Datei wurde als '{filename}' gespeichert.")
|
||||
|
||||
# Speichern der Datei
|
||||
file.save(file_path)
|
||||
flash('Datei erfolgreich hochgeladen und Job gestartet')
|
||||
if 'file' not in request.files:
|
||||
flash('Keine Datei ausgewählt.')
|
||||
return redirect(url_for('auth.upload'))
|
||||
|
||||
# Neuen Job erstellen
|
||||
new_job = Job(user_id=current_user.id, filename=filename, status="Pending")
|
||||
file = request.files['file']
|
||||
if not file or file.filename == '':
|
||||
flash('Keine gültige Datei.')
|
||||
return redirect(url_for('auth.upload'))
|
||||
|
||||
filename = secure_filename(file.filename)
|
||||
name, ext = os.path.splitext(filename)
|
||||
|
||||
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
||||
unique_filename = f"{name}_{timestamp}{ext}" if os.path.exists(os.path.join(UPLOAD_FOLDER, filename)) else filename
|
||||
|
||||
filepath = os.path.join(UPLOAD_FOLDER, unique_filename)
|
||||
file.save(filepath)
|
||||
print(f"💾 UPLOAD: {filepath}")
|
||||
|
||||
new_job = Job(
|
||||
user_id=current_user.id,
|
||||
filename=unique_filename,
|
||||
status="Pending"
|
||||
)
|
||||
db.session.add(new_job)
|
||||
db.session.commit()
|
||||
print(f"🆕 JOB #{new_job.id} für User {current_user.id}")
|
||||
|
||||
# Debugging-Ausgabe zur Überprüfung der Thread-Erstellung
|
||||
print(f"Starte Scraping-Thread für Job-ID: {new_job.id}")
|
||||
|
||||
# Starten des Scraping im Hintergrund-Thread und Übergeben des aktuellen Anwendungskontexts
|
||||
thread = threading.Thread(target=process_file, args=(filename, new_job.id, current_app._get_current_object()))
|
||||
thread = threading.Thread(
|
||||
target=process_file,
|
||||
args=(unique_filename, new_job.id, current_app._get_current_object())
|
||||
)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
print(f"🔄 THREAD STARTED Job {new_job.id}")
|
||||
|
||||
# Debugging-Ausgabe, nachdem der Thread gestartet wurde
|
||||
print(f"Thread für Job {new_job.id} erfolgreich gestartet.")
|
||||
|
||||
flash(f'"{unique_filename}" → Job #{new_job.id} läuft!')
|
||||
return redirect(url_for('auth.job_status'))
|
||||
|
||||
return render_template('upload.html')
|
||||
|
||||
@bp.route('/download/<int:job_id>', methods=['GET'])
|
||||
@bp.route('/download/<int:job_id>')
|
||||
@login_required
|
||||
def download_result(job_id):
|
||||
job = Job.query.get_or_404(job_id)
|
||||
print(f"Job ID: {job.id} - User ID: {job.user_id} - Current User ID: {current_user.id}")
|
||||
job = Job.query.filter_by(id=job_id, user_id=current_user.id).first_or_404()
|
||||
|
||||
# Überprüfen, ob der Job dem aktuellen Benutzer gehört
|
||||
if job.user_id != current_user.id:
|
||||
flash("Sie haben keine Berechtigung, dieses Ergebnis herunterzuladen.")
|
||||
if not job.result_filename or not job.status.startswith('✅'):
|
||||
flash('Ergebnis nicht bereit.')
|
||||
return redirect(url_for('auth.job_status'))
|
||||
|
||||
# Überprüfen, ob das Ergebnis vorhanden ist
|
||||
if not job.result_filename:
|
||||
flash("Das Ergebnis ist noch nicht verfügbar.")
|
||||
return redirect(url_for('auth.job_status'))
|
||||
|
||||
# Überprüfen, ob die Datei im angegebenen Pfad existiert
|
||||
result_path = os.path.join(current_app.config['RESULT_FOLDER'], job.result_filename)
|
||||
print(f"Versuche, Datei herunterzuladen von: {result_path}")
|
||||
|
||||
result_path = os.path.join(RESULT_FOLDER, job.result_filename)
|
||||
if os.path.exists(result_path):
|
||||
print("Datei existiert und wird zum Download bereitgestellt.")
|
||||
return send_file(result_path, as_attachment=True)
|
||||
else:
|
||||
print("Datei nicht gefunden. Ergebnisverzeichnis oder Pfad prüfen.")
|
||||
flash("Ergebnisdatei nicht gefunden.")
|
||||
flash('Datei fehlt.')
|
||||
return redirect(url_for('auth.job_status'))
|
||||
|
||||
@bp.route('/download_raw/<int:job_id>')
|
||||
@login_required
|
||||
def download_result_raw(job_id):
|
||||
job = Job.query.filter_by(id=job_id, user_id=current_user.id).first_or_404()
|
||||
|
||||
if not job.result_filename_raw:
|
||||
flash('Rohdaten nicht verfügbar.')
|
||||
return redirect(url_for('auth.job_status'))
|
||||
|
||||
result_path = os.path.join(RESULT_FOLDER, job.result_filename_raw)
|
||||
if os.path.exists(result_path):
|
||||
return send_file(result_path, as_attachment=True)
|
||||
flash('Datei fehlt.')
|
||||
return redirect(url_for('auth.job_status'))
|
||||
|
||||
@bp.route('/delete_job/<int:job_id>', methods=['POST'])
|
||||
@login_required
|
||||
def delete_job(job_id):
|
||||
job = Job.query.get_or_404(job_id)
|
||||
if job.user_id != current_user.id:
|
||||
flash("Sie haben keine Berechtigung, diesen Job zu löschen.")
|
||||
return redirect(url_for('auth.job_status'))
|
||||
job = Job.query.filter_by(id=job_id, user_id=current_user.id).first_or_404()
|
||||
|
||||
# Löschen der Upload-Datei
|
||||
upload_path = os.path.join(current_app.config['UPLOAD_FOLDER'], job.filename)
|
||||
upload_path = os.path.join(UPLOAD_FOLDER, job.filename)
|
||||
if os.path.exists(upload_path):
|
||||
os.remove(upload_path)
|
||||
print(f"Upload-Datei gelöscht: {upload_path}")
|
||||
else:
|
||||
print(f"Upload-Datei nicht gefunden: {upload_path}")
|
||||
|
||||
# Löschen der Results-Datei, falls vorhanden
|
||||
if job.result_filename:
|
||||
result_path = os.path.join(current_app.config['RESULT_FOLDER'], job.result_filename)
|
||||
print(f"Versuche Ergebnisdatei zu löschen: {result_path}")
|
||||
|
||||
result_path = os.path.join(RESULT_FOLDER, job.result_filename)
|
||||
if os.path.exists(result_path):
|
||||
try:
|
||||
os.remove(result_path)
|
||||
print(f"Ergebnisdatei gelöscht: {result_path}")
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Löschen der Ergebnisdatei: {e}")
|
||||
else:
|
||||
print(f"Ergebnisdatei nicht gefunden im Pfad: {result_path}")
|
||||
os.remove(result_path)
|
||||
|
||||
if job.result_filename_raw: # ✅ Raw auch löschen
|
||||
raw_path = os.path.join(RESULT_FOLDER, job.result_filename_raw)
|
||||
if os.path.exists(raw_path):
|
||||
os.remove(raw_path)
|
||||
|
||||
# Job aus der Datenbank löschen
|
||||
db.session.delete(job)
|
||||
db.session.commit()
|
||||
flash("Job erfolgreich gelöscht.")
|
||||
flash('Job gelöscht.')
|
||||
return redirect(url_for('auth.job_status'))
|
||||
|
||||
@bp.route('/job_status/<int:job_id>')
|
||||
@login_required
|
||||
def job_status_api(job_id):
|
||||
job = Job.query.filter_by(id=job_id, user_id=current_user.id).first()
|
||||
if not job:
|
||||
return jsonify({'error': 'Not found'}), 404
|
||||
return jsonify({
|
||||
'id': job.id,
|
||||
'status': job.status,
|
||||
'result_filename': job.result_filename,
|
||||
'result_filename_raw': getattr(job, 'result_filename_raw', None),
|
||||
'scraper_job_id': getattr(job, 'scraper_job_id', None)
|
||||
})
|
||||
|
||||
@bp.route('/resume_job/<int:job_id>', methods=['POST'])
|
||||
@login_required
|
||||
def resume_job(job_id):
|
||||
job = Job.query.filter_by(id=job_id, user_id=current_user.id).first_or_404()
|
||||
|
||||
thread = threading.Thread(
|
||||
target=process_file,
|
||||
args=(job.filename, job.id, current_app._get_current_object())
|
||||
)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
flash(f'Job #{job_id} wird fortgesetzt...')
|
||||
return redirect(url_for('auth.job_status'))
|
||||
|
||||
|
||||
# ── ADMIN ──────────────────────────────────────────
|
||||
@bp.route('/admin', methods=['GET'])
|
||||
@login_required
|
||||
def admin_panel():
|
||||
if not current_user.is_admin:
|
||||
flash("Keine Berechtigung.")
|
||||
return redirect(url_for('auth.job_status'))
|
||||
|
||||
users = User.query.all()
|
||||
return render_template('admin_panel.html', users=users)
|
||||
cfg = AppConfig.query.filter_by(key='allow_signup').first()
|
||||
signup_allowed = cfg and cfg.value == 'true'
|
||||
return render_template('admin_panel.html', users=users, signup_allowed=signup_allowed)
|
||||
|
||||
@bp.route('/admin/create_user', methods=['POST'])
|
||||
@login_required
|
||||
def create_user():
|
||||
if not current_user.is_admin:
|
||||
flash("Keine Berechtigung.")
|
||||
return redirect(url_for('auth.admin_panel'))
|
||||
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
is_admin = 'is_admin' in request.form # Checkbox für Adminrechte
|
||||
|
||||
hashed_password = generate_password_hash(password, method='sha256')
|
||||
new_user = User(username=username, password=hashed_password, is_admin=is_admin)
|
||||
password = generate_password_hash(request.form['password']) # ✅ Fix
|
||||
is_admin = 'is_admin' in request.form
|
||||
new_user = User(username=username, password=password, is_admin=is_admin)
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
|
||||
flash(f"Benutzer {username} wurde erstellt.")
|
||||
flash(f'{username} erstellt.')
|
||||
return redirect(url_for('auth.admin_panel'))
|
||||
|
||||
@bp.route('/admin/reset_password/<int:user_id>', methods=['POST'])
|
||||
@login_required
|
||||
def reset_password(user_id):
|
||||
if not current_user.is_admin:
|
||||
flash("Keine Berechtigung.")
|
||||
return redirect(url_for('auth.admin_panel'))
|
||||
|
||||
user = User.query.get_or_404(user_id)
|
||||
new_password = request.form['new_password']
|
||||
user.password = generate_password_hash(new_password, method='sha256')
|
||||
user.password = generate_password_hash(new_password) # ✅ Fix
|
||||
db.session.commit()
|
||||
|
||||
flash(f"Passwort für Benutzer {user.username} wurde zurückgesetzt.")
|
||||
flash(f'Passwort {user.username} zurückgesetzt.')
|
||||
return redirect(url_for('auth.admin_panel'))
|
||||
|
||||
@bp.route('/admin/delete_user/<int:user_id>', methods=['POST'])
|
||||
@login_required
|
||||
def delete_user(user_id):
|
||||
if not current_user.is_admin:
|
||||
flash("Keine Berechtigung.")
|
||||
return redirect(url_for('auth.admin_panel'))
|
||||
|
||||
user = User.query.get_or_404(user_id)
|
||||
if user.is_admin:
|
||||
flash("Administratoren können nicht gelöscht werden.")
|
||||
flash('Admin nicht löschbar.')
|
||||
return redirect(url_for('auth.admin_panel'))
|
||||
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
flash(f"Benutzer {user.username} wurde gelöscht.")
|
||||
flash(f'{user.username} gelöscht.')
|
||||
return redirect(url_for('auth.admin_panel'))
|
||||
|
||||
@bp.route('/admin/toggle_signup', methods=['POST'])
|
||||
@login_required
|
||||
def toggle_signup():
|
||||
if not current_user.is_admin:
|
||||
return redirect(url_for('auth.admin_panel'))
|
||||
|
||||
cfg = AppConfig.query.filter_by(key='allow_signup').first()
|
||||
if not cfg:
|
||||
cfg = AppConfig(key='allow_signup', value='true')
|
||||
db.session.add(cfg)
|
||||
else:
|
||||
cfg.value = 'false' if cfg.value == 'true' else 'true'
|
||||
|
||||
db.session.commit()
|
||||
state = '✅ aktiviert' if cfg.value == 'true' else '🔒 deaktiviert'
|
||||
flash(f'Registrierung {state}.')
|
||||
return redirect(url_for('auth.admin_panel'))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue