Compare commits
23 Commits
letterbox-
...
letterbox-
| Author | SHA1 | Date | |
|---|---|---|---|
| 93d569fb14 | |||
| f86a5f464d | |||
| 956c20b156 | |||
| 1eb498712b | |||
| f12979c0be | |||
| 4665f34e54 | |||
| bbdc35061c | |||
| f11f0b4d23 | |||
| c7c47e4a73 | |||
| c3835522b2 | |||
| dfa80f9046 | |||
| b8dfdabf8d | |||
| bbcf52b006 | |||
| f92c05cd28 | |||
| 885bbe0a8c | |||
| 8b1d111837 | |||
| 08abf31fa9 | |||
| fa99959508 | |||
| 0f6af0f475 | |||
| 4c486e9168 | |||
| 109d380ea7 | |||
| 4244fa0d82 | |||
| 4b15e71893 |
17
Cargo.lock
generated
17
Cargo.lock
generated
@@ -3034,7 +3034,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "letterbox-notmuch"
|
name = "letterbox-notmuch"
|
||||||
version = "0.17.10"
|
version = "0.17.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
@@ -3049,7 +3049,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "letterbox-procmail2notmuch"
|
name = "letterbox-procmail2notmuch"
|
||||||
version = "0.17.10"
|
version = "0.17.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -3062,7 +3062,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "letterbox-server"
|
name = "letterbox-server"
|
||||||
version = "0.17.10"
|
version = "0.17.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ammonia",
|
"ammonia",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -3080,10 +3080,9 @@ dependencies = [
|
|||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"headers",
|
"headers",
|
||||||
"html-escape",
|
"html-escape",
|
||||||
"letterbox-notmuch 0.17.10",
|
"letterbox-notmuch 0.17.22",
|
||||||
"letterbox-shared 0.17.10",
|
"letterbox-shared 0.17.22",
|
||||||
"linkify",
|
"linkify",
|
||||||
"log",
|
|
||||||
"lol_html",
|
"lol_html",
|
||||||
"mailparse",
|
"mailparse",
|
||||||
"maplit",
|
"maplit",
|
||||||
@@ -3121,10 +3120,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "letterbox-shared"
|
name = "letterbox-shared"
|
||||||
version = "0.17.10"
|
version = "0.17.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"build-info",
|
"build-info",
|
||||||
"letterbox-notmuch 0.17.10",
|
"letterbox-notmuch 0.17.22",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
@@ -3134,7 +3133,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "letterbox-web"
|
name = "letterbox-web"
|
||||||
version = "0.17.10"
|
version = "0.17.22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"build-info",
|
"build-info",
|
||||||
"build-info-build",
|
"build-info-build",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ authors = ["Bill Thiede <git@xinu.tv>"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "UNLICENSED"
|
license = "UNLICENSED"
|
||||||
publish = ["xinu"]
|
publish = ["xinu"]
|
||||||
version = "0.17.10"
|
version = "0.17.22"
|
||||||
repository = "https://git.z.xinu.tv/wathiede/letterbox"
|
repository = "https://git.z.xinu.tv/wathiede/letterbox"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|||||||
@@ -27,10 +27,9 @@ css-inline = "0.14.4"
|
|||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
headers = "0.4.0"
|
headers = "0.4.0"
|
||||||
html-escape = "0.2.13"
|
html-escape = "0.2.13"
|
||||||
letterbox-notmuch = { path = "../notmuch", version = "0.17.10", registry = "xinu" }
|
letterbox-notmuch = { path = "../notmuch", version = "0.17.22", registry = "xinu" }
|
||||||
letterbox-shared = { path = "../shared", version = "0.17.10", registry = "xinu" }
|
letterbox-shared = { path = "../shared", version = "0.17.22", registry = "xinu" }
|
||||||
linkify = "0.10.0"
|
linkify = "0.10.0"
|
||||||
log = "0.4.27"
|
|
||||||
lol_html = "2.3.0"
|
lol_html = "2.3.0"
|
||||||
mailparse = "0.16.1"
|
mailparse = "0.16.1"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
|
|||||||
@@ -200,15 +200,21 @@ async fn send_refresh_websocket_handler(
|
|||||||
None => Some(10),
|
None => Some(10),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = label_unprocessed(&nm, &pool, false, limit, "tag:unprocessed").await {
|
let mut ids = None;
|
||||||
error!("Failed to label_unprocessed: {err:?}");
|
match label_unprocessed(&nm, &pool, false, limit, "tag:unprocessed").await {
|
||||||
|
Ok(i) => ids = Some(i),
|
||||||
|
Err(err) => error!("Failed to label_unprocessed: {err:?}"),
|
||||||
};
|
};
|
||||||
connection_tracker
|
connection_tracker
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.send_message_all(WebsocketMessage::RefreshMessages)
|
.send_message_all(WebsocketMessage::RefreshMessages)
|
||||||
.await;
|
.await;
|
||||||
"refresh triggered"
|
if let Some(ids) = ids {
|
||||||
|
format!("{ids:?}")
|
||||||
|
} else {
|
||||||
|
"refresh triggered".to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn watch_new(
|
async fn watch_new(
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ use async_graphql::{
|
|||||||
use cacher::FilesystemCacher;
|
use cacher::FilesystemCacher;
|
||||||
use futures::stream;
|
use futures::stream;
|
||||||
use letterbox_notmuch::Notmuch;
|
use letterbox_notmuch::Notmuch;
|
||||||
use log::info;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
use tokio::join;
|
use tokio::join;
|
||||||
use tracing::instrument;
|
use tracing::{info, instrument};
|
||||||
|
|
||||||
#[cfg(feature = "tantivy")]
|
#[cfg(feature = "tantivy")]
|
||||||
use crate::tantivy::TantivyConnection;
|
use crate::tantivy::TantivyConnection;
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ use cacher::{Cacher, FilesystemCacher};
|
|||||||
use css_inline::{CSSInliner, InlineError, InlineOptions};
|
use css_inline::{CSSInliner, InlineError, InlineOptions};
|
||||||
pub use error::ServerError;
|
pub use error::ServerError;
|
||||||
use linkify::{LinkFinder, LinkKind};
|
use linkify::{LinkFinder, LinkKind};
|
||||||
use log::{debug, error, info, warn};
|
|
||||||
use lol_html::{
|
use lol_html::{
|
||||||
element, errors::RewritingError, html_content::ContentType, rewrite_str, text,
|
element, errors::RewritingError, html_content::ContentType, rewrite_str, text,
|
||||||
RewriteStrSettings,
|
RewriteStrSettings,
|
||||||
@@ -32,6 +31,7 @@ use reqwest::StatusCode;
|
|||||||
use scraper::{Html, Selector};
|
use scraper::{Html, Selector};
|
||||||
use sqlx::types::time::PrimitiveDateTime;
|
use sqlx::types::time::PrimitiveDateTime;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tracing::{debug, error, info, warn};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ use std::collections::HashMap;
|
|||||||
use cacher::FilesystemCacher;
|
use cacher::FilesystemCacher;
|
||||||
use futures::{stream::FuturesUnordered, StreamExt};
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
use letterbox_shared::compute_color;
|
use letterbox_shared::compute_color;
|
||||||
use log::{error, info};
|
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
use scraper::Selector;
|
use scraper::Selector;
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
use tracing::instrument;
|
use tracing::{error, info, instrument};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ use std::{
|
|||||||
|
|
||||||
use letterbox_notmuch::Notmuch;
|
use letterbox_notmuch::Notmuch;
|
||||||
use letterbox_shared::{compute_color, Rule};
|
use letterbox_shared::{compute_color, Rule};
|
||||||
use log::{error, info, warn};
|
|
||||||
use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail};
|
use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail};
|
||||||
use memmap::MmapOptions;
|
use memmap::MmapOptions;
|
||||||
use sqlx::{types::Json, PgPool};
|
use sqlx::{types::Json, PgPool};
|
||||||
use tracing::instrument;
|
use tracing::{error, info, info_span, instrument, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compute_offset_limit,
|
compute_offset_limit,
|
||||||
@@ -946,7 +945,7 @@ pub async fn label_unprocessed(
|
|||||||
dryrun: bool,
|
dryrun: bool,
|
||||||
limit: Option<usize>,
|
limit: Option<usize>,
|
||||||
query: &str,
|
query: &str,
|
||||||
) -> Result<(), ServerError> {
|
) -> Result<Box<[String]>, ServerError> {
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
let ids = nm.message_ids(query)?;
|
let ids = nm.message_ids(query)?;
|
||||||
info!(
|
info!(
|
||||||
@@ -978,6 +977,7 @@ pub async fn label_unprocessed(
|
|||||||
info!("Loaded {} rules", rules.len());
|
info!("Loaded {} rules", rules.len());
|
||||||
|
|
||||||
let ids = if let Some(limit) = limit {
|
let ids = if let Some(limit) = limit {
|
||||||
|
let limit = limit.min(ids.len());
|
||||||
&ids[..limit]
|
&ids[..limit]
|
||||||
} else {
|
} else {
|
||||||
&ids[..]
|
&ids[..]
|
||||||
@@ -988,10 +988,28 @@ pub async fn label_unprocessed(
|
|||||||
let id = format!("id:{id}");
|
let id = format!("id:{id}");
|
||||||
let files = nm.files(&id)?;
|
let files = nm.files(&id)?;
|
||||||
// Only process the first file path is multiple files have the same id
|
// Only process the first file path is multiple files have the same id
|
||||||
let path = files.iter().next().unwrap();
|
let Some(path) = files.iter().next() else {
|
||||||
|
error!("No files for message-ID {id}");
|
||||||
|
let t = "Letterbox/Bad";
|
||||||
|
nm.tag_add(t, &id)?;
|
||||||
|
let t = "unprocessed";
|
||||||
|
nm.tag_remove(t, &id)?;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let file = File::open(&path)?;
|
let file = File::open(&path)?;
|
||||||
|
info!("parsing {path}");
|
||||||
let mmap = unsafe { MmapOptions::new().map(&file)? };
|
let mmap = unsafe { MmapOptions::new().map(&file)? };
|
||||||
let m = parse_mail(&mmap)?;
|
let m = match info_span!("parse_mail", path = path).in_scope(|| parse_mail(&mmap)) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to parse {path}: {err}");
|
||||||
|
let t = "Letterbox/Bad";
|
||||||
|
nm.tag_add(t, &id)?;
|
||||||
|
let t = "unprocessed";
|
||||||
|
nm.tag_remove(t, &id)?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
let (matched_rule, add_tags) = find_tags(&rules, &m.headers);
|
let (matched_rule, add_tags) = find_tags(&rules, &m.headers);
|
||||||
if matched_rule {
|
if matched_rule {
|
||||||
if dryrun {
|
if dryrun {
|
||||||
@@ -1028,11 +1046,6 @@ pub async fn label_unprocessed(
|
|||||||
.push(id.clone());
|
.push(id.clone());
|
||||||
}
|
}
|
||||||
//nm.tag_remove("unprocessed", &id)?;
|
//nm.tag_remove("unprocessed", &id)?;
|
||||||
let t = "unprocessed".to_string();
|
|
||||||
rm_mutations
|
|
||||||
.entry(t)
|
|
||||||
.or_insert_with(|| Vec::new())
|
|
||||||
.push(id.clone());
|
|
||||||
} else {
|
} else {
|
||||||
if add_tags.is_empty() {
|
if add_tags.is_empty() {
|
||||||
let t = "Grey".to_string();
|
let t = "Grey".to_string();
|
||||||
@@ -1041,26 +1054,39 @@ pub async fn label_unprocessed(
|
|||||||
.or_insert_with(|| Vec::new())
|
.or_insert_with(|| Vec::new())
|
||||||
.push(id.clone());
|
.push(id.clone());
|
||||||
}
|
}
|
||||||
|
//nm.tag_remove("inbox", &id)?;
|
||||||
|
let t = "inbox".to_string();
|
||||||
|
rm_mutations
|
||||||
|
.entry(t)
|
||||||
|
.or_insert_with(|| Vec::new())
|
||||||
|
.push(id.clone());
|
||||||
}
|
}
|
||||||
|
let t = "unprocessed".to_string();
|
||||||
|
rm_mutations
|
||||||
|
.entry(t)
|
||||||
|
.or_insert_with(|| Vec::new())
|
||||||
|
.push(id.clone());
|
||||||
}
|
}
|
||||||
println!("Adding {} distinct labels", add_mutations.len());
|
info!("Adding {} distinct labels", add_mutations.len());
|
||||||
for (tag, ids) in add_mutations.iter() {
|
for (tag, ids) in add_mutations.iter() {
|
||||||
println!(" {tag}: {}", ids.len());
|
info!(" {tag}: {}", ids.len());
|
||||||
if !dryrun {
|
if !dryrun {
|
||||||
let ids: Vec<_> = ids.iter().map(|s| s.as_str()).collect();
|
let ids: Vec<_> = ids.iter().map(|s| s.as_str()).collect();
|
||||||
nm.tags_add(tag, &ids)?;
|
info_span!("tags_add", tag = tag, count = ids.len())
|
||||||
|
.in_scope(|| nm.tags_add(tag, &ids))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("Removing {} distinct labels", rm_mutations.len());
|
info!("Removing {} distinct labels", rm_mutations.len());
|
||||||
for (tag, ids) in rm_mutations.iter() {
|
for (tag, ids) in rm_mutations.iter() {
|
||||||
println!(" {tag}: {}", ids.len());
|
info!(" {tag}: {}", ids.len());
|
||||||
if !dryrun {
|
if !dryrun {
|
||||||
let ids: Vec<_> = ids.iter().map(|s| s.as_str()).collect();
|
let ids: Vec<_> = ids.iter().map(|s| s.as_str()).collect();
|
||||||
nm.tags_remove(tag, &ids)?;
|
info_span!("tags_remove", tag = tag, count = ids.len())
|
||||||
|
.in_scope(|| nm.tags_remove(tag, &ids))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(ids.into())
|
||||||
}
|
}
|
||||||
fn find_tags<'a, 'b>(rules: &'a [Rule], headers: &'b [MailHeader]) -> (bool, HashSet<&'a str>) {
|
fn find_tags<'a, 'b>(rules: &'a [Rule], headers: &'b [MailHeader]) -> (bool, HashSet<&'a str>) {
|
||||||
let mut matched_rule = false;
|
let mut matched_rule = false;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ version.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
build-info = "0.0.40"
|
build-info = "0.0.40"
|
||||||
letterbox-notmuch = { path = "../notmuch", version = "0.17.10", registry = "xinu" }
|
letterbox-notmuch = { path = "../notmuch", version = "0.17.22", registry = "xinu" }
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
sqlx = "0.8.5"
|
sqlx = "0.8.5"
|
||||||
|
|||||||
Reference in New Issue
Block a user