mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-18 20:56:43 +02:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LG2LLJuswrrTTPKJmunEVf
42 lines
1.6 KiB
Rust
42 lines
1.6 KiB
Rust
//! Journaux serveur des jobs (liste + téléchargement).
|
|
//!
|
|
//! `GET /api/logs` → liste des journaux disponibles (JSON)
|
|
//! `GET /api/logs?id=<jobId>` → le journal complet en pièce jointe (text/plain)
|
|
//!
|
|
//! Réservé aux admins : un journal d'extraction contient les diagnostics du
|
|
//! modèle Qlik et les codes articles manipulés (jamais de secret).
|
|
|
|
use crate::auth::AdminUser;
|
|
use crate::logcapture::{list_captures, read_capture};
|
|
use axum::extract::Query;
|
|
use axum::http::{header, StatusCode};
|
|
use axum::response::{IntoResponse, Response};
|
|
use axum::Json;
|
|
use serde::Deserialize;
|
|
use serde_json::json;
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct LogsQuery {
|
|
id: Option<String>,
|
|
}
|
|
|
|
pub async fn get_logs(_admin: AdminUser, Query(q): Query<LogsQuery>) -> Response {
|
|
let Some(id) = q.id.filter(|s| !s.is_empty()) else {
|
|
return Json(json!({ "success": true, "logs": list_captures() })).into_response();
|
|
};
|
|
let Some(contenu) = read_capture(&id) else {
|
|
return (StatusCode::NOT_FOUND, Json(json!({ "error": "Journal introuvable ou expiré" }))).into_response();
|
|
};
|
|
// Nom de fichier assaini : l'id finit dans un en-tête HTTP.
|
|
let nom: String = id.chars().map(|c| if c.is_ascii_alphanumeric() || "_.-".contains(c) { c } else { '_' }).collect();
|
|
(
|
|
[
|
|
(header::CONTENT_TYPE, "text/plain; charset=utf-8".to_string()),
|
|
(header::CONTENT_DISPOSITION, format!("attachment; filename=\"{nom}.log\"")),
|
|
(header::CACHE_CONTROL, "no-store".to_string()),
|
|
],
|
|
contenu,
|
|
)
|
|
.into_response()
|
|
}
|