|
2 | 2 | Shared utilities for devto-mirror scripts |
3 | 3 | """ |
4 | 4 |
|
| 5 | +import json |
| 6 | +import os |
5 | 7 | import pathlib |
6 | 8 | from datetime import datetime, timezone |
7 | 9 | from email.utils import parsedate_to_datetime |
8 | 10 |
|
9 | 11 | from jinja2 import Environment, FileSystemLoader, select_autoescape |
| 12 | +from markupsafe import Markup |
10 | 13 |
|
11 | 14 | # Use a Jinja environment with autoescape enabled for HTML/XML templates |
12 | 15 | # Also add FileSystemLoader to load templates from files |
|
16 | 19 | autoescape=select_autoescape(["html", "xml"]), |
17 | 20 | ) |
18 | 21 |
|
| 22 | +FIREBASE_SDK_VERSION = "12.15.0" |
| 23 | + |
| 24 | +_FIREBASE_ANALYTICS_TMPL = """<!-- Firebase Analytics --> |
| 25 | +<script type="module"> |
| 26 | + import {{ initializeApp }} from "https://www.gstatic.com/firebasejs/{version}/firebase-app.js"; |
| 27 | + import {{ getAnalytics }} from "https://www.gstatic.com/firebasejs/{version}/firebase-analytics.js"; |
| 28 | + const app = initializeApp({config}); |
| 29 | + getAnalytics(app); |
| 30 | +</script>""" |
| 31 | + |
| 32 | + |
| 33 | +def firebase_analytics_snippet(): |
| 34 | + """Build the Firebase Analytics script tag from FIREBASE_WEB_CONFIG. |
| 35 | +
|
| 36 | + Returns safe markup for the snippet, or empty markup when the env var is |
| 37 | + unset/invalid or carries no measurementId. Forks without their own |
| 38 | + configured project therefore ship no analytics. |
| 39 | + """ |
| 40 | + raw = os.getenv("FIREBASE_WEB_CONFIG", "").strip() |
| 41 | + if not raw: |
| 42 | + return Markup("") |
| 43 | + try: |
| 44 | + config = json.loads(raw) |
| 45 | + except (ValueError, TypeError): |
| 46 | + return Markup("") |
| 47 | + if not isinstance(config, dict) or not config.get("measurementId"): |
| 48 | + return Markup("") |
| 49 | + # Owner-controlled repo variable, not user input; escaping "<" neutralizes |
| 50 | + # any </script> breakout so the embedded JSON is inert regardless of source. |
| 51 | + config_json = json.dumps(config).replace("<", "\\u003c") |
| 52 | + return Markup(_FIREBASE_ANALYTICS_TMPL.format(version=FIREBASE_SDK_VERSION, config=config_json)) # nosec B704 |
| 53 | + |
| 54 | + |
| 55 | +# Expose to all templates rendered through this env; evaluated at render time. |
| 56 | +env.globals["firebase_analytics"] = firebase_analytics_snippet |
| 57 | + |
19 | 58 |
|
20 | 59 | # Load post template from file if available, otherwise use inline template |
21 | 60 | def get_post_template(): |
@@ -88,6 +127,7 @@ def get_post_template(): |
88 | 127 | </script> |
89 | 128 | {% endfor %} |
90 | 129 | {% endif %} |
| 130 | +{{ firebase_analytics() }} |
91 | 131 | </head><body> |
92 | 132 | <main> |
93 | 133 | <h1><a href="{{ canonical }}">{{ title }}</a></h1> |
@@ -191,6 +231,7 @@ def get_post_template(): |
191 | 231 | <!-- Additional Social Meta --> |
192 | 232 | <meta name="image" content="{{ social_image }}"> |
193 | 233 | <meta name="author" content="{{ username }}"> |
| 234 | +{{ firebase_analytics() }} |
194 | 235 | </head><body> |
195 | 236 | <main> |
196 | 237 | <h1>{{ username }}—Dev.to Mirror</h1> |
|
0 commit comments