feat: server-authoritative case window via users.toml
The /api/oneliners time window is now read per-user from users.toml (window_hours, default 72h). Clients no longer carry a window: client.toml oneliner_window_hours, SyncConfig.window_hours, the ?hours=N query param, and the render_case_list cutoff filter are gone. ETag suffix keeps the effective hours so an admin edit to users.toml invalidates client caches on the next request. OnelinersResponse.window_hours stays in the wire format, but now exists solely to anchor client reconciliation.
This commit is contained in:
@@ -19,9 +19,7 @@ use doctate_client_core::{
|
||||
pick_footer_status, run_startup_cleanup, write_sidecar,
|
||||
};
|
||||
use doctate_common::oneliners::OnelinerState;
|
||||
use doctate_common::timestamp::{
|
||||
extract_hhmm, now_rfc3339, parse_rfc3339, recorded_at_to_filename_stem,
|
||||
};
|
||||
use doctate_common::timestamp::{extract_hhmm, now_rfc3339, recorded_at_to_filename_stem};
|
||||
use eframe::egui;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::sync::{mpsc, watch};
|
||||
@@ -158,7 +156,6 @@ impl DoctateApp {
|
||||
.config
|
||||
.as_ref()
|
||||
.and_then(|c| c.oneliner_poll_interval_seconds),
|
||||
oneliner_window_hours: self.config.as_ref().and_then(|c| c.oneliner_window_hours),
|
||||
};
|
||||
if let Err(e) = crate::config::save(&cfg) {
|
||||
error!(error = %e, "config save failed");
|
||||
@@ -474,13 +471,9 @@ impl DoctateApp {
|
||||
|
||||
fn render_case_list(&self, ui: &mut egui::Ui) -> Option<UiAction> {
|
||||
let snapshot = self.snapshot_rx.borrow().clone();
|
||||
let window = self.config.as_ref().map(Config::window_hours).unwrap_or(72);
|
||||
let cutoff = time::OffsetDateTime::now_utc() - time::Duration::hours(window as i64);
|
||||
|
||||
let visible: Vec<&CaseMarker> = snapshot
|
||||
.iter()
|
||||
.filter(|m| parse_rfc3339(&m.last_activity_at).is_none_or(|t| t >= cutoff))
|
||||
.collect();
|
||||
// Snapshot is already server-filtered — the server's `window_hours`
|
||||
// per user (users.toml) is the single source of truth.
|
||||
let visible: Vec<&CaseMarker> = snapshot.iter().collect();
|
||||
|
||||
if visible.is_empty() {
|
||||
ui.add_space(8.0);
|
||||
@@ -634,12 +627,10 @@ fn spawn_worker(
|
||||
) {
|
||||
let _guard = runtime.enter();
|
||||
let poll_interval = config.poll_interval();
|
||||
let window_hours = config.window_hours();
|
||||
let sync_config = SyncConfig {
|
||||
server_url: config.server_url,
|
||||
api_key: config.api_key,
|
||||
poll_interval,
|
||||
window_hours,
|
||||
};
|
||||
let (sync, events_rx) =
|
||||
ServerSync::spawn(http, sync_config, case_store, snapshot_cache, pending_dir);
|
||||
|
||||
Reference in New Issue
Block a user