mirror of
https://github.com/jorijn/meshcore-stats.git
synced 2026-03-28 17:42:55 +01:00
feat: add configurable custom HTML head injection (#118)
Allow deployers to inject custom HTML into the <head> of every page via the CUSTOM_HEAD_HTML config option, useful for analytics scripts (Plausible, Matomo, etc.) without modifying source. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
de2290639f
commit
edde12f17c
@@ -148,6 +148,14 @@ RADIO_CODING_RATE=CR8
|
|||||||
# TELEMETRY_RETRY_ATTEMPTS=2
|
# TELEMETRY_RETRY_ATTEMPTS=2
|
||||||
# TELEMETRY_RETRY_BACKOFF_S=4
|
# TELEMETRY_RETRY_BACKOFF_S=4
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Custom HTML (Analytics, etc.)
|
||||||
|
# =============================================================================
|
||||||
|
# Inject custom HTML into the <head> of every page.
|
||||||
|
# Useful for analytics scripts (Plausible, Matomo, etc.) without modifying source.
|
||||||
|
# Example for Plausible:
|
||||||
|
# CUSTOM_HEAD_HTML=<script defer data-domain="stats.example.com" src="https://plausible.io/js/script.js"></script>
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Paths (Native installation only)
|
# Paths (Native installation only)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
@@ -273,6 +273,9 @@ class Config:
|
|||||||
|
|
||||||
self.html_path = get_str("HTML_PATH", "") or ""
|
self.html_path = get_str("HTML_PATH", "") or ""
|
||||||
|
|
||||||
|
# Custom HTML injected into <head> (e.g. analytics scripts)
|
||||||
|
self.custom_head_html = get_str("CUSTOM_HEAD_HTML", "") or ""
|
||||||
|
|
||||||
# Global config instance
|
# Global config instance
|
||||||
_config: Config | None = None
|
_config: Config | None = None
|
||||||
|
|
||||||
|
|||||||
@@ -727,6 +727,9 @@ def build_page_context(
|
|||||||
"page_title": page_title,
|
"page_title": page_title,
|
||||||
"page_subtitle": page_subtitle,
|
"page_subtitle": page_subtitle,
|
||||||
"chart_groups": chart_groups,
|
"chart_groups": chart_groups,
|
||||||
|
|
||||||
|
# Custom HTML
|
||||||
|
"custom_head_html": cfg.custom_head_html,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1304,6 +1307,7 @@ def render_report_page(
|
|||||||
"monthly_links": monthly_links,
|
"monthly_links": monthly_links,
|
||||||
"prev_report": prev_report,
|
"prev_report": prev_report,
|
||||||
"next_report": next_report,
|
"next_report": next_report,
|
||||||
|
"custom_head_html": cfg.custom_head_html,
|
||||||
}
|
}
|
||||||
|
|
||||||
template = env.get_template("report.html")
|
template = env.get_template("report.html")
|
||||||
@@ -1341,6 +1345,7 @@ def render_reports_index(report_sections: list[dict]) -> str:
|
|||||||
"css_path": "../",
|
"css_path": "../",
|
||||||
"report_sections": report_sections,
|
"report_sections": report_sections,
|
||||||
"month_abbrs": month_abbrs,
|
"month_abbrs": month_abbrs,
|
||||||
|
"custom_head_html": cfg.custom_head_html,
|
||||||
}
|
}
|
||||||
|
|
||||||
template = env.get_template("report_index.html")
|
template = env.get_template("report_index.html")
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<meta name="twitter:description" content="{{ meta_description }}">
|
<meta name="twitter:description" content="{{ meta_description }}">
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ css_path }}styles.css">
|
<link rel="stylesheet" href="{{ css_path }}styles.css">
|
||||||
|
{% if custom_head_html %}{{ custom_head_html | safe }}{% endif %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block body %}{% endblock %}
|
{% block body %}{% endblock %}
|
||||||
|
|||||||
@@ -147,3 +147,28 @@ class TestTemplateRendering:
|
|||||||
assert "<html" in html
|
assert "<html" in html
|
||||||
assert "<head>" in html
|
assert "<head>" in html
|
||||||
assert "<body>" in html
|
assert "<body>" in html
|
||||||
|
|
||||||
|
def test_custom_head_html_rendered_when_set(self):
|
||||||
|
"""Custom head HTML appears in rendered output when provided."""
|
||||||
|
env = get_jinja_env()
|
||||||
|
template = env.get_template("base.html")
|
||||||
|
|
||||||
|
snippet = '<script defer data-domain="example.com" src="https://plausible.io/js/script.js"></script>'
|
||||||
|
html = template.render(
|
||||||
|
title="Test",
|
||||||
|
custom_head_html=snippet,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert snippet in html
|
||||||
|
assert html.index(snippet) < html.index("</head>")
|
||||||
|
|
||||||
|
def test_custom_head_html_absent_when_empty(self):
|
||||||
|
"""No extra content in head when custom_head_html is empty."""
|
||||||
|
env = get_jinja_env()
|
||||||
|
template = env.get_template("base.html")
|
||||||
|
|
||||||
|
html_with = template.render(title="Test", custom_head_html="")
|
||||||
|
html_without = template.render(title="Test")
|
||||||
|
|
||||||
|
# Both should produce identical output (no extra content)
|
||||||
|
assert html_with == html_without
|
||||||
|
|||||||
Reference in New Issue
Block a user