From 2e53dff853cea601b7de39d88e096051412ece4d Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Fri, 29 May 2026 00:50:49 +0300 Subject: [PATCH] feat(settings): validate BaseUrl + cron on save, add BaseUrl hint - Reject a non-absolute / non-http(s) BaseUrl and an implausible (not 5- or 6-field) cron expression before the section is written to disk, mirroring the existing storage-path validation (snackbar + early return). - Add a hint to the BaseUrl field. Cron check is a lightweight UI guard; the worker still does the authoritative Cronos parse at startup. --- src/Marathon.UI/Pages/Settings.razor | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Marathon.UI/Pages/Settings.razor b/src/Marathon.UI/Pages/Settings.razor index 61f7da3..0dba50d 100644 --- a/src/Marathon.UI/Pages/Settings.razor +++ b/src/Marathon.UI/Pages/Settings.razor @@ -49,7 +49,7 @@ - + @@ -242,6 +242,20 @@ } } + if (payload is ScrapingSettingsForm scraping + && !(Uri.TryCreate(scraping.BaseUrl, UriKind.Absolute, out var baseUri) + && (baseUri.Scheme == Uri.UriSchemeHttp || baseUri.Scheme == Uri.UriSchemeHttps))) + { + Snackbar.Add(L["Settings.Scraping.BaseUrl.Invalid"], Severity.Error); + return; + } + + if (payload is WorkerOptions workers && !IsPlausibleCron(workers.UpcomingScheduleCron)) + { + Snackbar.Add(L["Settings.Workers.Cron.Invalid"], Severity.Error); + return; + } + var confirmed = await ConfirmAsync(); if (!confirmed) { @@ -260,6 +274,15 @@ } } + // Lightweight 5- or 6-field cron sanity check — avoids a Cronos dependency in the + // UI layer; the worker still does the authoritative parse at startup. + private static bool IsPlausibleCron(string? expression) + { + if (string.IsNullOrWhiteSpace(expression)) return false; + var fields = expression.Split(' ', StringSplitOptions.RemoveEmptyEntries); + return fields.Length is 5 or 6; + } + private async Task ResetSectionAsync(string section) { var confirmed = await ConfirmAsync();