Signals: an admin chooses whether the server's TypeSafe key pays for everyone (#271)
With signals_on_server set and TYPESAFE_KEY present, the judge asks Jev about everyone's Signals on the server's key; otherwise each person turns Signals on in Settings with a key of their own, which is now checked with TypeSafe before it is saved. The Signals folder shows only when they are on. The browser suite's daemon inherited the shell's TYPESAFE_KEY, so fixture feeds were sent to the real TypeSafe to be categorised; it is blanked now, and an empty key counts as none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2579,7 +2579,8 @@ pub struct Ask {
|
||||
#[derive(Debug)]
|
||||
pub struct Unjudged {
|
||||
pub user_id: i64,
|
||||
pub key: String,
|
||||
/// Their own key; None when the server's is used for everyone.
|
||||
pub key: Option<String>,
|
||||
pub feed_id: String,
|
||||
pub guid: String,
|
||||
pub feed_title: Option<String>,
|
||||
@@ -2704,8 +2705,9 @@ impl Db {
|
||||
|
||||
/// Up to `limit` items, newest first, that someone with a key has a Signal not yet asked
|
||||
/// about: from the feeds they subscribe to, the last `SIGNAL_DAYS`, and not hidden from them.
|
||||
/// `skip` are people whose key TypeSafe has refused.
|
||||
pub async fn unjudged(&self, skip: &[i64], limit: i64) -> Result<Vec<Unjudged>> {
|
||||
/// `skip` are people whose key TypeSafe has refused. With `everyone`, the server's key is
|
||||
/// used, and people without a key of their own are asked for too (#271).
|
||||
pub async fn unjudged(&self, skip: &[i64], limit: i64, everyone: bool) -> Result<Vec<Unjudged>> {
|
||||
let mut a = Args::default();
|
||||
let since = a.p(now() - SIGNAL_DAYS * 86_400);
|
||||
let skip = if skip.is_empty() {
|
||||
@@ -2714,6 +2716,7 @@ impl Db {
|
||||
format!("AND g.user_id NOT IN ({})", skip.iter().map(|u| a.p(*u)).collect::<Vec<_>>().join(", "))
|
||||
};
|
||||
let limit = a.p(limit);
|
||||
let keyed = if everyone { "true" } else { "u.typesafe_key IS NOT NULL" };
|
||||
let sql = format!(
|
||||
"SELECT g.id AS signal_id, g.name, g.keywords, g.description AS signal_description, g.user_id, u.typesafe_key, e.feed_id, e.guid, e.title,
|
||||
e.description, f.title AS feed_title
|
||||
@@ -2722,7 +2725,7 @@ impl Db {
|
||||
JOIN subscriptions s ON s.user_id = g.user_id
|
||||
JOIN entries e ON e.feed_id = s.feed_id
|
||||
LEFT JOIN feeds f ON f.id = e.feed_id
|
||||
WHERE u.typesafe_key IS NOT NULL {skip}
|
||||
WHERE {keyed} {skip}
|
||||
AND coalesce(e.published, e.first_seen) >= {since}
|
||||
AND NOT EXISTS (SELECT 1 FROM signal_verdicts v
|
||||
WHERE v.signal_id = g.id AND v.feed_id = e.feed_id AND v.guid = e.guid)
|
||||
@@ -3394,19 +3397,23 @@ mod tests {
|
||||
let food = db.add_signal(1, "Food", &words(&["baking"]), Some("Bread and cakes")).await.unwrap();
|
||||
db.add_signal(2, "Theirs", &words(&["x"]), None).await.unwrap();
|
||||
|
||||
assert!(db.unjudged(&[], 50).await.unwrap().is_empty(), "nobody has a key yet");
|
||||
assert!(db.unjudged(&[], 50, false).await.unwrap().is_empty(), "nobody has a key yet");
|
||||
let everyone = db.unjudged(&[], 50, true).await.unwrap();
|
||||
let mut seen: Vec<_> = everyone.iter().map(|u| (u.user_id, u.guid.as_str(), u.key.is_none())).collect();
|
||||
seen.sort();
|
||||
assert_eq!(seen, [(1, "x", true), (1, "y", true), (2, "x", true)], "on the server's key, everyone's Signals, no key of their own needed");
|
||||
db.set_typesafe_key(1, Some("k")).await.unwrap();
|
||||
let queue = db.unjudged(&[], 50).await.unwrap();
|
||||
let queue = db.unjudged(&[], 50, false).await.unwrap();
|
||||
let mut seen: Vec<_> = queue.iter().map(|u| (u.guid.as_str(), u.signals.len())).collect();
|
||||
seen.sort();
|
||||
assert_eq!(seen, [("x", 2), ("y", 2)], "each recent subscribed item once, with both Signals; old and unsubscribed left out");
|
||||
let ask = queue[0].signals.iter().find(|a| a.id == food).unwrap();
|
||||
assert_eq!((ask.name.as_str(), ask.description.as_deref()), ("Food", Some("Bread and cakes")), "Jev is asked about the name and the sentence");
|
||||
assert!(db.unjudged(&[1], 50).await.unwrap().is_empty(), "a refused key is passed over");
|
||||
assert!(db.unjudged(&[1], 50, false).await.unwrap().is_empty(), "a refused key is passed over");
|
||||
|
||||
db.record_verdicts("f", "x", &[(f1, true), (food, false)]).await.unwrap();
|
||||
db.record_verdicts("g", "y", &[(f1, false), (food, true)]).await.unwrap();
|
||||
assert!(db.unjudged(&[], 50).await.unwrap().is_empty(), "nothing is asked twice");
|
||||
assert!(db.unjudged(&[], 50, false).await.unwrap().is_empty(), "nothing is asked twice");
|
||||
|
||||
let order = order_sql("published", "desc");
|
||||
let page = db.signal_entries(1, f1, Filter::All, None, 0, 50, &order).await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user