mirror of
https://github.com/jorijn/meshcore-stats.git
synced 2026-03-28 17:42:55 +01:00
Compare commits
2 Commits
renovate/d
...
feat/relat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b7fcfa211 | ||
|
|
3ed1b0e495 |
@@ -678,6 +678,7 @@ The static site uses a modern, responsive design with the following features:
|
||||
- **Repeater pages at root**: `/day.html`, `/week.html`, etc. (entry point)
|
||||
- **Companion pages**: `/companion/day.html`, `/companion/week.html`, etc.
|
||||
- **`.htaccess`**: Sets `DirectoryIndex day.html` so `/` loads repeater day view
|
||||
- **Relative links**: All internal navigation and static asset references are relative (no leading `/`) so the dashboard can be served from a reverse-proxy subpath.
|
||||
|
||||
### Page Layout
|
||||
1. **Header**: Site branding, node name, pubkey prefix, status indicator, last updated time
|
||||
|
||||
@@ -480,6 +480,7 @@ def build_chart_groups(
|
||||
role: str,
|
||||
period: str,
|
||||
chart_stats: dict | None = None,
|
||||
asset_prefix: str = "",
|
||||
) -> list[dict]:
|
||||
"""Build chart groups for template.
|
||||
|
||||
@@ -490,6 +491,7 @@ def build_chart_groups(
|
||||
role: "companion" or "repeater"
|
||||
period: Time period ("day", "week", etc.)
|
||||
chart_stats: Stats dict from chart_stats.json (optional)
|
||||
asset_prefix: Relative path prefix to reach /assets from page location
|
||||
"""
|
||||
cfg = get_config()
|
||||
groups_config = REPEATER_CHART_GROUPS if role == "repeater" else COMPANION_CHART_GROUPS
|
||||
@@ -551,8 +553,9 @@ def build_chart_groups(
|
||||
chart_data["use_svg"] = True
|
||||
else:
|
||||
# Fallback to PNG paths
|
||||
chart_data["src_light"] = f"/assets/{role}/{metric}_{period}_light.png"
|
||||
chart_data["src_dark"] = f"/assets/{role}/{metric}_{period}_dark.png"
|
||||
asset_base = f"{asset_prefix}assets/{role}/"
|
||||
chart_data["src_light"] = f"{asset_base}{metric}_{period}_light.png"
|
||||
chart_data["src_dark"] = f"{asset_base}{metric}_{period}_dark.png"
|
||||
chart_data["use_svg"] = False
|
||||
|
||||
charts.append(chart_data)
|
||||
@@ -614,7 +617,10 @@ def build_page_context(
|
||||
|
||||
# Load chart stats and build chart groups
|
||||
chart_stats = load_chart_stats(role)
|
||||
chart_groups = build_chart_groups(role, period, chart_stats)
|
||||
|
||||
# Relative path prefixes (avoid absolute paths for subpath deployments)
|
||||
css_path = "" if at_root else "../"
|
||||
asset_prefix = "" if at_root else "../"
|
||||
|
||||
# Period config
|
||||
page_title, page_subtitle = PERIOD_CONFIG.get(period, ("Observations", "Radio telemetry"))
|
||||
@@ -634,9 +640,18 @@ def build_page_context(
|
||||
),
|
||||
}
|
||||
|
||||
# CSS and link paths - depend on whether we're at root or in /companion/
|
||||
css_path = "/" if at_root else "../"
|
||||
base_path = "" if at_root else "/companion"
|
||||
chart_groups = build_chart_groups(role, period, chart_stats, asset_prefix=asset_prefix)
|
||||
|
||||
# Navigation links depend on whether we're at root or in /companion/
|
||||
base_path = ""
|
||||
if at_root:
|
||||
repeater_link = "day.html"
|
||||
companion_link = "companion/day.html"
|
||||
reports_link = "reports/"
|
||||
else:
|
||||
repeater_link = "../day.html"
|
||||
companion_link = "day.html"
|
||||
reports_link = "../reports/"
|
||||
|
||||
return {
|
||||
# Page meta
|
||||
@@ -665,9 +680,9 @@ def build_page_context(
|
||||
# Navigation
|
||||
"period": period,
|
||||
"base_path": base_path,
|
||||
"repeater_link": f"{css_path}day.html",
|
||||
"companion_link": f"{css_path}companion/day.html",
|
||||
"reports_link": f"{css_path}reports/",
|
||||
"repeater_link": repeater_link,
|
||||
"companion_link": companion_link,
|
||||
"reports_link": reports_link,
|
||||
|
||||
# Timestamps
|
||||
"last_updated": last_updated,
|
||||
|
||||
@@ -113,10 +113,10 @@
|
||||
<main class="main-content">
|
||||
<!-- Period Navigation -->
|
||||
<nav class="period-nav">
|
||||
<a href="{{ base_path }}/day.html"{% if period == 'day' %} class="active"{% endif %}>Day</a>
|
||||
<a href="{{ base_path }}/week.html"{% if period == 'week' %} class="active"{% endif %}>Week</a>
|
||||
<a href="{{ base_path }}/month.html"{% if period == 'month' %} class="active"{% endif %}>Month</a>
|
||||
<a href="{{ base_path }}/year.html"{% if period == 'year' %} class="active"{% endif %}>Year</a>
|
||||
<a href="{{ base_path }}day.html"{% if period == 'day' %} class="active"{% endif %}>Day</a>
|
||||
<a href="{{ base_path }}week.html"{% if period == 'week' %} class="active"{% endif %}>Week</a>
|
||||
<a href="{{ base_path }}month.html"{% if period == 'month' %} class="active"{% endif %}>Month</a>
|
||||
<a href="{{ base_path }}year.html"{% if period == 'year' %} class="active"{% endif %}>Year</a>
|
||||
</nav>
|
||||
|
||||
<header class="page-header">
|
||||
|
||||
@@ -229,5 +229,28 @@ class TestBuildPageContext:
|
||||
at_root=False,
|
||||
)
|
||||
|
||||
assert root_context["css_path"] == "/"
|
||||
assert root_context["css_path"] == ""
|
||||
assert non_root_context["css_path"] == "../"
|
||||
|
||||
def test_links_use_relative_paths(self, configured_env, sample_row):
|
||||
"""Navigation and asset links are relative for subpath deployments."""
|
||||
root_context = build_page_context(
|
||||
role="repeater",
|
||||
period="day",
|
||||
row=sample_row,
|
||||
at_root=True,
|
||||
)
|
||||
non_root_context = build_page_context(
|
||||
role="companion",
|
||||
period="day",
|
||||
row=sample_row,
|
||||
at_root=False,
|
||||
)
|
||||
|
||||
assert root_context["repeater_link"] == "day.html"
|
||||
assert root_context["companion_link"] == "companion/day.html"
|
||||
assert root_context["reports_link"] == "reports/"
|
||||
|
||||
assert non_root_context["repeater_link"] == "../day.html"
|
||||
assert non_root_context["companion_link"] == "day.html"
|
||||
assert non_root_context["reports_link"] == "../reports/"
|
||||
|
||||
@@ -266,7 +266,8 @@ class TestHtmlOutput:
|
||||
|
||||
content = (out_dir / "day.html").read_text()
|
||||
|
||||
assert "styles.css" in content
|
||||
assert 'href="styles.css"' in content
|
||||
assert 'href="/styles.css"' not in content
|
||||
|
||||
def test_companion_pages_relative_css(self, html_env, metrics_rows):
|
||||
"""Companion pages use relative path to CSS."""
|
||||
@@ -277,4 +278,5 @@ class TestHtmlOutput:
|
||||
content = (out_dir / "companion" / "day.html").read_text()
|
||||
|
||||
# Should reference parent directory CSS
|
||||
assert "../styles.css" in content or "styles.css" in content
|
||||
assert 'href="../styles.css"' in content
|
||||
assert 'href="/styles.css"' not in content
|
||||
|
||||
@@ -6,6 +6,7 @@ from meshmon.html import (
|
||||
PERIOD_CONFIG,
|
||||
REPEATER_CHART_GROUPS,
|
||||
_build_traffic_table_rows,
|
||||
build_chart_groups,
|
||||
build_companion_metrics,
|
||||
build_node_details,
|
||||
build_radio_config,
|
||||
@@ -457,3 +458,32 @@ class TestChartGroupConstants:
|
||||
for _period, (title, subtitle) in PERIOD_CONFIG.items():
|
||||
assert isinstance(title, str)
|
||||
assert isinstance(subtitle, str)
|
||||
|
||||
|
||||
class TestBuildChartGroups:
|
||||
"""Tests for build_chart_groups."""
|
||||
|
||||
def test_png_paths_use_relative_prefix(self, configured_env):
|
||||
"""PNG fallback paths respect provided asset prefix."""
|
||||
out_dir = configured_env["out_dir"]
|
||||
asset_dir = out_dir / "assets" / "repeater"
|
||||
asset_dir.mkdir(parents=True, exist_ok=True)
|
||||
(asset_dir / "bat_day_light.png").write_bytes(b"fake")
|
||||
|
||||
groups = build_chart_groups(
|
||||
role="repeater",
|
||||
period="day",
|
||||
chart_stats={},
|
||||
asset_prefix="../",
|
||||
)
|
||||
|
||||
chart = next(
|
||||
chart
|
||||
for group in groups
|
||||
for chart in group["charts"]
|
||||
if chart["metric"] == "bat"
|
||||
)
|
||||
|
||||
assert chart["use_svg"] is False
|
||||
assert chart["src_light"] == "../assets/repeater/bat_day_light.png"
|
||||
assert chart["src_dark"] == "../assets/repeater/bat_day_dark.png"
|
||||
|
||||
Reference in New Issue
Block a user