"""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 = "\n" xml += "\n" xml += "\n".join(urls) xml += "\n\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([ " ", f" {loc}", f" {changefreq}", f" {priority}", " ", ])