Compare commits

...

4 Commits

Author SHA1 Message Date
dependabot[bot] 26d24eff20
Merge 118e03301f into 79098d3546 2025-10-28 18:50:37 +01:00
Adam Shiervani 79098d3546
feat: Enhance DHCP client timeout and retry logic (#908) 2025-10-28 18:50:29 +01:00
Adam Shiervani 50fc88aae1
bug: fix null pointer in wakeDisplay (#907) 2025-10-28 18:48:30 +01:00
dependabot[bot] 118e03301f
build(deps): bump validator
Bumps the npm_and_yarn group with 1 update in the /ui directory: [validator](https://github.com/validatorjs/validator.js).


Updates `validator` from 13.15.15 to 13.15.20
- [Release notes](https://github.com/validatorjs/validator.js/releases)
- [Changelog](https://github.com/validatorjs/validator.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/validatorjs/validator.js/compare/13.15.15...13.15.20)

---
updated-dependencies:
- dependency-name: validator
  dependency-version: 13.15.20
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 16:33:22 +00:00
5 changed files with 92 additions and 20 deletions

View File

@ -305,11 +305,11 @@ func wakeDisplay(force bool, reason string) {
displayLogger.Warn().Err(err).Msg("failed to wake display") displayLogger.Warn().Err(err).Msg("failed to wake display")
} }
if config.DisplayDimAfterSec != 0 { if config.DisplayDimAfterSec != 0 && dimTicker != nil {
dimTicker.Reset(time.Duration(config.DisplayDimAfterSec) * time.Second) dimTicker.Reset(time.Duration(config.DisplayDimAfterSec) * time.Second)
} }
if config.DisplayOffAfterSec != 0 { if config.DisplayOffAfterSec != 0 && offTicker != nil {
offTicker.Reset(time.Duration(config.DisplayOffAfterSec) * time.Second) offTicker.Reset(time.Duration(config.DisplayOffAfterSec) * time.Second)
} }
backlightState = 0 backlightState = 0

View File

@ -111,6 +111,7 @@ type Client struct {
var ( var (
defaultTimerDuration = 1 * time.Second defaultTimerDuration = 1 * time.Second
defaultLinkUpTimeout = 30 * time.Second defaultLinkUpTimeout = 30 * time.Second
defaultDHCPTimeout = 5 * time.Second // DHCP request timeout (not link up timeout)
maxRenewalAttemptDuration = 2 * time.Hour maxRenewalAttemptDuration = 2 * time.Hour
) )
@ -125,11 +126,11 @@ func NewClient(ctx context.Context, ifaces []string, c *Config, l *zerolog.Logge
} }
if cfg.Timeout == 0 { if cfg.Timeout == 0 {
cfg.Timeout = defaultLinkUpTimeout cfg.Timeout = defaultDHCPTimeout
} }
if cfg.Retries == 0 { if cfg.Retries == 0 {
cfg.Retries = 3 cfg.Retries = 4
} }
return &Client{ return &Client{
@ -153,9 +154,15 @@ func NewClient(ctx context.Context, ifaces []string, c *Config, l *zerolog.Logge
}, nil }, nil
} }
func resetTimer(t *time.Timer, l *zerolog.Logger) { func resetTimer(t *time.Timer, attempt int, l *zerolog.Logger) {
l.Debug().Dur("delay", defaultTimerDuration).Msg("will retry later") // Exponential backoff: 1s, 2s, 4s, 8s, max 8s
t.Reset(defaultTimerDuration) backoffAttempt := attempt
if backoffAttempt > 3 {
backoffAttempt = 3
}
delay := time.Duration(1<<backoffAttempt) * time.Second
l.Debug().Dur("delay", delay).Int("attempt", attempt).Msg("will retry later")
t.Reset(delay)
} }
func getRenewalTime(lease *Lease) time.Duration { func getRenewalTime(lease *Lease) time.Duration {
@ -168,12 +175,14 @@ func getRenewalTime(lease *Lease) time.Duration {
func (c *Client) requestLoop(t *time.Timer, family int, ifname string) { func (c *Client) requestLoop(t *time.Timer, family int, ifname string) {
l := c.l.With().Str("interface", ifname).Int("family", family).Logger() l := c.l.With().Str("interface", ifname).Int("family", family).Logger()
attempt := 0
for range t.C { for range t.C {
l.Info().Msg("requesting lease") l.Info().Int("attempt", attempt).Msg("requesting lease")
if _, err := c.ensureInterfaceUp(ifname); err != nil { if _, err := c.ensureInterfaceUp(ifname); err != nil {
l.Error().Err(err).Msg("failed to ensure interface up") l.Error().Err(err).Int("attempt", attempt).Msg("failed to ensure interface up")
resetTimer(t, c.l) resetTimer(t, attempt, c.l)
attempt++
continue continue
} }
@ -188,11 +197,14 @@ func (c *Client) requestLoop(t *time.Timer, family int, ifname string) {
lease, err = c.requestLease6(ifname) lease, err = c.requestLease6(ifname)
} }
if err != nil { if err != nil {
l.Error().Err(err).Msg("failed to request lease") l.Error().Err(err).Int("attempt", attempt).Msg("failed to request lease")
resetTimer(t, c.l) resetTimer(t, attempt, c.l)
attempt++
continue continue
} }
// Successfully obtained lease, reset attempt counter
attempt = 0
c.handleLeaseChange(lease) c.handleLeaseChange(lease)
nextRenewal := getRenewalTime(lease) nextRenewal := getRenewalTime(lease)

68
ui/package-lock.json generated
View File

@ -38,7 +38,7 @@
"recharts": "^3.3.0", "recharts": "^3.3.0",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"usehooks-ts": "^3.1.1", "usehooks-ts": "^3.1.1",
"validator": "^13.15.15", "validator": "^13.15.20",
"zustand": "^4.5.2" "zustand": "^4.5.2"
}, },
"devDependencies": { "devDependencies": {
@ -2198,6 +2198,66 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.5.0",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.1.0",
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.5.0",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.1.0",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "1.0.7",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.5.0",
"@emnapi/runtime": "^1.5.0",
"@tybys/wasm-util": "^0.10.1"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
"version": "2.8.1",
"dev": true,
"inBundle": true,
"license": "0BSD",
"optional": true
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.1.16", "version": "4.1.16",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.16.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.16.tgz",
@ -7570,9 +7630,9 @@
} }
}, },
"node_modules/validator": { "node_modules/validator": {
"version": "13.15.15", "version": "13.15.20",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz",
"integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 0.10" "node": ">= 0.10"

View File

@ -57,7 +57,7 @@
"recharts": "^3.3.0", "recharts": "^3.3.0",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"usehooks-ts": "^3.1.1", "usehooks-ts": "^3.1.1",
"validator": "^13.15.15", "validator": "^13.15.20",
"zustand": "^4.5.2" "zustand": "^4.5.2"
}, },
"devDependencies": { "devDependencies": {

View File

@ -46,10 +46,10 @@ export default function SettingsHardwareRoute() {
} }
setBacklightSettings(settings); setBacklightSettings(settings);
handleBacklightSettingsSave(); handleBacklightSettingsSave(settings);
}; };
const handleBacklightSettingsSave = () => { const handleBacklightSettingsSave = (backlightSettings: BacklightSettings) => {
send("setBacklightSettings", { params: backlightSettings }, (resp: JsonRpcResponse) => { send("setBacklightSettings", { params: backlightSettings }, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
notifications.error( notifications.error(