db: persist jupes across restart (event-source them)
All checks were successful
CI / check (push) Successful in 3m50s
All checks were successful
CI / check (push) Successful in 3m50s
A fold-parity property test caught it: jupes were plain in-memory Db fields that logged no event, so after any services restart every juped server was silently un-juped and could relink. Moved jupes into NetData (like akills/ forbids/groups) and event-sourced them with JupeAdded/JupeRemoved (Local scope), so a jupe now replays from the log. Adds the round-trip property test (live state == replayed state) as a permanent guard against this bug class.
This commit is contained in:
parent
61005f52f5
commit
90167dd46f
6 changed files with 119 additions and 13 deletions
|
|
@ -221,25 +221,28 @@ impl Db {
|
|||
/// Jupe a server name: allocate a fake sid, store it, return the sid to
|
||||
/// introduce (or the existing sid if the name is already juped).
|
||||
pub fn jupe_add(&mut self, name: &str, reason: &str) -> String {
|
||||
if let Some(j) = self.jupes.iter().find(|j| j.name.eq_ignore_ascii_case(name)) {
|
||||
if let Some(j) = self.net.jupes.iter().find(|j| j.name.eq_ignore_ascii_case(name)) {
|
||||
return j.sid.clone();
|
||||
}
|
||||
let sid = jupe_sid(self.jupe_seq);
|
||||
self.jupe_seq += 1;
|
||||
self.jupes.push(Jupe { name: name.to_string(), sid: sid.clone(), reason: reason.to_string() });
|
||||
let sid = jupe_sid(self.net.jupe_seq);
|
||||
// Persisted (Local scope) so a juped server stays juped across a restart.
|
||||
let _ = self.log.append(Event::JupeAdded { name: name.to_string(), sid: sid.clone(), reason: reason.to_string() });
|
||||
self.net.jupe_seq += 1;
|
||||
self.net.jupes.push(Jupe { name: name.to_string(), sid: sid.clone(), reason: reason.to_string() });
|
||||
sid
|
||||
}
|
||||
|
||||
/// Lift a jupe. Returns the sid to squit if it existed.
|
||||
pub fn jupe_del(&mut self, name: &str) -> Option<String> {
|
||||
let sid = self.jupes.iter().find(|j| j.name.eq_ignore_ascii_case(name)).map(|j| j.sid.clone())?;
|
||||
self.jupes.retain(|j| !j.name.eq_ignore_ascii_case(name));
|
||||
let sid = self.net.jupes.iter().find(|j| j.name.eq_ignore_ascii_case(name)).map(|j| j.sid.clone())?;
|
||||
let _ = self.log.append(Event::JupeRemoved { name: name.to_string() });
|
||||
self.net.jupes.retain(|j| !j.name.eq_ignore_ascii_case(name));
|
||||
Some(sid)
|
||||
}
|
||||
|
||||
/// The juped servers, as (name, sid, reason).
|
||||
pub fn jupes(&self) -> Vec<(String, String, String)> {
|
||||
self.jupes.iter().map(|j| (j.name.clone(), j.sid.clone(), j.reason.clone())).collect()
|
||||
self.net.jupes.iter().map(|j| (j.name.clone(), j.sid.clone(), j.reason.clone())).collect()
|
||||
}
|
||||
|
||||
/// File an abuse report, rate-limited per reporter. Returns the new report's
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue