"""Reproducibly build the atendyo.com STATIC site (Netlify) from source. atendyo.com is served statically by Netlify (the backend only renders these same pages). This regenerates the EXACT file tree the site needs so a landing/blog change can be redeployed without hand-assembling files in /tmp: index.html <- app/landing_ui/index.html (verbatim) og.png <- app/landing_ui/og.png blog/index.html <- render_index("es") blog/en.html <- render_index("en") blog/.html <- render_article(post) for every post robots.txt, sitemap.xml <- regenerated, canonical on atendyo.com _redirects <- the /demo -> backend redirect (load-bearing: every cold-email demo link is atendyo.com/demo?d=...) For static hosting the EN blog index lives at /blog/en (file blog/en.html), so we rewrite the renderer's "/blog?lang=en" links to "/blog/en". Usage: python -m scripts.build_static_site [output_dir] (default: dist/site) """ from __future__ import annotations import shutil import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT)) from app.blog.posts import POSTS # noqa: E402 from app.blog.render import render_article, render_index # noqa: E402 SITE = "https://atendyo.com" BACKEND = "https://victor34593993-flexigo-support-bot.hf.space" LANDING = ROOT / "app" / "landing_ui" / "index.html" OG = ROOT / "app" / "landing_ui" / "og.png" LEGAL_DIR = ROOT / "app" / "legal_ui" # Served at /legal/ (pretty URL). The privacy policy URL Meta needs is # https://atendyo.com/legal/privacidad — must be a real, accessible page. LEGAL_PAGES = ("aviso-legal", "privacidad", "cookies", "condiciones", "encargo") def _static(html: str) -> str: # the EN blog index is a file (/blog/en), not a query string, when static return html.replace(f"{SITE}/blog?lang=en", f"{SITE}/blog/en") def _robots() -> str: return ( "User-agent: *\n" "Allow: /$\n" "Allow: /blog\n" "Disallow: /admin\n" "Disallow: /portal\n" "Disallow: /preview\n" "Disallow: /chat\n" "Disallow: /widget-config\n" f"Sitemap: {SITE}/sitemap.xml\n" ) def _sitemap() -> str: rows = [ f" {SITE}/weekly1.0", f" {SITE}/blogweekly0.7", ] for p in POSTS: rows.append( f" {SITE}/blog/{p.slug}{p.date}" "monthly0.6" ) return ( '\n' '\n' + "\n".join(rows) + "\n\n" ) def _redirects() -> str: # The ONLY redirect the site needs: atendyo.com/demo -> backend /demo (302, # query string auto-forwarded). Load-bearing for every email demo link. # MUST be a _redirects file in the publish root — a netlify.toml inside the # deployed --dir is published as a plain file and NOT processed as config. return f"/demo {BACKEND}/demo 302\n" def build(out: Path) -> None: if out.exists(): shutil.rmtree(out) blog = out / "blog" blog.mkdir(parents=True) (out / "index.html").write_text(LANDING.read_text(encoding="utf-8"), encoding="utf-8") shutil.copyfile(OG, out / "og.png") legal = out / "legal" legal.mkdir() for page in LEGAL_PAGES: (legal / f"{page}.html").write_text( (LEGAL_DIR / f"{page}.html").read_text(encoding="utf-8"), encoding="utf-8") (blog / "index.html").write_text(_static(render_index("es")), encoding="utf-8") (blog / "en.html").write_text(_static(render_index("en")), encoding="utf-8") for p in POSTS: (blog / f"{p.slug}.html").write_text(_static(render_article(p)), encoding="utf-8") (out / "robots.txt").write_text(_robots(), encoding="utf-8") (out / "sitemap.xml").write_text(_sitemap(), encoding="utf-8") (out / "_redirects").write_text(_redirects(), encoding="utf-8") n = len(list(blog.glob("*.html"))) print(f"built site at {out}: index + {n} blog pages + {len(LEGAL_PAGES)} legal " "pages + sitemap/robots/_redirects/og.png") if __name__ == "__main__": target = Path(sys.argv[1]) if len(sys.argv) > 1 else ROOT / "dist" / "site" build(target)