1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! Server-level operator actions (as opposed to editable settings — those
//! live in `server_settings`).
//!
//! POST /api/server/restart (operator) restart the backend service
//!
//! The backend restarts **itself** rather than dispatching a job to the
//! co-located agent: it exits with a non-zero code, and the Windows SCM's
//! configured failure-recovery actions relaunch the service a few seconds
//! later (`sc.exe failure … actions= restart/5000/… ` + `failureflag 1`,
//! set by `scripts/deploy/backend.ps1`). This is the exact mechanism the
//! boot sentinel already uses to restart after a rollback (`exit(1)` in
//! `main`), so there's no agent, no NATS command, and no "which pc_id am I"
//! discovery to get wrong. Motivating use: applying a `server_settings` mail
//! (SMTP) change, which the backend only reads at startup (#884 / #962).
use Duration;
use Json;
use State;
use StatusCode;
use Serialize;
use warn;
use crateAppState;
use crateaudit;
use crateCaller;
/// How long the handler waits after responding before exiting, so the `202`
/// flushes to the operator's browser before the process dies. Short — just
/// enough to lose the race against the response write.
///
/// This is a heuristic, not a guarantee: it assumes the SPA talks to the
/// backend directly (the deployment topology here — a LAN host serving
/// `:8080`, no buffering reverse proxy in front). Behind a proxy that buffers
/// the response, 750 ms could be too short and the operator would see a
/// connection error even though the restart was accepted — cosmetic (the SPA
/// polls `/health` and reloads when the service is back), not a correctness
/// issue. Likewise `std::process::exit(1)` is a deliberate, non-graceful
/// process-wide kill: any other in-flight request is dropped, matching the
/// boot sentinel's `exit(1)` restart contract (a graceful drain isn't worth
/// the machinery for an operator-initiated, already-confirmed restart).
const RESTART_GRACE: Duration = from_millis;
/// Body of the `202 Accepted` reply to a restart request.
/// `POST /api/server/restart` — restart the backend service (operator).
///
/// Audit-logs the request, schedules a delayed `std::process::exit(1)`, and
/// returns `202` immediately. The non-zero exit trips the SCM failure-
/// recovery actions, which relaunch the service (~5 s). The HTTP API is
/// briefly unavailable in between — the SPA button confirms first and shows
/// a hint.
///
/// Dev/console path (no SCM): the process just exits and does **not** come
/// back — restart it by hand. In production the service always runs under
/// SCM with the recovery actions configured, so it returns on its own.
pub async