From f70b0ec2db5e932d893a0c764eef33c20092da66 Mon Sep 17 00:00:00 2001 From: slashformotion Date: Tue, 8 Apr 2025 21:57:00 +0200 Subject: [PATCH] wip --- src/__init__.py | 39 +++++++++++++++++++++++++++++++++++++-- vv.typ | 1 + 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 vv.typ diff --git a/src/__init__.py b/src/__init__.py index 10db5b5..749dfa7 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,15 +1,19 @@ from __future__ import annotations import logging +import os +import tempfile +from pathlib import Path + import typst -from fastapi import FastAPI, Request, Response, status +from fastapi import FastAPI, HTTPException, Request, Response, status from fastapi.responses import StreamingResponse from prometheus_fastapi_instrumentator import Instrumentator from pydantic import BaseModel from slowapi import Limiter, _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded from slowapi.util import get_remote_address -import os +from starlette.datastructures import UploadFile DEFAULT_CHUNK_SIZE = 1024 REQUEST_PER_MINUTES = os.getenv("TYPST_HTTP_API_REQUESTS_PER_MINUTES") @@ -55,3 +59,34 @@ def iterfile( yield input_bytes[i : i + chunk_size] return StreamingResponse(iterfile(res), media_type="application/pdf") + + +@app.post("/project") +@limiter.limit(f"{REQUEST_PER_MINUTES}/minute") +async def build_project(request: Request, response: Response): + form_data = await request.form() + with tempfile.TemporaryDirectory(suffix="_typst") as td: + for field_name, file_obj in form_data.items(): + if isinstance(file_obj, UploadFile): + if ".." in field_name: + raise HTTPException( + status_code=400, detail="no field names can contain '..'" + ) + with open(Path(td) / field_name, "wb") as f: + f.write(await file_obj.read()) + + try: + res = typst.compile(Path(td) / "main.typ") + except RuntimeError as e: + response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY + logger.error(f"failed to build {1}") + return CompilationError(reason="Compilation error", content=str(e)) + + def iterfile( + input_bytes: bytes, + chunk_size: int = DEFAULT_CHUNK_SIZE, + ): + for i in range(0, len(input_bytes), chunk_size): + yield input_bytes[i : i + chunk_size] + + return StreamingResponse(iterfile(res), media_type="application/pdf") diff --git a/vv.typ b/vv.typ new file mode 100644 index 0000000..8bf6b2d --- /dev/null +++ b/vv.typ @@ -0,0 +1 @@ +bip boup TURTHSF