feat(signature): Signaturen beim Report-Upload behalten + Cron-Cleanup nach Frist
Bisher loeschte die Report-Pipeline die Unterschriften nach erfolgreichem DOCUframe-Upload. Wir brauchen die Signatur-Dateien aber weiterhin, daher: - ProcessDeliveryReportUseCase: Signatur-Loeschung (delete_for_delivery) aus dem Cleanup entfernt + SignatureStorage-Dependency raus (Report-PDF/Bild-Notiz- Cleanup bleibt). - SignatureStorage: neue Methode delete_older_than(max_age) -> Anzahl; lokaler Adapter loescht PNGs aelter als die Frist (per mtime). - Config [signature]: retention_days (Default 90, 0 = aus) + cleanup_cron (Default taeglich 04:00). - main.rs: Signatur-Cleanup-Scheduler (gated retention_days > 0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -102,7 +102,12 @@ impl Config {
|
||||
app_names = ?self.gsd.app_names,
|
||||
"cfg.gsd"
|
||||
);
|
||||
tracing::info!(storage_dir = %self.signature.storage_dir, "cfg.signature");
|
||||
tracing::info!(
|
||||
storage_dir = %self.signature.storage_dir,
|
||||
retention_days = self.signature.retention_days,
|
||||
cleanup_cron = %self.signature.cleanup_cron,
|
||||
"cfg.signature"
|
||||
);
|
||||
tracing::info!(storage_dir = %self.attachment.storage_dir, "cfg.attachment (Bild-Notizen lokal)");
|
||||
tracing::info!(
|
||||
storage_dir = %self.report.storage_dir,
|
||||
@ -265,12 +270,22 @@ pub struct SignatureConfig {
|
||||
/// Basis-Verzeichnis für die PNG-Dateien (wird beim Start angelegt).
|
||||
#[serde(default = "default_signature_dir")]
|
||||
pub storage_dir: String,
|
||||
/// Aufbewahrungsfrist der lokalen Unterschrifts-Dateien in Tagen. Ein
|
||||
/// Cron-Job (`cleanup_cron`) löscht Dateien, die älter sind. `0` = Cleanup
|
||||
/// AUS (Unterschriften werden nie automatisch gelöscht).
|
||||
#[serde(default = "default_signature_retention_days")]
|
||||
pub retention_days: u32,
|
||||
/// Cron (6-stellig, inkl. Sekunden) für den Signatur-Cleanup.
|
||||
#[serde(default = "default_signature_cleanup_cron")]
|
||||
pub cleanup_cron: String,
|
||||
}
|
||||
|
||||
impl Default for SignatureConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
storage_dir: default_signature_dir(),
|
||||
retention_days: default_signature_retention_days(),
|
||||
cleanup_cron: default_signature_cleanup_cron(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -279,6 +294,15 @@ fn default_signature_dir() -> String {
|
||||
"./data/signatures".to_string()
|
||||
}
|
||||
|
||||
fn default_signature_retention_days() -> u32 {
|
||||
90
|
||||
}
|
||||
|
||||
fn default_signature_cleanup_cron() -> String {
|
||||
// täglich 04:00
|
||||
"0 0 4 * * *".to_string()
|
||||
}
|
||||
|
||||
/// Lokaler Speicher für hochgeladene Bild-Notizen. Statt DOCUframe landen die
|
||||
/// Bilder pro Belegnummer in einem Unterordner (`<dir>/<Belegnummer>/…`).
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
|
||||
@ -46,6 +46,7 @@ use holzleitner_application::usecases::{
|
||||
UpdateServiceUseCase, UploadDeliveryNoteImageUseCase,
|
||||
};
|
||||
use holzleitner_application::ports::DeliveryReportJobRepository;
|
||||
use holzleitner_application::ports::SignatureStorage;
|
||||
use holzleitner_application::ports::{SyncRunRepository, SyncTrigger};
|
||||
use holzleitner_infrastructure::auth::{
|
||||
KeycloakAdapterConfig, KeycloakAdminClient, KeycloakAdminConfig, KeycloakAuthService,
|
||||
@ -271,7 +272,6 @@ pub(crate) async fn run_app(
|
||||
gsd_service.clone(),
|
||||
attachment_repository.clone(),
|
||||
attachment_storage.clone(),
|
||||
signature_storage.clone(),
|
||||
report_sink.clone(),
|
||||
));
|
||||
|
||||
@ -368,7 +368,7 @@ pub(crate) async fn run_app(
|
||||
Arc::new(ApplyDeliveryActionUseCase::new(delivery_repository.clone()));
|
||||
let complete_delivery = Arc::new(CompleteDeliveryUseCase::new(
|
||||
delivery_completion_repository,
|
||||
signature_storage,
|
||||
signature_storage.clone(),
|
||||
car_repository.clone(),
|
||||
if cfg.erp.writeback_enabled {
|
||||
Some(push_completion_to_erp.clone())
|
||||
@ -631,6 +631,51 @@ pub(crate) async fn run_app(
|
||||
None
|
||||
};
|
||||
|
||||
// --- Signatur-Cleanup-Scheduler ------------------------------------
|
||||
// Unterschriften bleiben nach dem Report-Upload bewusst erhalten (wir
|
||||
// brauchen die Dateien) und werden hier periodisch gelöscht, sobald sie
|
||||
// älter als die Aufbewahrungsfrist sind. retention_days = 0 ⇒ deaktiviert.
|
||||
let _signature_cleanup_scheduler = if cfg.signature.retention_days > 0 {
|
||||
let scheduler = JobScheduler::new()
|
||||
.await
|
||||
.context("Signatur-Cleanup-JobScheduler konnte nicht erstellt werden")?;
|
||||
let signatures = signature_storage.clone();
|
||||
let max_age = Duration::from_secs(cfg.signature.retention_days as u64 * 86_400);
|
||||
let job = Job::new_async(cfg.signature.cleanup_cron.as_str(), move |_uuid, _lock| {
|
||||
let signatures = signatures.clone();
|
||||
Box::pin(async move {
|
||||
match signatures.delete_older_than(max_age).await {
|
||||
Ok(0) => {}
|
||||
Ok(n) => tracing::info!(
|
||||
deleted = n,
|
||||
"signature_cleanup: abgelaufene Unterschriften gelöscht"
|
||||
),
|
||||
Err(e) => tracing::error!(error = %e, "signature_cleanup fehlgeschlagen"),
|
||||
}
|
||||
})
|
||||
})
|
||||
.context("Signatur-Cleanup-Job konnte nicht erstellt werden")?;
|
||||
scheduler
|
||||
.add(job)
|
||||
.await
|
||||
.context("Signatur-Cleanup-Job add fehlgeschlagen")?;
|
||||
scheduler
|
||||
.start()
|
||||
.await
|
||||
.context("Signatur-Cleanup-Scheduler-Start fehlgeschlagen")?;
|
||||
tracing::info!(
|
||||
cron = %cfg.signature.cleanup_cron,
|
||||
retention_days = cfg.signature.retention_days,
|
||||
"signature_cleanup scheduler gestartet"
|
||||
);
|
||||
Some(scheduler)
|
||||
} else {
|
||||
tracing::info!(
|
||||
"signature_cleanup deaktiviert (signature.retention_days = 0) — Unterschriften werden nie automatisch gelöscht"
|
||||
);
|
||||
None
|
||||
};
|
||||
|
||||
let addr: SocketAddr = format!("{}:{}", cfg.server.host, cfg.server.port)
|
||||
.parse()
|
||||
.with_context(|| format!("ungültige Adresse {}:{}", cfg.server.host, cfg.server.port))?;
|
||||
|
||||
Reference in New Issue
Block a user