mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-19 06:46:23 +00:00
feat(resources): reclaim banner controls and accurate reclaim math (#1318)
* feat(resources): reclaim banner controls and accurate reclaim math Make the Resources Hub reclaim banner match what it advertises and give operators control over when it appears. - "Review & prune" now reclaims every category the banner lists (unused images, stopped containers, and dangling volumes) instead of images only, so the banner clears in one action. Pruning runs volumes first, while stopped containers still reference their named volumes, so a stopped stack's data is never cascaded into deletion. - Add a "Show reclaimable-space banner" toggle under Settings, System, Docker hygiene (on by default, per node) and a dismiss control on the banner that snoozes it until the reclaimable total grows again. - Fix the reclaimable-space math: count only containers a prune can actually remove (created, exited, dead) and size them by their writable layer, so a small, un-prunable remainder no longer keeps the banner up. * fix(resources): show the reclaim banner when the settings fetch fails A failed or empty /settings load left the banner's enabled flag at the previously active node's value, so switching from a node with the banner turned off to a node whose /settings errored kept the new node's banner hidden. Set the flag unconditionally after the staleness guard so a failed fetch falls back to the default-on state for the current node.
This commit is contained in:
@@ -133,7 +133,7 @@ function SettingsSkeleton() {
|
||||
);
|
||||
}
|
||||
|
||||
type SystemFields = Pick<PatchableSettings, 'host_cpu_limit' | 'host_ram_limit' | 'host_disk_limit' | 'host_alert_suppression_mins' | 'docker_janitor_gb' | 'global_crash' | 'prune_on_update' | 'mesh_auto_recreate'>;
|
||||
type SystemFields = Pick<PatchableSettings, 'host_cpu_limit' | 'host_ram_limit' | 'host_disk_limit' | 'host_alert_suppression_mins' | 'docker_janitor_gb' | 'global_crash' | 'prune_on_update' | 'mesh_auto_recreate' | 'reclaim_hero'>;
|
||||
|
||||
const DEFAULT_SYSTEM: SystemFields = {
|
||||
host_cpu_limit: DEFAULT_SETTINGS.host_cpu_limit,
|
||||
@@ -144,6 +144,7 @@ const DEFAULT_SYSTEM: SystemFields = {
|
||||
global_crash: DEFAULT_SETTINGS.global_crash,
|
||||
prune_on_update: DEFAULT_SETTINGS.prune_on_update,
|
||||
mesh_auto_recreate: DEFAULT_SETTINGS.mesh_auto_recreate,
|
||||
reclaim_hero: DEFAULT_SETTINGS.reclaim_hero,
|
||||
};
|
||||
|
||||
export function SystemSection({ onDirtyChange }: SystemSectionProps) {
|
||||
@@ -166,6 +167,7 @@ export function SystemSection({ onDirtyChange }: SystemSectionProps) {
|
||||
if (settings.global_crash !== baseline.global_crash) n++;
|
||||
if (settings.prune_on_update !== baseline.prune_on_update) n++;
|
||||
if (settings.mesh_auto_recreate !== baseline.mesh_auto_recreate) n++;
|
||||
if (settings.reclaim_hero !== baseline.reclaim_hero) n++;
|
||||
return n;
|
||||
}, [settings]);
|
||||
|
||||
@@ -202,6 +204,7 @@ export function SystemSection({ onDirtyChange }: SystemSectionProps) {
|
||||
global_crash: (nodeData.global_crash as '0' | '1') ?? DEFAULT_SETTINGS.global_crash,
|
||||
prune_on_update: (nodeData.prune_on_update as '0' | '1') ?? DEFAULT_SETTINGS.prune_on_update,
|
||||
mesh_auto_recreate: (nodeData.mesh_auto_recreate as '0' | '1') ?? DEFAULT_SETTINGS.mesh_auto_recreate,
|
||||
reclaim_hero: (nodeData.reclaim_hero as '0' | '1') ?? DEFAULT_SETTINGS.reclaim_hero,
|
||||
};
|
||||
setSettings(safe);
|
||||
serverSettingsRef.current = { ...safe };
|
||||
@@ -330,6 +333,15 @@ export function SystemSection({ onDirtyChange }: SystemSectionProps) {
|
||||
onChange={(next) => onSettingChange('prune_on_update', next ? '1' : '0')}
|
||||
/>
|
||||
</SettingsField>
|
||||
<SettingsField
|
||||
label="Show reclaimable-space banner"
|
||||
helper="Show the reclaimable-space banner at the top of the Resource Hub when this node has unused images, stopped containers, or dangling volumes to clear. On by default."
|
||||
>
|
||||
<TogglePill
|
||||
checked={settings.reclaim_hero === '1'}
|
||||
onChange={(next) => onSettingChange('reclaim_hero', next ? '1' : '0')}
|
||||
/>
|
||||
</SettingsField>
|
||||
</SettingsSection>
|
||||
|
||||
{/*
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface PatchableSettings {
|
||||
mesh_auto_recreate?: '0' | '1';
|
||||
scan_history_per_image_limit?: string;
|
||||
prune_on_update?: '0' | '1';
|
||||
reclaim_hero?: '0' | '1';
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: PatchableSettings = {
|
||||
@@ -30,6 +31,7 @@ export const DEFAULT_SETTINGS: PatchableSettings = {
|
||||
mesh_auto_recreate: '0',
|
||||
scan_history_per_image_limit: '50',
|
||||
prune_on_update: '1',
|
||||
reclaim_hero: '1',
|
||||
};
|
||||
|
||||
export type SectionId =
|
||||
|
||||
Reference in New Issue
Block a user