File size: 2,542 Bytes
e0effe1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
from __future__ import annotations

import mimetypes
import os
import subprocess
import tempfile
from pathlib import Path

from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse, Response
from pydantic import BaseModel


BASE_DIR = Path(__file__).resolve().parent

DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"


class DocxRequest(BaseModel):
    markdown: str
    filename: str | None = None


app = FastAPI()


@app.get("/")
def index():
    return FileResponse(BASE_DIR / "index.html")


@app.get("/{path:path}")
def static_files(path: str):
    if path.startswith("api/"):
        raise HTTPException(status_code=404)

    candidate = (BASE_DIR / path).resolve()
    if candidate == BASE_DIR or BASE_DIR not in candidate.parents:
        raise HTTPException(status_code=404)
    if not candidate.exists() or not candidate.is_file():
        raise HTTPException(status_code=404)

    media_type, _ = mimetypes.guess_type(str(candidate))
    return FileResponse(candidate, media_type=media_type)


@app.post("/api/docx")
def export_docx(req: DocxRequest):
    markdown = (req.markdown or "").strip()
    if not markdown:
        raise HTTPException(status_code=400, detail="empty markdown")

    filename = (req.filename or "output.docx").strip() or "output.docx"
    if not filename.lower().endswith(".docx"):
        filename += ".docx"

    with tempfile.TemporaryDirectory() as td:
        td_path = Path(td)
        input_md = td_path / "input.md"
        output_docx = td_path / "output.docx"
        input_md.write_text(markdown, encoding="utf-8")

        cmd = [
            "pandoc",
            str(input_md),
            "--from",
            "markdown+tex_math_dollars+tex_math_single_backslash",
            "--to",
            "docx",
            "--output",
            str(output_docx),
        ]
        try:
            subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        except FileNotFoundError as e:
            raise HTTPException(status_code=500, detail="pandoc not installed") from e
        except subprocess.CalledProcessError as e:
            detail = (e.stderr or b"").decode("utf-8", errors="replace")[-4000:]
            raise HTTPException(status_code=400, detail=f"pandoc failed: {detail}") from e

        data = output_docx.read_bytes()

    return Response(
        content=data,
        media_type=DOCX_MIME,
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )