AnonSec Team
AnonSec Team
Server IP : 149.13.87.7  /  Your IP : 185.174.145.2
Web Server : nginx
System : Linux h2 3.10.0-1160.76.1.el7.x86_64 #1 SMP Wed Aug 10 16:21:17 UTC 2022 x86_64
User : root ( 0)
PHP Version : 8.0.28
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON
Directory (0755) :  /var/www/

[  Home  ][  C0mmand  ][  Upload File  ]

Current File : //var/www/build_mass_v3.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
=============================================================
  MASS WEBSITE GENERATOR v3 — Real Hugo Themes
  Uses 12 different pre-cloned Hugo themes for
  maximum structural uniqueness across domains.

  REQUIRES: run setup_themes.sh first to cache themes
  
  Usage:
    export CF_ACCOUNT_ID="xxx"
    export CF_API_TOKEN="xxx"
    python3 build_mass_v3.py domains.txt
    python3 build_mass_v3.py food.com drink.com
=============================================================
"""

import os, sys, json, time, shutil, subprocess
import requests, hashlib, csv
from datetime import datetime

# ─────────────────────────────────────────────
#  CONFIG
# ─────────────────────────────────────────────
CF_ACCOUNT_ID  = os.environ.get("CF_ACCOUNT_ID", "39a058510fc70b590cd19740cd601ab9")
CF_API_TOKEN   = os.environ.get("CF_API_TOKEN", "cfut_NEgUuC3EBxftU0cRNvpk2jXDYDxLM1YBxIBnSNLe1132fde9")

UNSPLASH_KEY   = "D4Afb_uLn2foDIf6OehLo2Eq_u_6XaKfK7DdKgRj9Mg"
CF_MODEL       = "@cf/meta/llama-3.2-1b-instruct"
WEBROOT        = "/var/www"
THEMES_CACHE   = "/var/www/_themes_cache"
LOG_FILE       = "/var/www/build_log.csv"

# Themes requiring Hugo Extended (SCSS) — incompatible with standard Hugo on CentOS 7
SCSS_THEMES = {"hello-friend-ng", "coder", "terminal", "ficurinia", "ananke", "beautiful-hugo", "mainroad"}

# ─────────────────────────────────────────────
#  THEME REGISTRY
#  Each entry defines exactly how to configure
#  that theme — params, content structure, etc.
# ─────────────────────────────────────────────
THEMES = {

    "papermod": {
        "repo": "papermod",
        "name": "PaperMod",
        "config_extra": """
[params]
  author = "{brand}"
  description = "{desc}"
  ShowReadingTime = true
  ShowPostNavLinks = true
  ShowBreadCrumbs = true
  ShowCodeCopyButtons = true
  ShowShareButtons = false
  defaultTheme = "dark"
  homeInfoParams.Title = "{tagline}"
  homeInfoParams.Content = "{hero_text}"
  [[params.socialIcons]]
    name = "email"
    url  = "mailto:{email}"
""",
        "menu_extra": """
[[menu.main]]
  identifier = "search"
  name = "Search"
  url  = "/search/"
  weight = 10
""",
        "needs_search": True,
        "content_format": "standard",
        "index_type": "home_info",  # PaperMod home-info mode
    },

    "ananke": {
        "repo": "ananke",
        "name": "Ananke",
        "config_extra": """
[params]
  description        = "{desc}"
  featured_image     = "{hero_img}"
  recent_posts_number = 3
  text_color         = ""
  background_color_class = "bg-near-black"
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "terminal": {
        "repo": "terminal",
        "name": "Terminal",
        "config_extra": """
[params]
  themeColor      = "{accent}"
  fullWidthTheme  = false
  centerTheme     = true
  showMenuItems   = 5
  showLanguageSelector = false
  [params.twitter]
    creator = ""
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "hello-friend-ng": {
        "repo": "hello-friend-ng",
        "name": "Hello Friend NG",
        "config_extra": """
[author]
  name = "{brand} Team"
[params]
  dateform     = "Jan 2, 2006"
  dateformFull = "Mon Jan 2, 2006"
  description  = "{desc}"
  [params.logo]
    logoText      = "{brand}"
    logoHomeLink  = "/"
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "coder": {
        "repo": "coder",
        "name": "Coder",
        "config_extra": """
[params]
  author         = "{brand}"
  description    = "{desc}"
  keywords       = "blog,developer,personal"
  info           = "{tagline}"
  avatarurl      = "{hero_img}"
  footerLeft     = "Powered by {brand}"
  footerRight    = "{year}"
  colorScheme    = "dark"
  hidecolorschemetoggle = false
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "archie": {
        "repo": "archie",
        "name": "Archie",
        "config_extra": """
[params]
  mode       = "dark"
  useCDN     = false
  subtitle   = "{tagline}"
  description= "{desc}"
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "paper": {
        "repo": "paper",
        "name": "Paper",
        "config_extra": """
[params]
  color      = "linen"
  twitter    = ""
  description= "{desc}"
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "mainroad": {
        "repo": "mainroad",
        "name": "Mainroad",
        "config_extra": """
[params]
  description   = "{desc}"
  opengraph     = true
  schema        = true
  twitter_cards = false
  readmore      = false
  authorbox     = true
  toc           = false
  pager         = true
  [params.sidebar]
    home   = "right"
    list   = "left"
    single = false
    widgets = ["recent", "categories", "taglist"]
  [params.widgets]
    recent_num   = 5
    tags_counter = false
  [params.author]
    name  = "{brand} Team"
""",
        "menu_extra": """
[[menu.side]]
  name       = "About"
  pre        = "<i class='fa fa-heart'></i>"
  url        = "/about/"
  weight     = 1
""",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "beautiful-hugo": {
        "repo": "beautiful-hugo",
        "name": "Beautiful Hugo",
        "config_extra": """
[params]
  subtitle       = "{tagline}"
  logo           = ""
  description    = "{desc}"
  bigimg         = [{{src = "{hero_img}", desc = "{tagline}"}}]
  useHLJS        = true
  pygmentsStyle  = "trac"
  pygmentsCodeFences = true
  show_reading_time = true
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "mini": {
        "repo": "mini",
        "name": "Mini",
        "config_extra": """
[params]
  author      = "{brand}"
  description = "{desc}"
  keywords    = "blog, {brand}"
  readMore    = false
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "monochrome": {
        "repo": "monochrome",
        "name": "Monochrome",
        "config_extra": """
[params]
  description        = "{desc}"
  listPagePreviewMode = "summary"
  navbar             = "normal"
  author             = "{brand}"
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },

    "ficurinia": {
        "repo": "ficurinia",
        "name": "Ficurinia",
        "config_extra": """
[params]
  author       = "{brand}"
  description  = "{desc}"
  logo         = ""
  favicon      = ""
  iconTheme    = "filled"
  headerRandomize = false
""",
        "menu_extra": "",
        "needs_search": False,
        "content_format": "standard",
        "index_type": "standard",
    },
}

THEME_KEYS = list(THEMES.keys())

# ─────────────────────────────────────────────
#  NICHES
# ─────────────────────────────────────────────
NICHES = [
    ("Email Marketing Agency", "professional email marketing and deliverability",
     ["email marketing best practices","how to improve email deliverability",
      "understanding SPF DKIM and DMARC","domain warmup complete guide","list hygiene best practices"]),
    ("Digital Marketing Studio", "full-service digital marketing and growth",
     ["digital marketing trends 2025","how to grow your online presence",
      "content marketing that converts","paid advertising best practices","measuring marketing ROI"]),
    ("SaaS Growth Agency", "B2B SaaS marketing and demand generation",
     ["saas growth strategies","demand generation for b2b startups",
      "product led growth explained","reducing churn in saas","building a go to market strategy"]),
    ("Brand Communications", "strategic brand communications and messaging",
     ["building a strong brand identity","why brand consistency matters",
      "content strategy for brands","earned media strategies","brand messaging framework"]),
    ("Performance Marketing", "performance-based digital advertising and ROI",
     ["performance marketing fundamentals","ppc campaign optimization",
      "retargeting strategies that work","landing page best practices","ab testing guide for marketers"]),
    ("Growth Hacking Studio", "rapid growth experimentation and user acquisition",
     ["growth hacking strategies 2025","viral loop design for startups",
      "referral program best practices","funnel optimization tips","user acquisition on a budget"]),
    ("Marketing Automation", "marketing automation and CRM integration specialists",
     ["marketing automation guide","lead scoring best practices",
      "crm integration for marketers","email sequence strategy","behavioral marketing explained"]),
    ("Data Analytics Agency", "data-driven marketing and analytics consulting",
     ["data driven marketing guide","attribution modeling explained",
      "customer segmentation strategies","predictive analytics in marketing","building marketing dashboards"]),
]

TAGLINES = [
    "Results You Can Measure.",       "Built for Performance.",
    "Growth. Strategy. Execution.",   "Smarter Marketing Starts Here.",
    "Precision at Scale.",            "Where Data Meets Creativity.",
    "Your Growth Partner.",           "Performance Without Compromise.",
    "Strategy Meets Execution.",      "Engineered for Results.",
    "Marketing That Moves Needles.",  "Beyond Clicks. Real Growth.",
    "Scale Smarter. Grow Faster.",    "The Agency That Delivers.",
    "Impact at Every Touchpoint.",    "Bold Ideas. Proven Results.",
]

ACCENT_COLORS = [
    "#4f8cff","#e0a030","#2ecc71","#e53060",
    "#8b5cf6","#06b6d4","#ec4899","#10b981",
    "#f59e0b","#6366f1","#d97706","#58a6ff",
]

BLOG_ANGLES = [
    "practical how-to guide with actionable steps",
    "opinion piece with a strong point of view",
    "beginner-friendly explainer with real examples",
    "advanced deep-dive for experienced professionals",
]

AI_TONES = [
    "authoritative and confident",
    "friendly and approachable",
    "data-driven and analytical",
    "bold and direct",
    "warm and educational",
]

UNSPLASH_HERO = [
    "dark server technology","night city digital abstract",
    "dark office professional","technology network dark blue",
    "dark minimal workspace","server room dark","abstract dark digital",
    "dark data center","dark modern architecture","dark blue abstract",
]

# ─────────────────────────────────────────────
#  HELPERS
# ─────────────────────────────────────────────
def dseed(domain):
    return int(hashlib.md5(domain.encode()).hexdigest(), 16)

def pick(lst, seed, offset=0):
    return lst[(seed + offset) % len(lst)]

def log(msg, icon="  →"): print(f"{icon} {msg}")
def step(t): print(f"\n{'─'*52}\n  {t}\n{'─'*52}")

def ask_ai(prompt, retries=3, seed=0, call_idx=0):
    if not CF_ACCOUNT_ID or not CF_API_TOKEN:
        return "We deliver world-class solutions tailored to your business needs."
    url = (f"https://api.cloudflare.com/client/v4/accounts/"
           f"{CF_ACCOUNT_ID}/ai/run/{CF_MODEL}")
    headers = {"Authorization": f"Bearer {CF_API_TOKEN}",
               "Content-Type": "application/json"}
    payload = {
        "messages": [
            {"role": "system", "content":
             "You are a senior copywriter. Write compelling website copy. "
             "Plain paragraphs only — no bullet points, no markdown headers."},
            {"role": "user", "content": prompt}
        ],
        "max_tokens": 220
    }
    for attempt in range(1, retries + 1):
        try:
            r = requests.post(url, headers=headers, json=payload, timeout=45)
            r.raise_for_status()
            result = r.json()["result"]["response"].strip()
            time.sleep(3 + (seed % 2))
            return result
        except Exception as e:
            err = str(e)[:50]
            log(f"AI attempt {attempt}: {err}", "  ⚠")
            if attempt < retries:
                time.sleep(8)
    # Fallback — never block the build
    fallbacks = [
        "We deliver world-class solutions that drive real results for growing businesses.",
        "Our team combines deep expertise with proven strategies to help you scale faster and smarter.",
        "We are dedicated to delivering measurable outcomes through innovative and data-driven approaches.",
        "We have helped hundreds of businesses achieve their goals through focused execution.",
        "Great results start with understanding your unique challenges and building the right strategy.",
    ]
    return fallbacks[seed % len(fallbacks)]

def unsplash(queries, dest):
    os.makedirs(os.path.dirname(dest), exist_ok=True)
    for q in queries + ["dark technology abstract"]:
        try:
            r = requests.get(
                "https://api.unsplash.com/photos/random",
                params={"query": q, "orientation": "landscape", "w": 1400, "h": 800},
                headers={"Authorization": f"Client-ID {UNSPLASH_KEY}"},
                timeout=15)
            if r.status_code == 200:
                url = r.json()["urls"]["regular"]
                data = requests.get(url, timeout=25).content
                with open(dest, "wb") as f: f.write(data)
                time.sleep(0.5)
                return True
        except Exception: pass
        time.sleep(1)
    return False

def write_file(path, content):
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "w") as f:
        f.write(content)

def md(fm, body):
    out = "---\n"
    for k, v in fm.items():
        out += f'{k}: "{str(v).replace(chr(34), chr(39))}"\n'
    return out + "---\n\n" + body

def log_result(domain, status, theme, niche, pages):
    new = not os.path.isfile(LOG_FILE)
    with open(LOG_FILE, "a", newline="") as f:
        w = csv.writer(f)
        if new: w.writerow(["domain","status","theme","niche","pages","ts"])
        w.writerow([domain, status, theme, niche, pages,
                    datetime.now().strftime("%Y-%m-%d %H:%M:%S")])

def already_built(domain):
    if not os.path.isfile(LOG_FILE): return False
    with open(LOG_FILE) as f:
        for row in csv.reader(f):
            if row and row[0] == domain and row[1] == "ok":
                return True
    return False

def theme_available(theme_key):
    if theme_key in SCSS_THEMES:
        return False  # needs Hugo Extended — skip on CentOS 7
    path = f"{THEMES_CACHE}/{THEMES[theme_key]['repo']}"
    return os.path.isdir(path)

def copy_theme(theme_key, dest_themes_dir):
    src = f"{THEMES_CACHE}/{THEMES[theme_key]['repo']}"
    dst = f"{dest_themes_dir}/{theme_key}"
    if os.path.exists(dst):
        shutil.rmtree(dst)
    shutil.copytree(src, dst)
    return dst

# ─────────────────────────────────────────────
#  MAIN BUILD
# ─────────────────────────────────────────────
def build_domain(domain, index=0):
    if already_built(domain):
        print(f"  ⏭  SKIP {domain}")
        return "skip"

    seed       = dseed(domain)
    hugo_root  = f"{WEBROOT}/{domain}"
    brand      = domain.split(".")[0].capitalize()
    year       = datetime.now().year
    date_str   = datetime.now().strftime("%Y-%m-%dT%H:%M:%S+00:00")

    # Pick unique combination
    # Filter to only available themes
    available = [k for k in THEME_KEYS if theme_available(k)]
    if not available:
        log("No themes cached! Run setup_themes.sh first.", "  ✗")
        return False

    theme_key  = pick(available,   seed, 0)
    niche_data = pick(NICHES,      seed, 1)
    tagline    = pick(TAGLINES,    seed, 2)
    accent     = pick(ACCENT_COLORS, seed, 3)
    blog_angle = pick(BLOG_ANGLES, seed, 4)
    ai_tone    = pick(AI_TONES,    seed, 5)

    niche_label, niche_desc, blog_topics = niche_data
    theme_cfg  = THEMES[theme_key]
    email      = f"hello@{domain}"

    print(f"\n{'═'*52}")
    print(f"  [{index+1}] {domain}")
    print(f"  Theme : {theme_cfg['name']} ({theme_key})")
    print(f"  Niche : {niche_label}")
    print(f"  Tone  : {ai_tone}")
    print(f"{'═'*52}")

    # ── 1. Scaffold ───────────────────────────────
    if os.path.exists(hugo_root):
        shutil.rmtree(hugo_root)

    r = subprocess.run(["hugo","new","site", hugo_root],
                       stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                       universal_newlines=True)
    if r.returncode != 0:
        log(f"Scaffold failed: {r.stderr[:80]}", "  ✗")
        log_result(domain, "fail_scaffold", theme_key, niche_label, 0)
        return False
    log("Hugo scaffold OK", "  ✅")

    # ── 2. Copy theme ──────────────────────────────
    try:
        copy_theme(theme_key, f"{hugo_root}/themes")
        log(f"Theme {theme_cfg['name']} copied", "  ✅")
    except Exception as e:
        log(f"Theme copy failed: {e}", "  ✗")
        log_result(domain, "fail_theme", theme_key, niche_label, 0)
        return False

    # ── 3. Images ─────────────────────────────────
    img_dir = f"{hugo_root}/static/img"
    os.makedirs(img_dir, exist_ok=True)
    hi = seed % len(UNSPLASH_HERO)
    queries = [UNSPLASH_HERO[hi], UNSPLASH_HERO[(hi+1) % len(UNSPLASH_HERO)]]
    hero_ok = unsplash(queries, f"{img_dir}/hero.jpg")
    hero_url = f"https://{domain}/img/hero.jpg" if hero_ok else f"https://picsum.photos/seed/{seed}/1400/800"
    log(f"Hero image: {'✓' if hero_ok else '✗ (placeholder)'}")

    # ── 4. Config ─────────────────────────────────
    # Build theme-specific extra config
    extra = theme_cfg["config_extra"].format(
        brand=brand, desc=niche_desc, tagline=tagline,
        email=email, year=year, hero_img=hero_url,
        accent=accent, hero_text=""  # filled after AI gen
    )
    menu_extra = theme_cfg.get("menu_extra", "")

    config = f"""baseURL = "https://{domain}/"
languageCode = "en-us"
title = "{brand}"
theme = "{theme_key}"

[pagination]
  pagerSize = 6

[sitemap]
  changefreq = "weekly"
  priority   = 0.5

[[menu.main]]
  name   = "Home"
  url    = "/"
  weight = 1
[[menu.main]]
  name   = "Blog"
  url    = "/posts/"
  weight = 2
[[menu.main]]
  name   = "About"
  url    = "/about/"
  weight = 3
[[menu.main]]
  name   = "Contact"
  url    = "/contact/"
  weight = 4
{menu_extra}
{extra}
"""
    write_file(f"{hugo_root}/hugo.toml", config)

    # robots.txt
    write_file(f"{hugo_root}/static/robots.txt",
               f"User-agent: *\nAllow: /\nSitemap: https://{domain}/sitemap.xml\n")

    # Search page for PaperMod
    if theme_cfg.get("needs_search"):
        write_file(f"{hugo_root}/content/search.md",
                   '---\ntitle: "Search"\nlayout: "search"\n---\n')

    # ── 5. AI Content ─────────────────────────────
    log(f"Generating content via AI...")
    call_idx = 0

    def gen(prompt):
        nonlocal call_idx
        r = ask_ai(prompt, seed=seed, call_idx=call_idx)
        call_idx += 1
        return r

    # Home
    hero_text = gen(
        f"Write 2 short punchy sentences for the homepage of {brand}, "
        f"a {niche_desc} company. Tone: {ai_tone}. No bullet points.")
    write_file(f"{hugo_root}/content/_index.md",
               md({"title": brand, "description": tagline}, hero_text))

    # About
    about = gen(
        f"Write 2 paragraphs for the About page of {brand}, a {niche_desc} company. "
        f"Cover mission and why clients trust us. Tone: {ai_tone}.")
    write_file(f"{hugo_root}/content/about/index.md",
               md({"title": "About Us", "description": f"About {brand}"}, about))

    # Contact
    contact = gen(
        f"Write 2 short sentences for the contact page of {brand}. "
        f"Email is {email}. Team responds within 24 hours.")
    write_file(f"{hugo_root}/content/contact/index.md",
               md({"title": "Contact", "description": "Get in touch"}, contact))

    # Privacy Policy
    privacy = gen(
        f"Write a short privacy policy for {brand} at {domain}. "
        f"Cover data collection, cookies, GDPR, third parties. 3 short paragraphs.")
    write_file(f"{hugo_root}/content/privacy-policy/index.md",
               md({"title": "Privacy Policy", "description": "Privacy"}, privacy))

    # Unsubscribe
    unsub = gen(f"Write 2 friendly sentences confirming email unsubscribe for {brand}.")
    write_file(f"{hugo_root}/content/unsubscribe/index.md",
               md({"title": "Unsubscribed", "description": "Unsubscribed"}, unsub))

    # Blog posts with internal links
    log(f"Generating {len(blog_topics)} blog posts...")
    post_urls = [f"/posts/{t.replace(' ','-').lower()}/" for t in blog_topics]

    for i, topic in enumerate(blog_topics):
        slug = topic.replace(" ", "-").lower()
        others = [(blog_topics[j], post_urls[j])
                  for j in range(len(blog_topics)) if j != i][:2]
        links_hint = " ".join([f"Mention and link to '{t}'." for t, _ in others])

        body = gen(
            f"Write a blog post as a {blog_angle} about '{topic}'. "
            f"3 paragraphs, plain text, no bullet points. {links_hint}")

        # Inject markdown internal links
        for ot, ou in others:
            first_word = ot.split()[0]
            if first_word in body:
                body = body.replace(first_word, f"[{first_word}]({ou})", 1)

        write_file(f"{hugo_root}/content/posts/{slug}.md", md(
            {"title": topic.title(), "date": date_str,
             "description": f"Guide on {topic}", "author": "The Team"},
            body))

    log("Content written", "  ✅")

    # ── 6. Fix PaperMod home-info config ─────────
    # Re-write config with actual hero_text now that we have it
    if theme_key == "papermod":
        safe_hero = hero_text.replace('"', "'").replace("\n", " ")[:120]
        config2 = config.replace(
            'homeInfoParams.Content = ""',
            f'homeInfoParams.Content = "{safe_hero}"')
        write_file(f"{hugo_root}/hugo.toml", config2)

    # ── 7. Hugo build ─────────────────────────────
    r = subprocess.run(
        ["hugo", "--source", hugo_root, "--minify"],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE,
        universal_newlines=True)
    combined = r.stdout + r.stderr

    if r.returncode != 0 and "Error" in combined:
        if "TOCSS" in combined or "libsass" in combined or "scss" in combined.lower():
            log(f"Theme {theme_key} needs Hugo Extended, auto-blacklisted", "  ⚠")
            SCSS_THEMES.add(theme_key)
            log_result(domain, "fail_scss", theme_key, niche_label, 0)
            return False
        log("Hugo build FAILED:", "  ✗")
        # Show first real error line
        for line in combined.split("\n"):
            if "Error" in line or "error" in line:
                print(f"    {line}")
                break
        log_result(domain, "fail_hugo", theme_key, niche_label, 0)
        return False

    pages_lines = [l for l in combined.split("\n") if "Pages" in l]
    pages_n = pages_lines[0].strip() if pages_lines else "?"
    log(f"Hugo OK — {pages_n}", "  ✅")

    # ── 8. Nginx ───────────────────────────────────
    nginx = f"""server {{
    listen 80;
    server_name {domain} www.{domain};
    root /var/www/{domain}/public;
    index index.html;
    charset utf-8;
    gzip on;
    gzip_types text/plain text/css application/javascript image/svg+xml;
    location / {{ try_files $uri $uri/ =404; }}
    location ~* \\.(jpg|jpeg|png|gif|ico|css|js|svg|woff2)$ {{
        expires 30d;
        add_header Cache-Control "public, immutable";
    }}
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
}}
"""
    try:
        with open(f"/etc/nginx/conf.d/{domain}.conf", "w") as f:
            f.write(nginx)
        log("Nginx config written", "  ✅")
    except Exception as e:
        log(f"Nginx: {e}", "  ⚠")

    log_result(domain, "ok", theme_key, niche_label, pages_n)
    return True

# ─────────────────────────────────────────────
#  ENTRY POINT
# ─────────────────────────────────────────────
if __name__ == "__main__":

    # Check themes cache
    available = [k for k in THEME_KEYS if theme_available(k)]
    if not available:
        print(f"""
  ✗ No themes found in {THEMES_CACHE}

  Run this FIRST to download all themes:
    bash setup_themes.sh

  Then run this script again.
""")
        sys.exit(1)

    print(f"\n  Available themes ({len(available)}): {', '.join(available)}")

    if len(sys.argv) < 2:
        print("""
Usage:
  python3 build_mass_v3.py domains.txt
  python3 build_mass_v3.py food.com drink.com news.com

Resume: already-built domains skipped automatically
Log:    /var/www/build_log.csv
""")
        sys.exit(1)

    domains = []
    for arg in sys.argv[1:]:
        if os.path.isfile(arg):
            with open(arg) as f:
                for line in f:
                    d = line.strip().lower()
                    if d and not d.startswith("#"):
                        domains.append(d)
        else:
            domains.append(arg.strip().lower())

    domains = list(dict.fromkeys(domains))
    print(f"\n  Domains to build: {len(domains)}")

    ok, skipped, fail = 0, 0, []

    for i, domain in enumerate(domains):
        result = build_domain(domain, i)
        if result == "skip": skipped += 1
        elif result:         ok += 1
        else:                fail.append(domain)

    # Reload nginx
    try:
        subprocess.run(["nginx","-t"],
                       stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        subprocess.run(["systemctl","reload","nginx"],
                       stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        print("\n  ✅ Nginx reloaded")
    except Exception: pass

    if fail:
        with open("/var/www/failed_domains.txt","w") as f:
            f.write("\n".join(fail))
        print(f"\n  ⚠  Failed list → /var/www/failed_domains.txt")
        print(f"  Retry: python3 build_mass_v3.py /var/www/failed_domains.txt")

    print(f"""
{'═'*52}
  SUMMARY
  Built   : {ok}
  Skipped : {skipped}
  Failed  : {len(fail)}
  Log     : {LOG_FILE}
{'═'*52}
""")

AnonSec - 2021