Implement tasks 1-3: sidebar order, jail activation rollback, pie chart colors
Task 1: Move Configuration to last position in sidebar NAV_ITEMS
Task 2: Add automatic rollback when jail activation fails
- Back up .local override file before writing
- Restore original file (or delete) on reload failure, health-check
failure, or jail not appearing post-reload
- Return recovered=True/False in JailActivationResponse
- Show warning/critical banner in ActivateJailDialog based on recovery
- Add _restore_local_file_sync and _rollback_activation_async helpers
- Add 3 new tests: rollback on reload failure, health-check failure,
and double failure (recovered=False)
Task 3: Color pie chart legend labels to match their slice color
- legendFormatter now returns ReactNode with span style={{ color }}
- Import LegendPayload from recharts/types/component/DefaultLegendContent
This commit was merged in pull request #1.
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
||||
Input,
|
||||
MessageBar,
|
||||
MessageBarBody,
|
||||
MessageBarTitle,
|
||||
Spinner,
|
||||
Text,
|
||||
tokens,
|
||||
@@ -85,6 +86,7 @@ export function ActivateJailDialog({
|
||||
const [logpath, setLogpath] = useState("");
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [recoveryStatus, setRecoveryStatus] = useState<"recovered" | "unrecovered" | null>(null);
|
||||
|
||||
// Pre-activation validation state
|
||||
const [validating, setValidating] = useState(false);
|
||||
@@ -98,6 +100,7 @@ export function ActivateJailDialog({
|
||||
setPort("");
|
||||
setLogpath("");
|
||||
setError(null);
|
||||
setRecoveryStatus(null);
|
||||
setValidationIssues([]);
|
||||
setValidationWarnings([]);
|
||||
};
|
||||
@@ -153,10 +156,17 @@ export function ActivateJailDialog({
|
||||
activateJail(jail.name, overrides)
|
||||
.then((result) => {
|
||||
if (!result.active) {
|
||||
// Backend rejected the activation (e.g. missing logpath or filter).
|
||||
// Show the server's message and keep the dialog open so the user
|
||||
// can read the explanation without the dialog disappearing.
|
||||
setError(result.message);
|
||||
if (result.recovered === true) {
|
||||
// Activation failed but the system rolled back automatically.
|
||||
setRecoveryStatus("recovered");
|
||||
} else if (result.recovered === false) {
|
||||
// Activation failed and rollback also failed.
|
||||
setRecoveryStatus("unrecovered");
|
||||
} else {
|
||||
// Backend rejected before writing (e.g. missing logpath or filter).
|
||||
// Show the server's message and keep the dialog open.
|
||||
setError(result.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (result.validation_warnings.length > 0) {
|
||||
@@ -323,6 +333,31 @@ export function ActivateJailDialog({
|
||||
onChange={(_e, d) => { setLogpath(d.value); }}
|
||||
/>
|
||||
</Field>
|
||||
{recoveryStatus === "recovered" && (
|
||||
<MessageBar
|
||||
intent="warning"
|
||||
style={{ marginTop: tokens.spacingVerticalS }}
|
||||
>
|
||||
<MessageBarBody>
|
||||
<MessageBarTitle>Activation Failed — System Recovered</MessageBarTitle>
|
||||
Activation of jail “{jail.name}” failed. The server
|
||||
has been automatically recovered.
|
||||
</MessageBarBody>
|
||||
</MessageBar>
|
||||
)}
|
||||
{recoveryStatus === "unrecovered" && (
|
||||
<MessageBar
|
||||
intent="error"
|
||||
style={{ marginTop: tokens.spacingVerticalS }}
|
||||
>
|
||||
<MessageBarBody>
|
||||
<MessageBarTitle>Activation Failed — Manual Intervention Required</MessageBarTitle>
|
||||
Activation of jail “{jail.name}” failed and
|
||||
automatic recovery was unsuccessful. Manual intervention is
|
||||
required.
|
||||
</MessageBarBody>
|
||||
</MessageBar>
|
||||
)}
|
||||
{error && (
|
||||
<MessageBar
|
||||
intent="error"
|
||||
|
||||
Reference in New Issue
Block a user