Repository navigation
feat(settings): configurable default check interval (fixes #239) - #249
Merged
Merged
Conversation
…239) The Add Product form opened on 12 hours and both product-add paths fell back to the same hardcoded 43200, so anybody wanting a different cadence changed the field on every single product -- real friction when bulk-adding a list right after setup. Two places now answer it, because in a self-hosted multi-user instance two different people have a claim on it: - system_settings.default_refresh_interval, set under Admin -> System, is the instance default and the only one that applies to an account that has never opened Settings. - users.default_refresh_interval, set under Settings -> Tracking, is one person's own preference and wins over the instance default for the products they add. NULL means follow the instance default, which is what every existing account gets. Resolution is explicit value > user preference > instance default > the old 12 hours, ranked in utils/system/check-interval.ts and read once per add by DefaultCheckIntervalService. Nothing reads or writes an existing product's refresh_interval, and an instance that has configured neither behaves exactly as it did before. A stored default is held to a one-hour floor and a one-week ceiling: it is the one interval here that is applied to every product somebody adds, and PriceCheckTask's per-retailer 30s spacing paces requests rather than protecting a retailer from an aggressive setting. An explicit per-product interval is still honoured verbatim. The resolved default is read straight from the repositories rather than through SettingsCache, so a changed setting takes effect on the next product added instead of after the cache's 30-minute TTL. Migration 022 adds the nullable column and seeds the instance default.
This was referenced Oct 11, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #239. Thanks @GeilerGelber — bulk-adding right after setup is exactly the case this was annoying for.
Both levels, which answers @stevene1919's open question on the issue
He asked whether this should be admin-only. It is genuinely both, because each level answers a different need:
system_settings.default_refresh_interval, under Admin → System → Product Tracking. The only value that applies to an account which has never opened Settings, including every account on a fresh install. This is where someone responsible for how hard the install leans on retailers sets the house cadence.users.default_refresh_interval, under Settings → Tracking (new section). Literally what the issue asks for; a self-hosted instance with several people tracking different kinds of thing has no business forcing one cadence on everyone.The change stayed contained because both halves reuse established patterns — the admin field is one more
CollapsibleCardin the existingSystemSectionform, and the new user section copiesRegionalSectionexactly, following the save model #237 and #238 established.Deliberately not built: the instance value is a default, not a floor on user preferences. A floor is a different feature and the issue does not ask for one.
Resolution order
Ranked in a pure module (
utils/system/check-interval.ts) and applied inProductAddService.addProduct, so the discovery and confirmation paths resolve identically. Every step treatsnull,'','auto',0,-1andNaNas "not set" and falls through — so an instance with nothing configured lands on exactly the 12 hours the form previously hardcoded.Existing products are untouched. Migration 022 contains no
UPDATE products, and nothing in the new path reads or writes an existing listing's interval. A default only ever decides the interval of a listing being created.The form prefills from a server-resolved number (
GET /api/settings/product-defaults), so it never knows the ranking and a non-admin can see the effective default without being handed Admin → System.A minimum interval, on stored defaults only
One hour floor, one week ceiling, on both write paths and again at resolution.
A default is the one interval here that multiplies: set it to five minutes and a bulk-add of thirty listings is thirty retailers' worth of traffic every five minutes, forever. And #226's
DOMAIN_GAP_MS = 30_000serialises per retailer, so an interval shorter than a sweep can complete in is not honoured anyway — the sweep just never catches up. One hour is the shortest the UI has ever offered, so nothing a person could previously pick is now refused.An explicit per-product interval is honoured verbatim, floor included — clamping it would change the behaviour of an API call that works today. A floor on
PUT /products/:idwould be a separate policy decision, flagged rather than quietly added.On the SettingsCache TTL
Sidestepped rather than worked around: this reads
system_settingsdirectly rather than throughsettingsCache. The cache exists so the scraper can consult selector lists on every extraction; this value is read once when the form mounts and once per product added, so the cache saves nothing measurable — while 30 minutes of staleness would mean an admin changes the default, adds a product to check, sees the old number and concludes the feature is broken.Migration 022, tested against a real Postgres
Not just
tsc. A throwaway Homebrew cluster, driven through the project's actual migrate CLI:up— all 22 applied21600, themigrationsrow removed,upre-run: re-applied and the administrator's value left intactdown— both halves gone;upagain cleanPlus an end-to-end script against that database exercising the real repository SQL, confirming the ranking, the clamp (requested
60stored as3600), and that a save from another settings section cannot clear the preference.The 021 gap is deliberate and harmless — #245 claims that number and is still in review. The runner globs the directory and tracks applied names in a
migrationstable, and 021 is a seed-only insert, so it can land later in any order.Verification
tscclean both sides. 836 backend tests (814 on main, +22) and 39 frontend (33, +6). Playwright 20 passed on private port 4402, bundle ownership proved by grepping the served chunks forDefault Check Interval.The one
as anyin the diff is inrouteTree.gen.ts, which TanStack Router generates for the new/settings/trackingroute.Noted, not fixed
UserProfileService.updateProfile's generic change log prints the requested value, so a clamped save logs-> 60while3600is stored. Pre-existing shared loop overObject.keys(data); restructuring it for one field would be churn beyond this issue.