Microservicio dockerizado en Python para convertir archivos Markdown a PDF. Disponible como interfaz web y API REST.
- Interfaz web intuitiva con textarea
- API REST para integración con otros sistemas
- Conversión de Markdown a PDF en tiempo real
- Soporte completo de sintaxis Markdown:
- Encabezados (H1-H6)
- Negrita, cursiva y tachado
- Listas ordenadas y desordenadas
- Enlaces e imágenes
- Bloques de código con sintaxis
- Tablas
- Citas
- Y más...
- Diseño PDF profesional con estilos CSS personalizados
- Dockerizado para fácil despliegue
- Descarga automática del PDF generado
- Autenticación con API Key para proteger endpoints
El microservicio incluye un sistema de autenticación con API Key OBLIGATORIA para proteger todos los endpoints.
La API Key es SIEMPRE requerida para:
- ✅ Generar PDFs desde la interfaz web (
/convert) - ✅ Usar la API REST (
/api/convert)
Acceso público (sin API Key):
- ✅ Ver la interfaz web (
/) - para que puedas ingresar tu API Key - ✅ Health check (
/api/health) - para monitoreo de Docker/Kubernetes
Configuración inicial (OBLIGATORIA):
- Copia el archivo de ejemplo:
cp .env.example .env- Edita
.envy configura tu API Key:
API_KEY=tu-api-key-super-secreta-aqui
FLASK_ENV=production
FLASK_DEBUG=False
# Rate Limiting (opcional - ajusta según necesidades)
RATE_LIMIT_API_CONVERT=10 per minute
# Usa 'unlimited' para sin límite- Levanta el servicio:
docker-compose upO configura directamente en docker-compose.yml:
environment:
- API_KEY=tu-api-key-super-secreta-aquiRecomendaciones de seguridad:
- Genera una API Key fuerte:
openssl rand -hex 32 - Nunca compartas tu API Key públicamente
- Rota la API Key periódicamente
- Guarda el archivo
.enven un lugar seguro (nunca lo subas a Git)
Incluye el header X-API-Key en tus peticiones al endpoint /api/convert:
curl -X POST http://localhost:5000/api/convert \
-H "Content-Type: application/json" \
-H "X-API-Key: tu-api-key-super-secreta-aqui" \
-d '{"markdown": "# Hola"}' \
--output documento.pdfNotas importantes sobre endpoints:
/- Interfaz web: Público (para ver el formulario)/convert- Conversión web: Requiere API Key (enviada desde formulario)/api/convert- API REST: Requiere API Key (header X-API-Key)/api/health- Healthcheck: Público (no requiere API Key)
Protecciones de seguridad:
- ✅ Autenticación obligatoria con API Key
- ✅ Rate limiting configurable (10 peticiones/minuto por defecto)
- ✅ Validación de entrada
- ✅ Protección contra fuerza bruta
El rate limiting es completamente configurable desde el archivo .env:
Valores disponibles por endpoint:
# Interfaz web
RATE_LIMIT_WEB_INDEX=20 per minute # Página principal
RATE_LIMIT_WEB_CONVERT=10 per minute # Conversión desde formulario
# API REST
RATE_LIMIT_API_CONVERT=10 per minute # Conversión desde API
RATE_LIMIT_API_HEALTH=60 per minute # Health checkFormatos válidos:
"10 per minute"- 10 peticiones por minuto"100 per hour"- 100 peticiones por hora"1000 per day"- 1000 peticiones por día"unlimited"o"0"- Sin límite (ilimitado)
Ejemplos de uso:
# Para uso personal (sin límites)
RATE_LIMIT_API_CONVERT=unlimited
# Para servidor público (límites estrictos)
RATE_LIMIT_API_CONVERT=5 per minute
# Para uso interno empresarial (límites altos)
RATE_LIMIT_API_CONVERT=1000 per hour- Docker
- Docker Compose (opcional, pero recomendado)
- Clona este repositorio:
git clone https://github.com/javiervilchezl/MdToPdf.git
cd MdToPdf- Construye y ejecuta el contenedor:
docker-compose up --build- Abre tu navegador en:
http://localhost:5000
- Construye la imagen:
docker build -t md-to-pdf .- Ejecuta el contenedor:
docker run -p 5000:5000 md-to-pdf- Abre tu navegador en:
http://localhost:5000
- Crea un entorno virtual:
python -m venv venv
venv\Scripts\activate # Windows
# o
source venv/bin/activate # Linux/Mac- Instala las dependencias:
pip install -r requirements.txt- Ejecuta la aplicación:
python app.py- Abre tu navegador en:
http://localhost:5000
- Abre la aplicación en tu navegador:
http://localhost:5000 - Ingresa tu API Key en el campo correspondiente (se guarda en tu navegador)
- Escribe o pega tu contenido Markdown en el área de texto
- Haz clic en "Generar PDF"
- El PDF se descargará automáticamente
Usa el header X-API-Key para autenticarte (ver sección "Uso de la API REST" más abajo).
Convierte Markdown a PDF. Acepta JSON y retorna un archivo PDF.
Request (con autenticación):
curl -X POST http://localhost:5000/api/convert \
-H "Content-Type: application/json" \
-H "X-API-Key: tu-api-key-super-secreta-aqui" \
-d '{"markdown": "# Hola Mundo\n\nEsto es **negrita**"}' \
--output documento.pdfRequest Body:
{
"markdown": "# Tu contenido Markdown aquí..."
}Response:
- Success (200): Archivo PDF binario
- Error (400):
{"error": "mensaje de error"} - Error (401):
{"error": "API Key requerida", "message": "Incluye el header X-API-Key en tu petición"} - Error (403):
{"error": "API Key inválida", "message": "La API Key proporcionada no es válida"} - Error (500):
{"error": "mensaje de error"}
Endpoint de healthcheck para monitoreo.
Response:
{
"status": "healthy",
"service": "markdown-to-pdf",
"version": "1.0.0"
}Con cURL:
# Convertir Markdown desde línea de comando
curl -X POST http://localhost:5000/api/convert \
-H "Content-Type: application/json" \
-d @- --output resultado.pdf << 'EOF'
{
"markdown": "# Mi Documento\n\n## Sección 1\n\nContenido con **negrita** y *cursiva*.\n\n- Item 1\n- Item 2"
}
EOFCon Python:
import requests
markdown_content = """
# Mi Documento
## Introducción
Este es un documento de prueba.
| Columna 1 | Columna 2 |
|-----------|-----------|
| Dato A | Dato B |
"""
# Headers con API Key (OBLIGATORIA)
headers = {
'Content-Type': 'application/json',
'X-API-Key': 'tu-api-key-super-secreta-aqui' # REQUERIDA
}
response = requests.post(
'http://localhost:5000/api/convert',
json={'markdown': markdown_content},
headers=headers
)
if response.status_code == 200:
with open('resultado.pdf', 'wb') as f:
f.write(response.content)
print("PDF generado exitosamente")
else:
print(f"Error {response.status_code}: {response.json()}")Con JavaScript/Node.js:
const fs = require('fs');
const fetch = require('node-fetch');
const markdown = `
# Mi Documento
## Contenido
Texto con **formato**.
`;
// Headers con API Key (OBLIGATORIA)
const headers = {
'Content-Type': 'application/json',
'X-API-Key': 'tu-api-key-super-secreta-aqui' // REQUERIDA
};
fetch('http://localhost:5000/api/convert', {
method: 'POST',
headers: headers,
body: JSON.stringify({ markdown })
})
.then(async res => {
if (res.ok) {
const buffer = await res.buffer();
fs.writeFileSync('documento.pdf', buffer);
console.log('PDF generado');
} else {
const error = await res.json();
console.error(`Error ${res.status}:`, error);
}
})
.catch(err => console.error('Error:', err));# Mi Documento
## Introducción
Este es un ejemplo de **Markdown**. Puedes usar:
- Listas
- *Cursiva* y **negrita**
- [Enlaces](https://ejemplo.com)
### Código
```python
def hola():
print('¡Hola Mundo!')| Columna 1 | Columna 2 |
|---|---|
| Dato 1 | Dato 2 |
| Dato 3 | Dato 4 |
## Estructura del Proyecto
md-to-pdf/ │ ├── app.py # Aplicación Flask principal ├── requirements.txt # Dependencias de Python ├── Dockerfile # Configuración de Docker ├── docker-compose.yml # Orquestación de Docker ├── README.md # Este archivo │ └── templates/ └── index.html # Interfaz web
## Tecnologías Utilizadas
- **Flask**: Framework web de Python
- **markdown2**: Conversión de Markdown a HTML
- **WeasyPrint**: Generación de PDF desde HTML
- **Docker**: Contenedorización
- **HTML/CSS/JavaScript**: Interfaz web
## Solución de Problemas
### El contenedor no inicia
- Verifica que Docker esté ejecutándose
- Asegúrate de que el puerto 5000 no esté en uso
- Revisa los logs: `docker-compose logs`
### Error al generar PDF
- Verifica que el contenido Markdown sea válido
- Revisa los logs del contenedor para más detalles
### Problemas con caracteres especiales
- Asegúrate de usar codificación UTF-8
- La aplicación soporta caracteres especiales y emojis
## Configuración de Seguridad
La aplicación incluye múltiples capas de seguridad:
- ✅ **Autenticación con API Key OBLIGATORIA** para todos los endpoints (excepto healthcheck)
- ✅ **Rate Limiting Configurable** - Ajusta límites o desactívalo con `unlimited` en `.env`
- ✅ Límite de tamaño de archivo (16MB)
- ✅ Validación estricta de entrada
- ✅ Timeout de operaciones
- ✅ Headers de seguridad HTTP
- ✅ Protección contra fuerza bruta
**IMPORTANTE para producción:**
- ⚠️ Configura SIEMPRE una API Key fuerte: `openssl rand -hex 32`
- ⚠️ Usa HTTPS (configura un proxy reverso con nginx)
- ⚠️ Cambia `FLASK_DEBUG=False`
- ⚠️ Ajusta los límites de rate limiting según tus necesidades:
- Uso personal: `unlimited`
- Servidor público: `5-10 per minute`
- Uso empresarial: `1000 per hour`
- ⚠️ Monitorea los logs para detectar intentos de acceso no autorizado
## Producción
Para deploying en producción, considera:
1. **Configurar API Key:**
```bash
export API_KEY="tu-api-key-super-secreta-y-larga-aqui-$(openssl rand -hex 32)"
-
Desactivar debug mode:
FLASK_ENV=production FLASK_DEBUG=False
-
Usar un servidor WSGI como Gunicorn:
gunicorn -w 4 -b 0.0.0.0:5000 app:app
-
Configurar un proxy reverso (nginx) con HTTPS:
server { listen 443 ssl; server_name tu-dominio.com; location / { proxy_pass http://localhost:5000; proxy_set_header X-API-Key $http_x_api_key; } }
-
Limitar rate limiting con nginx o un load balancer
-
Monitorear usando el endpoint
/api/health
Este proyecto es de código abierto y está disponible bajo la licencia MIT.
Creado para convertir fácilmente Markdown a PDF sin necesidad de subir documentos con datos sensibles a ninguna web de terceros.
Las contribuciones son bienvenidas. Por favor:
- Haz fork del proyecto
- Crea una rama para tu feature
- Commit tus cambios
- Push a la rama
- Abre un Pull Request
Si encuentras algún problema o tienes sugerencias, por favor abre un issue en el repositorio.
¡Disfruta convirtiendo Markdown a PDF!