refactor(settings): extract shared parseGoDuration to drop the duplicated parser (F10)

The auto-heal floor fix copied durationPattern/unitSeconds/parseGoDurationSeconds
verbatim from AutoUpdatePanel into AutoHealPanel. Move them to a shared
SettingsView/parseGoDuration module imported by both panels — single source of
truth (mirrors the STALE_TIME dedup), so the two clients' floors can't drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
claude code agent
2026-06-29 20:49:35 +03:00
parent be3bfd0513
commit f658d67ccb
3 changed files with 34 additions and 66 deletions
@@ -1,48 +1,16 @@
import { SchemaOf, boolean, mixed, object, string } from 'yup';
import { AutoHealScope } from '../../types';
import { durationPattern, parseGoDurationSeconds } from '../parseGoDuration';
import { Values } from './types';
// Matches Go's time.ParseDuration units (e.g. "30s", "1m30s", "2h").
const durationPattern = /^(\d+(\.\d+)?(ns|us|µs|ms|s|m|h))+$/;
// Lower bound for the check interval, kept in sync with the backend
// (minAutoHealCheckInterval). A near-zero interval would hammer Docker with
// list/inspect calls for no benefit, and the backend rejects it (400), so the
// front-end must reject it too rather than letting a sub-second value through.
const minCheckIntervalSeconds = 1;
// Seconds per Go duration unit, used to evaluate the configured interval against
// the floor. Mirrors the units accepted by durationPattern.
const unitSeconds: Record<string, number> = {
ns: 1e-9,
us: 1e-6,
µs: 1e-6,
ms: 1e-3,
s: 1,
m: 60,
h: 3600,
};
// parseGoDurationSeconds converts a Go-style duration (e.g. "1h30m") to seconds,
// or returns null when the string is not a well-formed duration.
function parseGoDurationSeconds(value: string): number | null {
if (!durationPattern.test(value)) {
return null;
}
let total = 0;
const componentPattern = /(\d+(?:\.\d+)?)(ns|us|µs|ms|s|m|h)/g;
let match = componentPattern.exec(value);
while (match !== null) {
total += parseFloat(match[1]) * unitSeconds[match[2]];
match = componentPattern.exec(value);
}
return total;
}
export function validation(): SchemaOf<Values> {
return object({
enabled: boolean().default(false),
@@ -1,12 +1,10 @@
import { SchemaOf, boolean, mixed, object, string } from 'yup';
import { AutoUpdateScope } from '../../types';
import { durationPattern, parseGoDurationSeconds } from '../parseGoDuration';
import { Values } from './types';
// Matches Go's time.ParseDuration units (e.g. "6h", "30m", "1h30m").
const durationPattern = /^(\d+(\.\d+)?(ns|us|µs|ms|s|m|h))+$/;
// Lower bound for the poll interval, kept in sync with the backend
// (minAutoUpdatePollInterval). Polling more often than this only adds registry
// load: the image-status cache (~5m) bounds detection latency, so a sub-minute
@@ -18,36 +16,6 @@ const minPollIntervalSeconds = 60;
// container can pass its healthcheck, defeating the gate.
const minRollbackTimeoutSeconds = 10;
// Seconds per Go duration unit, used to evaluate the configured interval against
// the floor. Mirrors the units accepted by durationPattern.
const unitSeconds: Record<string, number> = {
ns: 1e-9,
us: 1e-6,
µs: 1e-6,
ms: 1e-3,
s: 1,
m: 60,
h: 3600,
};
// parseGoDurationSeconds converts a Go-style duration (e.g. "1h30m") to seconds,
// or returns null when the string is not a well-formed duration.
function parseGoDurationSeconds(value: string): number | null {
if (!durationPattern.test(value)) {
return null;
}
let total = 0;
const componentPattern = /(\d+(?:\.\d+)?)(ns|us|µs|ms|s|m|h)/g;
let match = componentPattern.exec(value);
while (match !== null) {
total += parseFloat(match[1]) * unitSeconds[match[2]];
match = componentPattern.exec(value);
}
return total;
}
export function validation(): SchemaOf<Values> {
return object({
enabled: boolean().default(false),
@@ -0,0 +1,32 @@
// Matches Go's time.ParseDuration units (e.g. "6h", "30m", "1h30m").
export const durationPattern = /^(\d+(\.\d+)?(ns|us|µs|ms|s|m|h))+$/;
// Seconds per Go duration unit, used to evaluate a configured interval against a
// floor. Mirrors the units accepted by durationPattern.
const unitSeconds: Record<string, number> = {
ns: 1e-9,
us: 1e-6,
µs: 1e-6,
ms: 1e-3,
s: 1,
m: 60,
h: 3600,
};
// parseGoDurationSeconds converts a Go-style duration (e.g. "1h30m") to seconds,
// or returns null when the string is not a well-formed duration.
export function parseGoDurationSeconds(value: string): number | null {
if (!durationPattern.test(value)) {
return null;
}
let total = 0;
const componentPattern = /(\d+(?:\.\d+)?)(ns|us|µs|ms|s|m|h)/g;
let match = componentPattern.exec(value);
while (match !== null) {
total += parseFloat(match[1]) * unitSeconds[match[2]];
match = componentPattern.exec(value);
}
return total;
}