systeme:documenso:minio
Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
| systeme:documenso:minio [2026/04/11 16:06] – [Webhook Python] techer.charles_educ-valadon-limoges.fr | systeme:documenso:minio [2026/04/11 17:55] (Version actuelle) – [Webhook Python] techer.charles_educ-valadon-limoges.fr | ||
|---|---|---|---|
| Ligne 169: | Ligne 169: | ||
| * Envoyer le fichier par blocs (chunks) | * Envoyer le fichier par blocs (chunks) | ||
| * Graph reconstruit le fichier côté SharePoint | * Graph reconstruit le fichier côté SharePoint | ||
| + | </ | ||
| + | |||
| + | <WRAP center round info> | ||
| + | Gestion de plusieurs dossiers dans une seule équipe SharePoint : | ||
| + | * dossiers automatiquement créés dans Sharepoint | ||
| + | * aucun droit d’écriture manuel côté SharePoint | ||
| + | * traçabilité MinIO → SharePoint | ||
| + | |||
| + | Structure : | ||
| + | < | ||
| + | Documents | ||
| + | ├── administration | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | ├── bts-sio | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | ├── bts-mco | ||
| + | │ | ||
| + | │ | ||
| + | │ | ||
| + | </ | ||
| + | |||
| + | Mapping pédagogique MinIO → SharePoint (préfixe MinIO = dossier SharePoint) | ||
| + | ^ MinIO (bucket pedagogie) | ||
| + | |administration/ | ||
| + | |bts-sio/ | ||
| + | |bts-sio/ | ||
| </ | </ | ||
| Ligne 174: | Ligne 204: | ||
| <code python main.py> | <code python main.py> | ||
| from flask import Flask, request, abort | from flask import Flask, request, abort | ||
| - | import os | + | import os, requests |
| from graph import graph_request | from graph import graph_request | ||
| Ligne 182: | Ligne 212: | ||
| GRAPH_BASE = " | GRAPH_BASE = " | ||
| - | SHARED_SECRET | + | WEBHOOK_SECRET |
| if not WEBHOOK_SECRET: | if not WEBHOOK_SECRET: | ||
| raise RuntimeError(" | raise RuntimeError(" | ||
| - | # Détection de la taille du fichier | + | # Détection de la taille du fichier |
| MAX_SIMPLE_UPLOAD = 4 * 1024 * 1024 # 4 MB | MAX_SIMPLE_UPLOAD = 4 * 1024 * 1024 # 4 MB | ||
| + | CHUNK_SIZE = 10 * 1024 * 1024 # 10 MB | ||
| - | size = os.path.getsize(file_path) | + | app = Flask(__name__) |
| - | if size <= MAX_SIMPLE_UPLOAD: | ||
| - | upload_simple(path, | ||
| - | else: | ||
| - | upload_chunked(path, | ||
| - | app = Flask(__name__) | + | # ───────────────────────────── |
| + | # Sécurité webhook MinIO | ||
| + | # ───────────────────────────── | ||
| + | def verify_minio_webhook(req): | ||
| + | auth = req.headers.get(" | ||
| + | |||
| + | if not auth or not auth.startswith(" | ||
| + | abort(401, " | ||
| + | |||
| + | token = auth[len(" | ||
| + | |||
| + | if token != WEBHOOK_SECRET: | ||
| + | abort(403, " | ||
| + | |||
| + | # ───────────────────────────── | ||
| + | # SharePoint drive | ||
| + | # ───────────────────────────── | ||
| + | def get_drive_id(): | ||
| + | site = graph_request( | ||
| + | " | ||
| + | f" | ||
| + | ) | ||
| + | drive = graph_request( | ||
| + | " | ||
| + | f" | ||
| + | ) | ||
| + | return drive[" | ||
| + | |||
| + | # INITIALISATION AU DÉMARRAGE | ||
| + | print(" | ||
| + | DRIVE_ID = get_drive_id() | ||
| + | print(f" | ||
| - | #Création de la session d’upload | + | # ───────────────────────────── |
| - | def create_upload_session(drive_id, | + | # Upload helpers |
| - | | + | # ───────────────────────────── |
| + | def create_upload_session(sp_path): | ||
| + | | ||
| " | " | ||
| - | f" | + | f" |
| json={ | json={ | ||
| " | " | ||
| Ligne 208: | Ligne 268: | ||
| } | } | ||
| } | } | ||
| - | )[" | + | ) |
| + | return res[" | ||
| - | # Upload par chunks (robuste et reprenable) | ||
| - | def upload_chunked(sp_path, | ||
| - | upload_url = create_upload_session(DRIVE_ID, | ||
| - | chunk_size = 10 * 1024 * 1024 # 10 MB | + | def upload_simple(sp_path, |
| - | | + | |
| + | " | ||
| + | f" | ||
| + | data=data | ||
| + | | ||
| - | | + | |
| + | def upload_chunked(sp_path, | ||
| + | upload_url = create_upload_session(sp_path) | ||
| + | size = os.path.getsize(file_path) | ||
| + | |||
| + | | ||
| start = 0 | start = 0 | ||
| while start < size: | while start < size: | ||
| - | | + | |
| - | end = start + len(data) - 1 | + | end = start + len(chunk) - 1 |
| headers = { | headers = { | ||
| - | " | + | " |
| " | " | ||
| } | } | ||
| - | r = requests.put(upload_url, | + | r = requests.put(upload_url, |
| r.raise_for_status() | r.raise_for_status() | ||
| + | start += len(chunk) | ||
| - | start += chunk_size | ||
| + | # ───────────────────────────── | ||
| + | # Event handlers | ||
| + | # ───────────────────────────── | ||
| + | def upload_object(key): | ||
| + | sp_path = map_mapping_path(key) | ||
| - | | + | if not sp_path: |
| - | def verify_minio_webhook(req): | + | print(f" |
| - | auth = req.headers.get(" | + | return |
| - | | + | |
| - | abort(401, " | + | |
| - | | + | |
| + | file_path = f"/ | ||
| - | if token != WEBHOOK_SECRET: | + | |
| - | | + | |
| + | | ||
| + | | ||
| + | upload_simple(key, | ||
| + | else: | ||
| + | upload_chunked(key, | ||
| + | |||
| + | os.remove(file_path) | ||
| - | def get_drive_id(): | + | |
| - | | + | def delete_object(key): |
| - | "GET", | + | |
| - | f" | + | |
| + | if not sp_path: | ||
| + | return | ||
| + | |||
| + | print(f" | ||
| + | |||
| + | | ||
| + | "DELETE", | ||
| + | f" | ||
| ) | ) | ||
| - | drive = graph_request( | + | |
| - | "GET", | + | # ───────────────────────────── |
| - | f" | + | # Mappping |
| - | | + | # ───────────────────────────── |
| - | | + | def map_mapping_path(key: str) -> str | None: |
| + | """ | ||
| + | | ||
| + | | ||
| + | """ | ||
| - | # INITIALISATION AU DÉMARRAGE | + | key = key.lstrip("/") |
| - | print(" | + | |
| - | DRIVE_ID | + | |
| - | print(f"Drive ID initialized: | + | |
| + | ALLOWED_PREFIXES = [ | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | ] | ||
| + | |||
| + | for prefix in ALLOWED_PREFIXES: | ||
| + | if key.startswith(prefix): | ||
| + | return key | ||
| + | |||
| + | # Tout le reste est ignoré | ||
| + | return None | ||
| + | |||
| + | |||
| + | |||
| + | # ───────────────────────────── | ||
| + | # Webhook endpoint | ||
| + | # ───────────────────────────── | ||
| @app.route("/ | @app.route("/ | ||
| def s3_event(): | def s3_event(): | ||
| Ligne 276: | Ligne 382: | ||
| return "", | return "", | ||
| - | |||
| - | def upload_object(key): | ||
| - | print(f" | ||
| - | url = f" | ||
| - | graph_request(" | ||
| - | |||
| - | def delete_object(key): | ||
| - | print(f" | ||
| - | url = f" | ||
| - | graph_request(" | ||
| if __name__ == " | if __name__ == " | ||
| Ligne 485: | Ligne 581: | ||
| Pour sécuriser le webhook MinIO vers le Proxy, utilisation d'un header statique contenant un token pour empêcher toute requête non légitime d’appeler /s3event. | Pour sécuriser le webhook MinIO vers le Proxy, utilisation d'un header statique contenant un token pour empêcher toute requête non légitime d’appeler /s3event. | ||
| + | |||
| * Configurer le webhook avec le token : | * Configurer le webhook avec le token : | ||
| Ligne 557: | Ligne 653: | ||
| Please restart your server with `mc admin service restart minio`. | Please restart your server with `mc admin service restart minio`. | ||
| </ | </ | ||
| + | |||
| + | * créer le bucket | ||
| + | |||
| + | < | ||
| + | mc mb minio/lycee | ||
| + | |||
| + | => résultat attendu | ||
| + | Bucket created successfully `minio/ | ||
| + | </ | ||
| + | |||
| + | * vérification | ||
| + | |||
| + | < | ||
| + | mc ls minio | ||
| + | </ | ||
| + | |||
| * Lier le bucket aux événements afin que MinIO envoie les données | * Lier le bucket aux événements afin que MinIO envoie les données | ||
| + | |||
| + | <WRAP center round info> | ||
| + | Filtrage côté MinIO (RECOMMANDÉ) : le proxy ne reçoit QUE ce qui est attendu | ||
| + | </ | ||
| + | |||
| < | < | ||
| - | mc event add minio/test arn: | + | mc event add minio/lycee arn: |
| + | | ||
| + | --prefix administration/ | ||
| + | --prefix bts-sio/ \ | ||
| + | --prefix bts-mco/ | ||
| + | |||
| => doit renvoyer | => doit renvoyer | ||
systeme/documenso/minio.1775916377.txt.gz · Dernière modification : 2026/04/11 16:06 de techer.charles_educ-valadon-limoges.fr
