Skip to content

Commit 2428ce4

Browse files
committed
feat: first commit
0 parents  commit 2428ce4

File tree

15 files changed

+768
-0
lines changed

15 files changed

+768
-0
lines changed

.dockerignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
**/__pycache__
2+
3+
.mypy_cache
4+
.venv
5+
6+
config/config.json
7+
.env

.env.example

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Configuration Zammad
2+
ZAMMAD_API_URL=
3+
ZAMMAD_API_TOKEN=
4+
5+
# Configuration authentification webhook
6+
WEBHOOK_USERNAME=
7+
WEBHOOK_PASSWORD=
8+
9+
# Paramètres Flask
10+
FLASK_DEBUG=
11+
FLASK_PORT=
12+
13+
# Paramètres Email
14+
MAIL_FROM=
15+
SMTP_HOST=
16+
SMTP_PORT=
17+
SMTP_USERNAME=
18+
SMTP_PASSWORD=
19+
SMTP_RECIPIENTS=

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
**/__pycache__
2+
3+
.mypy_cache
4+
.venv
5+
6+
config/config.json
7+
.env

Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
FROM python:3.12-slim
2+
3+
WORKDIR /app
4+
5+
COPY requirements.txt ./
6+
RUN pip install --no-cache-dir -r requirements.txt
7+
8+
COPY . .
9+
10+
EXPOSE 8080
11+
12+
CMD ["gunicorn", "--workers", "2", "--bind", "0.0.0.0:8080", "wsgi:application"]

README.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# BDE Tickets to PDF
2+
3+
Webhook Flask pour générer automatiquement des PDFs à partir des tickets Zammad du BDE.
4+
5+
## Structure du projet
6+
7+
```
8+
bde-tickets-to-pdf/
9+
├── server.py # Point d'entrée Flask (endpoints, auth)
10+
├── wsgi.py # Entrée WSGI (exporte `application`) pour gunicorn/uWSGI
11+
├── config.py # Chargement/validation des variables d'environnement
12+
├── auth.py # Décorateur @requires_auth pour le webhook
13+
├── services/
14+
│ ├── pdf.py # Génération des PDFs (ReportLab)
15+
│ ├── zammad.py # Interaction avec l'API Zammad
16+
│ └── email.py # Envoi d'emails avec pièce jointe PDF (SMTP)
17+
├── requirements.txt # Dépendances Python
18+
├── docker-compose.yaml # Exemple de configuration Docker Compose
19+
├── .env.example # Exemple de variables d'environnement
20+
└── README.md # Documentation (ce fichier)
21+
```
22+
23+
## Configuration
24+
25+
1. Copiez `.env.example` vers `.env` :
26+
```bash
27+
cp .env.example .env
28+
```
29+
30+
2. Modifiez le fichier `.env` avec vos valeurs :
31+
```env
32+
ZAMMAD_API_URL=https://votre-instance.zammad.com/api/v1/ticket_articles
33+
ZAMMAD_API_TOKEN=votre_token_api_zammad
34+
WEBHOOK_USERNAME=votre_nom_utilisateur
35+
WEBHOOK_PASSWORD=votre_mot_de_passe_securise
36+
FLASK_DEBUG=false
37+
FLASK_PORT=5000
38+
SMTP_HOST=smtp.example.com
39+
SMTP_PORT=587
40+
SMTP_USERNAME=...
41+
SMTP_PASSWORD=...
42+
MAIL_FROM=webhook@example.com
43+
```
44+
45+
## Installation
46+
47+
```bash
48+
pip install -r requirements.txt
49+
```
50+
51+
## Utilisation
52+
53+
```bash
54+
python server.py
55+
```
56+
57+
Le serveur démarre sur `http://localhost:5000` (ou le port configuré).
58+
59+
### En production (WSGI)
60+
61+
Ne pas utiliser le serveur de développement Flask en production. Utilisez un serveur WSGI comme gunicorn ou waitress.
62+
63+
Exemple avec gunicorn :
64+
65+
```bash
66+
# depuis le répertoire du projet
67+
pip install gunicorn
68+
# lancer 4 workers et écouter sur le port 8080
69+
gunicorn --workers 4 --bind 0.0.0.0:8080 wsgi:application
70+
```
71+
72+
Exemple avec waitress (Windows / simplicité) :
73+
74+
```bash
75+
pip install waitress
76+
python -m waitress --port=8080 wsgi:application
77+
```
78+
79+
Remarques :
80+
- Assurez-vous que FLASK_DEBUG=false dans vos variables d'environnement pour la production.
81+
- Vérifiez la configuration des variables d'environnement (voir .env.example) avant de démarrer.
82+
- Pour déploiements robustes, utilisez un process manager (systemd, supervisor) et configurez la rotation des logs et la supervision.
83+
84+
## Endpoint
85+
86+
- **POST** `/webhook` - Reçoit les données de ticket Zammad et génère un PDF
87+
88+
### Authentification
89+
90+
L'endpoint nécessite une authentification HTTP Basic avec les identifiants configurés dans le fichier `.env`.
91+
92+
### Exemple de requête
93+
94+
Le webhook attend un JSON avec la structure :
95+
```json
96+
{
97+
"ticket": {
98+
"id": 123,
99+
"number": "32087",
100+
"title": "Titre du ticket",
101+
"owner": {
102+
"firstname": "Jean",
103+
"lastname": "Dupont"
104+
},
105+
// ... autres champs BDE
106+
},
107+
"article": {
108+
"body": "Contenu de l'article",
109+
"from": "user@example.com",
110+
"created_at": "2025-09-25T10:30:00Z"
111+
}
112+
}
113+
```

