orchestrated-discussions/.venv/lib/python3.12/site-packages/cmdforge/web/seo.py

79 lines
2.4 KiB
Python

"""SEO helpers for sitemap and robots.txt."""
from __future__ import annotations
from datetime import datetime, timedelta
from typing import List
from flask import Response, current_app, url_for
from cmdforge.registry.db import connect_db, query_all
SITEMAP_TTL = timedelta(hours=6)
_sitemap_cache = {"generated_at": None, "xml": ""}
def generate_sitemap() -> str:
now = datetime.utcnow()
cached_at = _sitemap_cache.get("generated_at")
if cached_at and now - cached_at < SITEMAP_TTL:
return _sitemap_cache["xml"]
urls: List[str] = []
static_paths = ["/", "/tools", "/docs", "/tutorials", "/about"]
for path in static_paths:
urls.append(_url_entry(path, "daily", "1.0"))
conn = connect_db()
try:
rows = query_all(conn, "SELECT DISTINCT owner, name FROM tools")
for row in rows:
tool_path = url_for("web.tool_detail", owner=row["owner"], name=row["name"], _external=True)
urls.append(_url_entry(tool_path, "daily", "0.9"))
categories = query_all(conn, "SELECT DISTINCT category FROM tools WHERE category IS NOT NULL")
for row in categories:
cat_path = url_for("web.category", name=row["category"], _external=True)
urls.append(_url_entry(cat_path, "weekly", "0.7"))
finally:
conn.close()
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
xml += "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"
xml += "\n".join(urls)
xml += "\n</urlset>\n"
_sitemap_cache["generated_at"] = now
_sitemap_cache["xml"] = xml
return xml
def sitemap_response() -> Response:
xml = generate_sitemap()
return Response(xml, mimetype="application/xml")
def robots_txt() -> Response:
lines = [
"User-agent: *",
"Allow: /",
"Disallow: /login",
"Disallow: /register",
"Disallow: /dashboard",
"Disallow: /api/",
f"Sitemap: {url_for('web.sitemap', _external=True)}",
]
return Response("\n".join(lines) + "\n", mimetype="text/plain")
def _url_entry(loc: str, changefreq: str, priority: str) -> str:
if not loc.startswith("http"):
loc = url_for("web.index", _external=True).rstrip("/") + loc
return "\n".join([
" <url>",
f" <loc>{loc}</loc>",
f" <changefreq>{changefreq}</changefreq>",
f" <priority>{priority}</priority>",
" </url>",
])