Compare commits
No commits in common. "dev" and "main" have entirely different histories.
|
|
@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "forgejo-blog-manager"
|
name = "forgejo-blog-manager"
|
||||||
version = "0.1.5"
|
version = "0.1.4"
|
||||||
description = "Blog-Manager für Uberspace 8 mit Marvin API, Pelican und DOCX-Konvertierung"
|
description = "Blog-Manager für Uberspace 8 mit Marvin API und Pelican sowie DOCX-Konvertierung"
|
||||||
authors = [{name = "Ihre Autorin", email = "autorin@example.com"}]
|
authors = [{name = "Ihre Autorin", email = "autorin@example.com"}]
|
||||||
license = {text = "MIT"}
|
license = {text = "MIT"}
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
@ -30,7 +30,4 @@ package-dir = {"" = "src"}
|
||||||
packages = ["dlw"]
|
packages = ["dlw"]
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
"dlw" = [
|
"dlw" = ["ssg_config/*", "ssg_config/templates/*"]
|
||||||
"ssg_config/*",
|
|
||||||
"templates/**/*.j2"
|
|
||||||
]
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
"""
|
|
||||||
DLW Frontend - Web-Interface für die DOCX-zu-PDF Konvertierung.
|
|
||||||
|
|
||||||
Dieses Modul stellt eine Flask-App bereit, die es Autorinnen ermöglicht,
|
|
||||||
Word-Dokumente hochzuladen und direkt als PDF im Browser zurückzuerhalten.
|
|
||||||
Genutzt werden dafür Mammoth (Struktur) und WeasyPrint (Layout).
|
|
||||||
"""
|
|
||||||
|
|
||||||
from flask import Flask, render_template, request, send_file
|
|
||||||
from dlw.convert import convert_docx_to_pdf_stream
|
|
||||||
import os
|
|
||||||
|
|
||||||
# --- Konfiguration der Verzeichnisstruktur ---
|
|
||||||
# Wir ermitteln den absoluten Pfad zum zentralen Template-Ordner,
|
|
||||||
# damit Flask die Jinja2-Dateien auch dann findet, wenn die App
|
|
||||||
# aus einem anderen Verzeichnis gestartet wird.
|
|
||||||
base_dir = os.path.dirname(__file__)
|
|
||||||
template_dir = os.path.abspath(os.path.join(base_dir, 'templates'))
|
|
||||||
|
|
||||||
app = Flask(__name__, template_folder=template_dir)
|
|
||||||
|
|
||||||
# Begrenzung der Upload-Größe auf 16 Megabyte, um den Server zu schützen.
|
|
||||||
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET', 'POST'])
|
|
||||||
def index():
|
|
||||||
"""
|
|
||||||
Haupt-Route für den Konverter.
|
|
||||||
|
|
||||||
GET: Zeigt das Upload-Formular an.
|
|
||||||
POST: Verarbeitet das hochgeladene DOCX, konvertiert es in ein PDF-Stream
|
|
||||||
und sendet dieses als Download an den Client zurück.
|
|
||||||
"""
|
|
||||||
if request.method == 'POST':
|
|
||||||
# 1. Validierung: Prüfen, ob überhaupt ein Datei-Teil im Request ist
|
|
||||||
if 'file' not in request.files:
|
|
||||||
return "Fehler: Kein Datei-Feld im Formular gefunden.", 400
|
|
||||||
|
|
||||||
file = request.files['file']
|
|
||||||
|
|
||||||
# 2. Validierung: Prüfen, ob eine Datei ausgewählt wurde
|
|
||||||
if file.filename == '':
|
|
||||||
return "Fehler: Es wurde keine Datei zum Hochladen ausgewählt.", 400
|
|
||||||
|
|
||||||
# 3. Verarbeitung: Nur .docx Dateien zulassen
|
|
||||||
if file and file.filename.endswith('.docx'):
|
|
||||||
try:
|
|
||||||
# Wir lesen die Datei direkt in den Arbeitsspeicher (Bytes)
|
|
||||||
docx_bytes = file.read()
|
|
||||||
|
|
||||||
# Konvertierung via Mammoth & WeasyPrint (gibt BytesIO Stream zurück)
|
|
||||||
pdf_stream = convert_docx_to_pdf_stream(docx_bytes)
|
|
||||||
|
|
||||||
# Dynamische Generierung des Dateinamens (aus .docx wird .pdf)
|
|
||||||
download_name = file.filename.replace('.docx', '.pdf')
|
|
||||||
|
|
||||||
# PDF-Stream als Download an den Browser senden
|
|
||||||
return send_file(
|
|
||||||
pdf_stream,
|
|
||||||
mimetype='application/pdf',
|
|
||||||
as_attachment=True,
|
|
||||||
download_name=download_name
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
# Grobe Fehlerbehandlung für den MVP-Status
|
|
||||||
return f"Interner Fehler bei der Konvertierung: {str(e)}", 500
|
|
||||||
|
|
||||||
# Falls GET-Request oder fehlerhafter Dateityp: Zeige das Formular
|
|
||||||
# Der Pfad 'app/converter.html.j2' bezieht sich auf den template_folder
|
|
||||||
return render_template('app/converter.html.j2')
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# Startet den lokalen Entwicklungs-Server.
|
|
||||||
# 'debug=True' sorgt für automatisches Neuladen bei Code-Änderungen.
|
|
||||||
app.run(debug=True, port=5000)
|
|
||||||
|
|
@ -1,14 +1,3 @@
|
||||||
import os
|
|
||||||
|
|
||||||
# --- Dynamische Pfadberechnung ---
|
|
||||||
# Ermittelt den absoluten Pfad zum Verzeichnis dieser Datei (src/dlw/ssg_config)
|
|
||||||
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
|
|
||||||
# Setzt den Theme-Pfad auf den neuen zentralen Ort (src/dlw/templates/ssg)
|
|
||||||
# Wir gehen eine Ebene hoch (..) und dann in den templates/ssg Ordner
|
|
||||||
THEME = os.path.join(CURRENT_DIR, '..', 'templates', 'ssg')
|
|
||||||
|
|
||||||
# --- Allgemeine Website-Einstellungen ---
|
|
||||||
AUTHOR = 'Ihre Autorin'
|
AUTHOR = 'Ihre Autorin'
|
||||||
SITENAME = 'Forgejo Blog'
|
SITENAME = 'Forgejo Blog'
|
||||||
SITEURL = ''
|
SITEURL = ''
|
||||||
|
|
@ -16,12 +5,11 @@ PATH = 'content'
|
||||||
TIMEZONE = 'Europe/Berlin'
|
TIMEZONE = 'Europe/Berlin'
|
||||||
DEFAULT_LANG = 'de'
|
DEFAULT_LANG = 'de'
|
||||||
|
|
||||||
# --- URL-Struktur ---
|
|
||||||
ARTICLE_URL = 'blog/{slug}/'
|
ARTICLE_URL = 'blog/{slug}/'
|
||||||
ARTICLE_SAVE_AS = 'blog/{slug}/index.html'
|
ARTICLE_SAVE_AS = 'blog/{slug}/index.html'
|
||||||
STATIC_PATHS = ['downloads', 'images']
|
STATIC_PATHS = ['downloads', 'images']
|
||||||
|
THEME = 'ssg_config/templates'
|
||||||
|
|
||||||
# --- Markdown-Konfiguration ---
|
|
||||||
MARKDOWN = {
|
MARKDOWN = {
|
||||||
'extension_configs': {
|
'extension_configs': {
|
||||||
'markdown.extensions.extra': {},
|
'markdown.extensions.extra': {},
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>DLW - DOCX zu PDF</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, system-ui, sans-serif;
|
|
||||||
max-width: 500px;
|
|
||||||
margin: 100px auto;
|
|
||||||
background: #f4f7f6;
|
|
||||||
color: #2c3e50;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
background: white;
|
|
||||||
padding: 40px;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 10px 25px rgba(0,0,0,0.05);
|
|
||||||
}
|
|
||||||
h1 { font-size: 1.25rem; margin-bottom: 20px; text-align: center; }
|
|
||||||
.upload-area {
|
|
||||||
border: 2px dashed #cbd5e0;
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
background: #4a90e2;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
button:hover { background: #357abd; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>Word-Konverter für Autorinnen</h1>
|
|
||||||
<form method="post" enctype="multipart/form-data">
|
|
||||||
<div class="upload-area">
|
|
||||||
<input type="file" name="file" accept=".docx" required>
|
|
||||||
</div>
|
|
||||||
<button type="submit">PDF generieren</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Loading…
Reference in New Issue