__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Package bde-tickets-to-pdf
2+
# Webhook pour générer des PDFs de tickets Zammad

auth.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from flask import request, jsonify
2+
from functools import wraps
3+
from config import Config
4+
5+
6+
def check_auth(username, password):
7+
"""Vérifie les identifiants d'authentification"""
8+
return username == Config.WEBHOOK_USERNAME and password == Config.WEBHOOK_PASSWORD
9+
10+
11+
def authenticate():
12+
"""Renvoie une réponse 401 demandant l'authentification"""
13+
return jsonify({"error": "Authentication required"}), 401
14+
15+
16+
def requires_auth(f):
17+
"""Décorateur pour vérifier l'authentification HTTP Basic"""
18+
19+
@wraps(f)
20+
def decorated(*args, **kwargs):
21+
auth = request.authorization
22+
if not auth or not check_auth(auth.username, auth.password):
23+
return authenticate()
24+
return f(*args, **kwargs)
25+
26+
return decorated

config.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import os
2+
from dotenv import load_dotenv
3+
4+
# Charger les variables d'environnement
5+
load_dotenv()
6+
7+
8+
class Config:
9+
"""Configuration centralisée de l'application"""
10+
11+
# Configuration Zammad
12+
ZAMMAD_API_URL = os.getenv("ZAMMAD_API_URL", "")
13+
ZAMMAD_API_TOKEN = os.getenv("ZAMMAD_API_TOKEN", "")
14+
15+
# Configuration authentification webhook
16+
WEBHOOK_USERNAME = os.getenv("WEBHOOK_USERNAME", "")
17+
WEBHOOK_PASSWORD = os.getenv("WEBHOOK_PASSWORD", "")
18+
19+
# Configuration Flask
20+
DEBUG = os.getenv("FLASK_DEBUG", "False").lower() == "true"
21+
PORT = int(os.getenv("FLASK_PORT", "5000"))
22+
23+
# Configuration Mails (correction et paramètres SMTP)
24+
MAIL_FROM = os.getenv("MAIL_FROM", "")
25+
SMTP_HOST = os.getenv("SMTP_HOST", "")
26+
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
27+
SMTP_USERNAME = os.getenv("SMTP_USERNAME", "")
28+
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
29+
SMTP_RECIPIENTS = os.getenv("SMTP_RECIPIENTS", "").split(",")
30+
SMTP_USE_TLS = os.getenv("SMTP_USE_TLS", "True").lower() == "true"
31+
32+
@classmethod
33+
def validate(cls):
34+
"""Valide que toutes les configurations obligatoires sont présentes"""
35+
required_vars = [
36+
("ZAMMAD_API_URL", cls.ZAMMAD_API_URL),
37+
("ZAMMAD_API_TOKEN", cls.ZAMMAD_API_TOKEN),
38+
("WEBHOOK_USERNAME", cls.WEBHOOK_USERNAME),
39+
("WEBHOOK_PASSWORD", cls.WEBHOOK_PASSWORD),
40+
("MAIL_FROM", cls.MAIL_FROM),
41+
("SMTP_HOST", cls.SMTP_HOST),
42+
("SMTP_PORT", cls.SMTP_PORT),
43+
("SMTP_USERNAME", cls.SMTP_USERNAME),
44+
("SMTP_PASSWORD", cls.SMTP_PASSWORD),
45+
("SMTP_RECIPIENTS", cls.SMTP_RECIPIENTS),
46+
]
47+
48+
missing_vars = []
49+
for var_name, var_value in required_vars:
50+
if not var_value:
51+
missing_vars.append(var_name)
52+
53+
if missing_vars:
54+
raise ValueError(
55+
f"Variables d'environnement manquantes: {', '.join(missing_vars)}"
56+
)
57+
58+
return True

docker-compose.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
services:
2+
zammad-workflows:
3+
image: zammad-workflows
4+
ports:
5+
- "8080:8080"
6+
restart: unless-stopped
7+
build: .
8+
env_file:
9+
- .env

requirements.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
beautifulsoup4==4.13.5
2+
blinker==1.9.0
3+
bs4==0.0.2
4+
certifi==2025.8.3
5+
charset-normalizer==3.4.3
6+
click==8.3.0
7+
Flask==3.1.2
8+
gunicorn==23.0.0
9+
idna==3.10
10+
itsdangerous==2.2.0
11+
Jinja2==3.1.6
12+
MarkupSafe==3.0.2
13+
packaging==25.0
14+
pillow==11.3.0
15+
python-dotenv==1.1.1
16+
reportlab==4.4.4
17+
requests==2.32.5
18+
soupsieve==2.8
19+
typing_extensions==4.15.0
20+
urllib3==2.5.0
21+
Werkzeug==3.1.3

0 commit comments

Comments
 (0)