commit b55f7843dbc01ecd0f200fc4382701ec05fbb29f Author: Repinoid Date: Fri Oct 23 17:55:00 2026 +0300 Initial import of liberta convert service diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7024fd7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.12 + +RUN apt-get update && apt-get install -y --no-install-recommends libreoffice && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY app.py . + +EXPOSE 5000 + +CMD ["python3", "app.py"] diff --git a/app.py b/app.py new file mode 100644 index 0000000..6c0df5f --- /dev/null +++ b/app.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +"""LibreOffice converter — Flask HTTP service. .doc → .docx with UI + API.""" +import subprocess, tempfile, os, base64 +from flask import Flask, request, send_file, render_template_string + +app = Flask(__name__) +VERSION = "1.0.0" + +# Логотип как data URI (как в Сверке — через img, не inline SVG) +_LOGO_SVG = """""" +LOGO_DATA_URI = "data:image/svg+xml;base64," + base64.b64encode(_LOGO_SVG.encode()).decode() + +HTML = """ + + + + +Конвертер документов — Nubes + + + + +
+ Nubes + КОНВЕРТЕР + v__VERSION__ +
+
+

Конвертация старых документов Word

+

+ Загрузите файл в формате .doc (Microsoft Word 97–2003), + .rtf или .odt — получите современный + .docx. Конвертация происходит на сервере через LibreOffice, + исходный файл никуда не сохраняется. +

+
+
📦 Конвертировать файл
+
+
+
+ + +
+
+
+
+
+
+
🔌 API для разработчиков
+
+
+ Адрес: https://liberta.containerk8s.dev.nubes.ru/convert
+ Метод: POST, файл в multipart/form-data, поле file.
+ Успех: в ответ придёт готовый .docx — сразу сохраняйте.
+ Ошибка: {"ok": false, "error": "..."} с кодом 500.
+ Проверка: GET /health → {"ok": true}

+ Пример curl:
+ curl -X POST -F "file=@документ.doc" https://liberta.containerk8s.dev.nubes.ru/convert -o результат.docx +
+
+
+
+ + + +""" + +def _convert_file(f): + """Convert one file to .docx, return (bytes, download_name) or raise.""" + with tempfile.NamedTemporaryFile(suffix=".doc", delete=False) as tmp: + f.save(tmp) + doc_path = tmp.name + tmpdir = tempfile.mkdtemp() + try: + subprocess.run( + ["libreoffice", "--headless", "--convert-to", "docx", "--outdir", tmpdir, doc_path], + timeout=120, capture_output=True, check=True + ) + docx_files = [x for x in os.listdir(tmpdir) if x.endswith(".docx")] + if not docx_files: + raise ValueError("conversion produced no .docx") + with open(os.path.join(tmpdir, docx_files[0]), "rb") as out: + return out.read(), os.path.splitext(f.filename)[0] + ".docx" + finally: + if os.path.exists(doc_path): + os.unlink(doc_path) + for x in os.listdir(tmpdir): + os.unlink(os.path.join(tmpdir, x)) + os.rmdir(tmpdir) + +@app.route("/", methods=["GET"]) +def index(): + return render_template_string(HTML.replace("__VERSION__", VERSION)) + +@app.route("/convert", methods=["POST"]) +def convert(): + if "file" not in request.files: + return {"ok": False, "error": "no file"}, 400 + f = request.files["file"] + if not f.filename: + return {"ok": False, "error": "empty filename"}, 400 + try: + data, name = _convert_file(f) + return send_file( + io.BytesIO(data), + mimetype="application/vnd.openxmlformats-officedocument.wordprocessingml.document", + as_attachment=True, + download_name=name, + ) + except subprocess.TimeoutExpired: + return {"ok": False, "error": "conversion timeout"}, 500 + except subprocess.CalledProcessError as e: + err = e.stderr.decode() if e.stderr else "unknown" + return {"ok": False, "error": f"libreoffice: {err}"}, 500 + except ValueError as e: + return {"ok": False, "error": str(e)}, 500 + +@app.route("/health", methods=["GET"]) +def health(): + return {"ok": True, "version": VERSION} + +@app.route("/static/logo.svg") +def logo(): + from flask import Response + return Response(_LOGO_SVG, mimetype="image/svg+xml") + +if __name__ == "__main__": + import io + app.run(host="0.0.0.0", port=5000) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +flask