From ae003ded58ce6636b7061a94988a1e6caa1b4f24 Mon Sep 17 00:00:00 2001 From: Louis King Date: Sun, 19 Jul 2026 21:09:19 +0100 Subject: [PATCH] chore(routes): default window_hours=48 and max_hop_span=8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the new-route defaults so the create modal and API/CLI paths pre-fill a 48h evaluation window and a max-hop-span of 8 instead of the previous 24h / unlimited (∞). - schemas/routes.py: RouteCreate + RoutePreviewRequest defaults - models/route.py: SQLAlchemy INSERT-time defaults - collector/cli.py: YAML import fallbacks - web/static/js/spa/pages/routes.js: new-route modal pre-fills 48/8 No migration: window_hours/max_hop_span use Python-side default= (no server_default), so existing rows are untouched. Clearing the max-hop-span field in the UI still sends null (no cap). --- src/meshcore_hub/collector/cli.py | 8 ++++---- src/meshcore_hub/common/models/route.py | 3 ++- src/meshcore_hub/common/schemas/routes.py | 8 ++++---- src/meshcore_hub/web/static/js/spa/pages/routes.js | 6 +++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/meshcore_hub/collector/cli.py b/src/meshcore_hub/collector/cli.py index 3f81a91..8e78cc0 100644 --- a/src/meshcore_hub/collector/cli.py +++ b/src/meshcore_hub/collector/cli.py @@ -804,12 +804,12 @@ def _import_routes( route.description = value.get("description") route.visibility = visibility route.match_width = match_width - route.window_hours = value.get("window_hours", 24) + route.window_hours = value.get("window_hours", 48) route.packet_count_threshold = value.get( "packet_count_threshold", 3 ) route.clear_threshold = value.get("clear_threshold") - route.max_hop_span = value.get("max_hop_span") + route.max_hop_span = value.get("max_hop_span", 8) route.enabled = value.get("enabled", True) route.reversible = value.get("reversible", True) # Replace path nodes wholesale @@ -826,10 +826,10 @@ def _import_routes( description=value.get("description"), visibility=visibility, match_width=match_width, - window_hours=value.get("window_hours", 24), + window_hours=value.get("window_hours", 48), packet_count_threshold=value.get("packet_count_threshold", 3), clear_threshold=value.get("clear_threshold"), - max_hop_span=value.get("max_hop_span"), + max_hop_span=value.get("max_hop_span", 8), enabled=value.get("enabled", True), reversible=value.get("reversible", True), ) diff --git a/src/meshcore_hub/common/models/route.py b/src/meshcore_hub/common/models/route.py index 6eeb884..cf5c0a0 100644 --- a/src/meshcore_hub/common/models/route.py +++ b/src/meshcore_hub/common/models/route.py @@ -69,7 +69,7 @@ class Route(Base, UUIDMixin, TimestampMixin): ) window_hours: Mapped[int] = mapped_column( Integer, - default=24, + default=48, nullable=False, ) packet_count_threshold: Mapped[int] = mapped_column( @@ -83,6 +83,7 @@ class Route(Base, UUIDMixin, TimestampMixin): ) max_hop_span: Mapped[Optional[int]] = mapped_column( Integer, + default=8, nullable=True, ) enabled: Mapped[bool] = mapped_column( diff --git a/src/meshcore_hub/common/schemas/routes.py b/src/meshcore_hub/common/schemas/routes.py index 9a7a8f4..d2df380 100644 --- a/src/meshcore_hub/common/schemas/routes.py +++ b/src/meshcore_hub/common/schemas/routes.py @@ -64,7 +64,7 @@ class RouteCreate(BaseModel): default=1, ge=1, le=3, description="Hash prefix width (1-3 bytes)" ) window_hours: int = Field( - default=24, ge=1, le=720, description="Evaluation window in hours" + default=48, ge=1, le=720, description="Evaluation window in hours" ) packet_count_threshold: int = Field( default=3, ge=1, le=10000, description="Minimum distinct packets for healthy" @@ -73,7 +73,7 @@ class RouteCreate(BaseModel): default=None, description="Comfort bar (null = 2x threshold)" ) max_hop_span: Optional[int] = Field( - default=None, description="Max hop distance between first and last node" + default=8, description="Max hop distance between first and last node" ) enabled: bool = Field(default=True, description="Whether this route is evaluated") reversible: bool = Field( @@ -232,10 +232,10 @@ class RoutePreviewRequest(BaseModel): ..., description="Ordered path node public keys" ) match_width: int = Field(default=1, ge=1, le=3) - window_hours: int = Field(default=24, ge=1, le=720) + window_hours: int = Field(default=48, ge=1, le=720) packet_count_threshold: int = Field(default=3, ge=1, le=10000) clear_threshold: Optional[int] = None - max_hop_span: Optional[int] = None + max_hop_span: Optional[int] = Field(default=8) observer_public_keys: Optional[list[str]] = None reversible: bool = Field(default=True) diff --git a/src/meshcore_hub/web/static/js/spa/pages/routes.js b/src/meshcore_hub/web/static/js/spa/pages/routes.js index 2a386f8..f86a128 100644 --- a/src/meshcore_hub/web/static/js/spa/pages/routes.js +++ b/src/meshcore_hub/web/static/js/spa/pages/routes.js @@ -400,7 +400,7 @@ function renderRouteModal({ modalState, onSave, onCancel, saving }) {
+ .value=${route.window_hours || 48} min="1" max="720" />
@@ -633,7 +633,7 @@ export async function render(container, params, router) { } function handleAdd() { - modalState = _newModalState('add', { visibility: 'community', enabled: true, match_width: 1 }); + modalState = _newModalState('add', { visibility: 'community', enabled: true, match_width: 1, window_hours: 48, max_hop_span: 8 }); renderPage(routes); } @@ -803,7 +803,7 @@ export async function render(container, params, router) { description: descEl.value.trim() || null, visibility: visEl.value, match_width: parseInt(widthEl.value, 10) || 1, - window_hours: parseInt(windowEl.value, 10) || 24, + window_hours: parseInt(windowEl.value, 10) || 48, packet_count_threshold: parseInt(thresholdEl.value, 10) || 3, max_hop_span: spanEl.value ? parseInt(spanEl.value, 10) : null, enabled: enabledEl.checked,