src/dlw/admin.py aktualisiert

This commit is contained in:
Kim Diallo 2026-01-03 02:30:19 +01:00
parent 853e9e5ea7
commit 67f73d5286
1 changed files with 50 additions and 111 deletions

View File

@ -3,144 +3,83 @@ from werkzeug.utils import secure_filename
import os import os
import time import time
from . import git_ops from . import git_ops
from .marvin_ops import MarvinClient # Für Uberspace 8 Marvin API
# --- Konfiguration aus Umgebungsvariablen ---
# Diese Variablen werden auf dem Uberspace (z.B. via .bash_profile oder Service-Config) gesetzt.
UBERSPACE_ASTEROID = os.getenv('UBERSPACE_ASTEROID')
UBERSPACE_DOMAIN = os.getenv('UBERSPACE_DOMAIN')
MARVIN_API_KEY = os.getenv('MARVIN_API_KEY')
FLASK_SECRET_KEY = os.getenv('FLASK_SECRET_KEY', 'fallback-fuer-lokale-entwicklung')
app = Flask(__name__) app = Flask(__name__)
# Konfiguration: Nutzt das 'downloads' Verzeichnis im aktuellen Arbeitsverzeichnis
app.config['UPLOAD_FOLDER'] = os.path.join(os.getcwd(), 'downloads') app.config['UPLOAD_FOLDER'] = os.path.join(os.getcwd(), 'downloads')
app.secret_key = FLASK_SECRET_KEY app.config['CONTENT_FOLDER'] = os.path.join(os.getcwd(), 'content')
app.secret_key = os.getenv('FLASK_SECRET_KEY', 'dev-key-123')
# Sicherstellen, dass der Upload-Ordner existiert # Verzeichnisse sicherstellen
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['CONTENT_FOLDER'], exist_ok=True)
# --- Hilfsfunktionen für die Content-Erzeugung --- def generate_markdown_content(data, slug):
return f"""---
def generate_markdown_content(data, article_slug):
"""Erzeugt den Markdown-Inhalt inklusive YAML Frontmatter für Pelican."""
# Pfade für die Links im statischen Blog (relativ zur Domain)
pdf_path = f"/downloads/{article_slug}.pdf"
audio_path = f"/downloads/{article_slug}.mp3"
markdown_template = f"""---
Title: {data['title']} Title: {data['title']}
Date: {time.strftime("%Y-%m-%d %H:%M")} Date: {time.strftime("%Y-%m-%d %H:%M")}
Slug: {article_slug} Slug: {slug}
Description: {data['description']} Description: {data['description']}
Abstract: {data['abstract']} Abstract: {data['abstract']}
PDF_Download: {pdf_path} PDF_Download: /downloads/{slug}.pdf
Audio_Download: {audio_path} Audio_Download: /downloads/{slug}.mp3
--- ---
{data['article_body']} {data['article_body']}
""" """
return markdown_template.strip()
# --- Flask Routen ---
@app.route('/', methods=['GET']) @app.route('/', methods=['GET'])
def new_article_form(): def index():
"""Zeigt das Eingabeformular für die Autorin."""
form_html = """ form_html = """
<h1>Neuen Artikel erstellen</h1> <h1>Blog Admin</h1>
<p>Angemeldet auf Asteroid: <strong>{{ asteroid }}</strong></p>
<form method="POST" action="/preview" enctype="multipart/form-data"> <form method="POST" action="/preview" enctype="multipart/form-data">
<label>Titel:</label><br> <input type="text" name="title" placeholder="Titel" required><br>
<input type="text" name="title" style="width:100%" required><br><br> <input type="text" name="description" placeholder="Kurzbeschreibung"><br>
<textarea name="abstract" placeholder="Abstract"></textarea><br>
<label>Beschreibung (Short Desc.):</label><br> <textarea name="article_body" placeholder="Inhalt (Markdown)" required></textarea><br>
<input type="text" name="description" style="width:100%"><br><br> PDF: <input type="file" name="pdf_file"><br>
MP3: <input type="file" name="audio_file"><br>
<label>Abstract:</label><br> <button type="submit">Vorschau</button>
<textarea name="abstract" style="width:100%; height:100px"></textarea><br><br>
<label>Artikeltext (Markdown):</label><br>
<textarea name="article_body" style="width:100%; height:300px" required></textarea><br><br>
<label>PDF Download:</label>
<input type="file" name="pdf_file" accept=".pdf"><br>
<label>Audio Download (MP3):</label>
<input type="file" name="audio_file" accept=".mp3"><br><br>
<button type="submit" name="action" value="preview">Vorschau generieren</button>
</form> </form>
""" """
return render_template_string(form_html, asteroid=UBERSPACE_ASTEROID) return render_template_string(form_html)
@app.route('/preview', methods=['POST']) @app.route('/preview', methods=['POST'])
def preview_article(): def preview():
"""Erzeugt eine Vorschau des Artikels."""
data = request.form data = request.form
# Slug für Dateinamen generieren
slug = secure_filename(data['title']).lower().replace('-', '_') slug = secure_filename(data['title']).lower().replace('-', '_')
# Dateien temporär speichern (vereinfacht für dieses Beispiel)
if 'pdf_file' in request.files:
request.files['pdf_file'].save(os.path.join(app.config['UPLOAD_FOLDER'], f"{slug}.pdf"))
if 'audio_file' in request.files:
request.files['audio_file'].save(os.path.join(app.config['UPLOAD_FOLDER'], f"{slug}.mp3"))
markdown_content = generate_markdown_content(data, slug) markdown_content = generate_markdown_content(data, slug)
return render_template_string("""
# In der Praxis müssten die Dateien hier temporär zwischengespeichert werden, <h2>Vorschau: {{ title }}</h2>
# um sie im /publish Schritt final zu übernehmen. <pre>{{ content }}</pre>
<form method="POST" action="/publish">
preview_html = f""" <input type="hidden" name="slug" value="{{ slug }}">
<h2>Vorschau: {data['title']}</h2> <input type="hidden" name="title" value="{{ title }}">
<hr> <input type="hidden" name="content" value='{{ content }}'>
<div style="background: #f9f9f9; padding: 20px; border: 1px solid #ccc;"> <button type="submit">Veröffentlichen</button>
<strong>Abstract:</strong><p>{data['abstract']}</p> </form>
<hr> """, title=data['title'], content=markdown_content, slug=slug)
<p><i>[Hier würde der gerenderte Artikeltext stehen]</i></p>
</div>
<hr>
<form method="POST" action="/publish">
<input type="hidden" name="content_data" value='{markdown_content}'>
<input type="hidden" name="article_title" value="{data['title']}">
<p>Möchten Sie diesen Artikel jetzt auf <strong>{UBERSPACE_DOMAIN}</strong> veröffentlichen?</p>
<button type="submit">Artikel Bestätigen und Veröffentlichen (Git Push)</button>
<a href="/">Abbrechen und zurück</a>
</form>
"""
return preview_html
@app.route('/publish', methods=['POST']) @app.route('/publish', methods=['POST'])
def publish_article(): def publish():
"""Speichert Dateien und führt den Git-Push aus.""" slug = request.form.get('slug')
markdown_content = request.form.get('content_data') content = request.form.get('content')
article_title = request.form.get('article_title') title = request.form.get('title')
if not markdown_content: file_path = os.path.join(app.config['CONTENT_FOLDER'], f"{slug}.md")
flash("Fehler: Keine Inhaltsdaten vorhanden.") with open(file_path, 'w') as f:
return redirect(url_for('new_article_form')) f.write(content)
# Dateinamen und Pfad festlegen # Git Workflow
article_slug = secure_filename(article_title).lower().replace('-', '_') success, msg = git_ops.commit_and_push_article(file_path, [], title)
filename = f"{article_slug}.md" flash(msg)
article_path = os.path.join(os.getcwd(), 'content', filename) return redirect(url_for('index'))
# 1. Speichern des Markdown-Artikels
try:
with open(article_path, 'w', encoding='utf-8') as f:
f.write(markdown_content)
except IOError as e:
return f"Fehler beim Speichern des Artikels: {e}", 500
# 2. Git Operationen ausführen
try:
# Wir übergeben die Umgebungsvariablen an die git_ops, falls dort
# spezifische Commit-Messages oder Remote-Targets benötigt werden.
git_ops.add_and_commit(filename, f"Neuer Artikel: {article_title}")
git_ops.push_to_remote()
flash(f"Erfolgreich veröffentlicht auf {UBERSPACE_DOMAIN}!")
except Exception as e:
return f"Git-Fehler: {e}", 500
return redirect(url_for('new_article_form'))
if __name__ == '__main__': if __name__ == '__main__':
# Lokal zum Testen, auf Uberspace wird dies via Gunicorn/Passenger gestartet
app.run(debug=True) app.run(debug=True)