diff --git a/Readme.md b/Readme.md index 68c0809..d3d1c86 100644 --- a/Readme.md +++ b/Readme.md @@ -40,6 +40,19 @@ This project is a comprehensive full‑stack web app for doing simple, local fil The backend is a Flask API and the frontend is a React app (Vite). +### Manual dependency installation + +If you run the backend outside Docker, make sure the native tools required by +the conversion pipeline are installed on the host: + +```bash +sudo apt-get update +sudo apt-get install poppler-utils tesseract-ocr ghostscript +``` + +You can also check whether the server image is missing anything by calling +`GET /health/dependencies`. + ### Project Rules These rules define how this project must be implemented and extended: diff --git a/backend/app/__init__.py b/backend/app/__init__.py index 31997e2..409d360 100644 --- a/backend/app/__init__.py +++ b/backend/app/__init__.py @@ -1,7 +1,9 @@ -from flask import Flask, request +from flask import Flask, jsonify, request import os from flask_cors import CORS +from utils.dependency_checker import check_dependency_status + def create_app(): app = Flask(__name__) @@ -46,6 +48,10 @@ def home(): @app.route("/health", methods=["GET", "OPTIONS"]) def _health(): return {"status": "ok"}, 200 + + @app.route("/health/dependencies", methods=["GET", "OPTIONS"]) + def _dependency_health(): + return jsonify(check_dependency_status()), 200 app.config["MAX_CONTENT_LENGTH"] = 10 * 1024 * 1024 diff --git a/backend/utils/dependency_checker.py b/backend/utils/dependency_checker.py new file mode 100644 index 0000000..ac423e4 --- /dev/null +++ b/backend/utils/dependency_checker.py @@ -0,0 +1,46 @@ +from shutil import which + + +def _tool_status(name, binary_names, required_for, install_command, optional=False): + installed = any(which(binary) for binary in binary_names) + return { + "installed": installed, + "required_for": required_for, + "install_command": install_command, + "optional": optional, + "binary_names": binary_names, + } + + +def check_dependency_status(): + dependencies = { + "poppler-utils": _tool_status( + "poppler-utils", + ["pdfinfo", "pdftoppm"], + ["PDF to PNG", "PDF info", "PDF merge validation"], + "apt-get install poppler-utils", + ), + "tesseract-ocr": _tool_status( + "tesseract-ocr", + ["tesseract"], + ["Image OCR"], + "apt-get install tesseract-ocr", + ), + "ghostscript": _tool_status( + "ghostscript", + ["gs"], + ["PDF compression", "PDF post-processing"], + "apt-get install ghostscript", + optional=True, + ), + } + + required_missing = [ + name for name, data in dependencies.items() if not data["optional"] and not data["installed"] + ] + + return { + "status": "degraded" if required_missing else "ok", + "dependencies": dependencies, + "missing_required_dependencies": required_missing, + } diff --git a/frontend/src/components/DependencyWarning.jsx b/frontend/src/components/DependencyWarning.jsx new file mode 100644 index 0000000..dfc6306 --- /dev/null +++ b/frontend/src/components/DependencyWarning.jsx @@ -0,0 +1,38 @@ +import React from "react"; +import { AlertTriangle, ExternalLink } from "lucide-react"; + +const DependencyWarning = ({ dependencies = [], status = "ok" }) => { + if (status !== "degraded" || dependencies.length === 0) { + return null; + } + + const labels = dependencies.map((item) => item.name).join(", "); + + return ( +
+ Some server dependencies are missing or unavailable. +
++ {labels} may be unavailable until the server image includes the + required packages. +
+ + Learn how to fix this +