Blob: app.py
Blob id: b585b1d13703e6491143e26bbd9c1690f092c72c
Size: 6.4 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | import os import subprocess from flask import Flask, render_template, request, abort, redirect, url_for from dotenv import load_dotenv from git.repository import get_bare_repos from git.commit import get_commits, get_commit from git.reference import get_references from git.tree import get_tree_items from git.blob import get_blob from git.misc import get_version, validate_repo_name, validate_ref, validate_ref_as_commit, sanitize_path from git.diff import get_diff from highlight import highlight_diff from filters import register_filters load_dotenv() app = Flask(__name__) # for base.html app.jinja_env.globals['request'] = request register_filters(app) @app.context_processor def inject_current_ref(): ref = request.args.get('ref', 'HEAD').strip() # if ref is invalid, default to HEAD to prevent broken links repo_name = request.view_args.get('repo_name') if request.view_args else None if repo_name: try: if not validate_ref_as_commit(f"{repo_path}/{repo_name}", ref): ref = 'HEAD' except: ref = 'HEAD' return {'current_ref': ref} repo_path = os.getenv('GIT_REPO_PATH') @app.route("/") def index(): repos = get_bare_repos(repo_path) version = get_version() return render_template("index.html", repos=repos, version=version) @app.route("/<repo_name>") def repo_detail(repo_name): if not validate_repo_name(repo_name): abort(404) ref = request.args.get('ref', 'HEAD').strip() if not validate_ref_as_commit(f"{repo_path}/{repo_name}", ref): abort(400, "Invalid ref") return redirect(url_for('repo_commits', repo_name=repo_name, ref=ref)) @app.route("/<repo_name>/commits") def repo_commits(repo_name): if not validate_repo_name(repo_name): abort(404) ref = request.args.get('ref', 'HEAD').strip() if not validate_ref_as_commit(f"{repo_path}/{repo_name}", ref): abort(400, "Invalid ref") refs = get_references(f"{repo_path}/{repo_name}") page = int(request.args.get('page', 0)) # maybe pages are not the wisest way to do this? per_page = 50 skip = page * per_page commits = get_commits(f"{repo_path}/{repo_name}", ref=ref, max_count=per_page, skip=skip) has_next = len(commits) == per_page has_prev = page > 0 return render_template("commits.html", repo_name=repo_name, commits=commits, page=page, has_next=has_next, has_prev=has_prev, ref=ref, refs=refs) @app.route("/<repo_name>/commits/<commit_id>") def commit_detail(repo_name, commit_id): if not validate_repo_name(repo_name): abort(404) if not validate_ref_as_commit(f"{repo_path}/{repo_name}", commit_id): abort(400, "Invalid commit id") commit = get_commit(f"{repo_path}/{repo_name}", commit_id) return render_template("commit.html", repo_name=repo_name, commit=commit) @app.route("/<repo_name>/patch/<commit_id>") def commit_patch(repo_name, commit_id): if not validate_repo_name(repo_name): abort(404) if not validate_ref_as_commit(f"{repo_path}/{repo_name}", commit_id): abort(400, "Invalid commit id") # Use git format-patch to generate the patch result = subprocess.run(['git', '--git-dir', f"{repo_path}/{repo_name}", 'format-patch', '-1', '--stdout', commit_id], capture_output=True, text=True, encoding='utf-8') # git leaves encoded headers like =?UTF-8?Q?...?=, but that should be fine for patch view for now, TODO if result.returncode != 0: abort(500, f"Failed to generate patch: {result.stderr}") return result.stdout, 200, {'Content-Type': 'text/plain; charset=utf-8'} @app.route("/<repo_name>/refs") def repo_refs(repo_name): if not validate_repo_name(repo_name): abort(404) refs = get_references(f"{repo_path}/{repo_name}") return render_template("refs.html", repo_name=repo_name, refs=refs) @app.route("/<repo_name>/tree", defaults={'path': ''}) @app.route("/<repo_name>/tree/<path:path>") def repo_tree_path(repo_name, path): if not validate_repo_name(repo_name): abort(404) ref = request.args.get('ref', 'HEAD').strip() if not validate_ref(f"{repo_path}/{repo_name}", ref): abort(400, "Invalid ref") try: path = sanitize_path(path) except ValueError: abort(400, "Invalid path") refs = get_references(f"{repo_path}/{repo_name}") tree_items = get_tree_items(f"{repo_path}/{repo_name}", ref, path) return render_template("tree.html", repo_name=repo_name, ref=ref, path=path, tree_items=tree_items, refs=refs) @app.route("/<repo_name>/blob/<path:path>") def repo_blob_path(repo_name, path): if not validate_repo_name(repo_name): abort(404) ref = request.args.get('ref', 'HEAD').strip() if not validate_ref(f"{repo_path}/{repo_name}", ref): abort(400, "Invalid ref") try: path = sanitize_path(path) except ValueError: abort(400, "Invalid path") refs = get_references(f"{repo_path}/{repo_name}") blob = get_blob(f"{repo_path}/{repo_name}", ref, path) return render_template("blob.html", repo_name=repo_name, ref=ref, path=path, blob=blob, refs=refs) @app.route("/<repo_name>/diff") def repo_diff(repo_name): if not validate_repo_name(repo_name): abort(404) refs = get_references(f"{repo_path}/{repo_name}") id1 = request.args.get('id1') id2 = request.args.get('id2') if id1 and not validate_ref_as_commit(f"{repo_path}/{repo_name}", id1): abort(400, "Invalid id1 (reference from)") if id2 and not validate_ref_as_commit(f"{repo_path}/{repo_name}", id2): abort(400, "Invalid id2 (reference to)") context_lines = int(request.args.get('context_lines', 3)) interhunk_lines = int(request.args.get('interhunk_lines', 0)) # TODO: ADD ERROR HANDLING EVERYWHERE!! try: diff = get_diff(f"{repo_path}/{repo_name}", id1=id1, id2=id2, context_lines=context_lines, interhunk_lines=interhunk_lines) highlighted_patch = highlight_diff(diff['patch']) return render_template("diff.html", diff=diff, refs=refs, context_lines=context_lines, interhunk_lines=interhunk_lines, highlighted_patch=highlighted_patch) except ValueError as e: return render_template("diff.html", error=str(e), refs=refs, context_lines=context_lines, interhunk_lines=interhunk_lines) except Exception as e: return render_template("diff.html", error="Server error", refs=refs, context_lines=context_lines, interhunk_lines=interhunk_lines) if __name__ == "__main__": app.run(debug=True) |