"""DocForge routes — generate full documentation for any GitHub repo.""" import json import os from flask import (Blueprint, render_template, request, jsonify, current_app, Response) from .github_fetcher import (parse_repo_url, get_repo_info, get_file_tree, fetch_key_files) from .doc_generator import (generate_readme, generate_architecture, generate_api_docs) from .db import get_db, upsert_repo, upsert_doc, get_docs, list_recent bp = Blueprint("doc_forge", __name__, template_folder="templates") def _db(): db_path = os.path.join(os.path.dirname(current_app.root_path), "docforge.db") return get_db(db_path) # ── Pages ────────────────────────────────────────────────────────────────────── @bp.route("/") def index(): db = _db() recent = list_recent(db) return render_template("doc_forge/index.html", recent=recent) @bp.route("/docs//") def docs_view(owner, repo): db = _db() data = get_docs(db, owner, repo) if not data: return render_template("doc_forge/index.html", recent=list_recent(db), error=f"No docs found for {owner}/{repo}. Generate them first.") return render_template("doc_forge/docs.html", owner=owner, repo=repo, data=data) # ── API ──────────────────────────────────────────────────────────────────────── @bp.route("/api/analyze", methods=["POST"]) def analyze(): """Step 1: fetch repo metadata + file tree. Returns info without generating docs.""" body = request.get_json(silent=True) or {} url = (body.get("url") or "").strip() if not url: return jsonify({"error": "repo_url is required"}), 400 try: owner, repo = parse_repo_url(url) info = get_repo_info(owner, repo) tree = get_file_tree(owner, repo, info["default_branch"]) return jsonify({ "owner": owner, "repo": repo, "info": info, "file_count": len(tree), "tree_preview": tree[:20], }) except Exception as exc: return jsonify({"error": str(exc)}), 400 @bp.route("/api/generate", methods=["POST"]) def generate(): """Step 2: generate all docs for a repo.""" body = request.get_json(silent=True) or {} url = (body.get("url") or "").strip() types = body.get("types", ["readme", "architecture", "api"]) if not url: return jsonify({"error": "url is required"}), 400 try: owner, repo = parse_repo_url(url) info = get_repo_info(owner, repo) tree = get_file_tree(owner, repo, info["default_branch"]) files = fetch_key_files(owner, repo, tree, info["default_branch"]) db = _db() repo_id = upsert_repo(db, owner, repo, info, tree) results = {} if "readme" in types: readme = generate_readme(info, tree, files) upsert_doc(db, repo_id, "readme", readme) results["readme"] = readme if "architecture" in types: arch = generate_architecture(info, tree, files) upsert_doc(db, repo_id, "architecture", arch) results["architecture"] = arch if "api" in types: api_docs = generate_api_docs(info, tree, files) upsert_doc(db, repo_id, "api", api_docs) results["api"] = api_docs return jsonify({"owner": owner, "repo": repo, "info": info, "docs": results}) except Exception as exc: return jsonify({"error": str(exc)}), 500 @bp.route("/api/download///") def download(owner, repo, doc_type): """Download a generated doc as a file.""" db = _db() data = get_docs(db, owner, repo) if not data or doc_type not in data: return jsonify({"error": "not found"}), 404 content = data[doc_type] if doc_type == "readme": text = content.get("readme", "") filename = "README.md" mime = "text/markdown" else: text = json.dumps(content, indent=2, ensure_ascii=False) filename = f"{doc_type}.json" mime = "application/json" return Response(text, mimetype=mime, headers={"Content-Disposition": f'attachment; filename="{filename}"'}) @bp.route("/api/recent") def recent(): db = _db() return jsonify(list_recent(db))