feat(config): configurable restart_apply_mode + graceful shutdown (Stage 3b)

- console section: restart_apply_mode (manual | auto), an operator setting (runtime-safe,
  DB-overridable via /admin/config), env RESTART_APPLY_MODE default manual.
- Graceful shutdown: main serve now awaits SIGTERM/Ctrl-C OR an in-app shutdown signal
  (AppState.shutdown Notify) via with_graceful_shutdown, draining in-flight requests.
- PUT /admin/config in `auto` mode self-restarts shortly after responding to apply a
  restart-required change (systemd Restart=always brings it back with the new config);
  `manual` just flags restart_pending. The console warns before an auto save.

Stage 3 complete: operator config is readable + editable through the real validated,
audited, precedence-based layer; runtime-safe edits apply live, infra edits restart.
cargo all-targets clean, lib tests pass.
This commit is contained in:
jwinterm
2026-07-18 14:08:33 -04:00
parent 22d362b62f
commit a80fa46daf
6 changed files with 130 additions and 1 deletions
+35
View File
@@ -171,6 +171,32 @@ pub struct Config {
pub messaging: MessagingConfig,
pub premium: PremiumConfig,
pub feed: FeedConfig,
pub console: ConsoleConfig,
}
/// How a restart-required config change is applied.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum RestartApplyMode {
/// Persist + flag; the operator applies it with a graceful restart.
Manual,
/// The instance self-restarts after saving (the console warns first). Requires the
/// service to be brought back on exit (systemd `Restart=always`).
Auto,
}
impl RestartApplyMode {
pub fn as_str(&self) -> &'static str {
match self {
RestartApplyMode::Manual => "manual",
RestartApplyMode::Auto => "auto",
}
}
}
/// Operator-console preferences (runtime-safe, DB-overridable via /admin/config).
#[derive(Clone)]
pub struct ConsoleConfig {
pub restart_apply_mode: RestartApplyMode,
}
#[derive(Clone)]
@@ -912,6 +938,12 @@ impl Config {
allowlist_npubs: env_list("FEED_ALLOWLIST_NPUBS"),
extra_relays: env_list("FEED_EXTRA_RELAYS"),
},
console: ConsoleConfig {
restart_apply_mode: match env_or("RESTART_APPLY_MODE", "manual").as_str() {
"auto" => RestartApplyMode::Auto,
_ => RestartApplyMode::Manual,
},
},
};
cfg.validate()?;
@@ -1502,6 +1534,9 @@ mod tests {
allowlist_npubs: Vec::new(),
extra_relays: Vec::new(),
},
console: ConsoleConfig {
restart_apply_mode: RestartApplyMode::Manual,
},
}
}