devkit / app /tools /doc_forge /routes.py
Mohammed AL Sarraj
initial deploy
950dcd2
"""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/<owner>/<repo>")
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/<owner>/<repo>/<doc_type>")
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))