feat: add Telegram notification system with granular event toggles

Add notifications crate shared between API and indexer to send Telegram
messages on scan/thumbnail/conversion completion/failure, metadata linking,
batch and refresh events. Configurable via a new Notifications tab in the
backoffice settings with per-event toggle switches grouped by category.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 17:24:43 +01:00
parent bd74c9e3e3
commit 81d1586501
22 changed files with 1096 additions and 8 deletions

View File

@@ -14,6 +14,7 @@ use crate::{
utils,
AppState,
};
use std::collections::HashSet;
#[derive(Serialize)]
pub struct JobStats {
@@ -22,6 +23,7 @@ pub struct JobStats {
pub removed_files: usize,
pub errors: usize,
pub warnings: usize,
pub new_series: usize,
}
const BATCH_SIZE: usize = 100;
@@ -106,6 +108,18 @@ pub async fn scan_library_discovery(
HashMap::new()
};
// Track existing series names for new_series counting
let existing_series: HashSet<String> = sqlx::query_scalar(
"SELECT DISTINCT COALESCE(NULLIF(series, ''), 'unclassified') FROM books WHERE library_id = $1",
)
.bind(library_id)
.fetch_all(&state.pool)
.await
.unwrap_or_default()
.into_iter()
.collect();
let mut seen_new_series: HashSet<String> = HashSet::new();
let mut seen: HashMap<String, bool> = HashMap::new();
let mut library_processed_count = 0i32;
let mut last_progress_update = std::time::Instant::now();
@@ -382,6 +396,12 @@ pub async fn scan_library_discovery(
let book_id = Uuid::new_v4();
let file_id = Uuid::new_v4();
// Track new series
let series_key = parsed.series.as_deref().unwrap_or("unclassified").to_string();
if !existing_series.contains(&series_key) && seen_new_series.insert(series_key) {
stats.new_series += 1;
}
books_to_insert.push(BookInsert {
book_id,
library_id,