~fkfd/git.gmi

59c2e948797d088e795cc2290fa8c3004ddb71c1 — Frederick Yin 1 year, 4 months ago ee8f25f v0.2.0
Massive improvements

- Python CGI server script renamed to gateway.py to avoid confusion
- Repo not found error handling
- Page header & navs
- Fix faulty 20 response header
- View (non-)raw blob
4 files changed, 56 insertions(+), 27 deletions(-)

M git-gmi/cgi
M git-gmi/const.py
R git-gmi/{cgi.py => gateway.py}
M git-gmi/git.py
M git-gmi/cgi => git-gmi/cgi +1 -1
@@ 1,3 1,3 @@
#!/home/fakefred/p/git.gmi/venv/bin/python3.8
# gotta change the executable path before running
import cgi.py
\ No newline at end of file
import gateway
\ No newline at end of file

M git-gmi/const.py => git-gmi/const.py +4 -2
@@ 1,5 1,7 @@
GIT_CATALOG = "/home/fakefred/p/gemini/git/"
STATUS_SUCCESS = "20 SUCCESS"
GIT_CATALOG = "/home/fakefred/p/gemini/repos/"
CGI_PATH = "/git/cgi/"
GIT_GMI_SITE_TITLE = "git.gmi demo instance"
STATUS_SUCCESS = "20"
STATUS_NOT_FOUND = "51 NOT FOUND"
STATUS_TEMPORARY_FAILURE = "40 TEMPORARY FAILURE"
META_GEMINI = "text/gemini"

R git-gmi/cgi.py => git-gmi/gateway.py +10 -10
@@ 10,7 10,7 @@ def generate_navigation(repo_name: str):
    pass  # TODO


def handle_cgi_request(path: str):
def handle_cgi_request(path: str, query: str):
    # intended to work with Jetforce.
    # url: gemini://git.gemini.site/cgi-bin/cgi.py/repo/src/static/css/[index.css]
    # path: /repo/src/static/css/[index.css]


@@ 18,8 18,8 @@ def handle_cgi_request(path: str):
    path_trace = path[1:].split("/")
    if path_trace == [""]:  # empty path
        print(f"{STATUS_SUCCESS} {META_GEMINI}")  # welcome page
        print("Welcome to the git.gmi demo")
        print("Available repositories:")
        print(f"# Welcome to {GIT_GMI_SITE_TITLE}")
        print("## Available repositories:")
        print("\n".join([f"=> {dir}/" for dir in listdir(GIT_CATALOG)]))
        return



@@ 51,18 51,18 @@ def handle_cgi_request(path: str):
        if len(path_trace) > 2:
            branch = path_trace[2]

        if len(path_trace) == 3:
            location = []
        else:
            location = path_trace[3:]
        location = path_trace[3:]

        try:  # is dir
            print(repo.view_tree(branch, location))
        except FileNotFoundError:  # is file
            try:
                print(repo.view_blob(branch, location))
                if query == "raw":
                    print(repo.view_raw_blob(branch, location))
                else:
                    print(repo.view_blob(branch, location))
            except FileNotFoundError:
                print("50 Error locating content")
                print(STATUS_NOT_FOUND)

    elif view == "log":
        try:


@@ 73,4 73,4 @@ def handle_cgi_request(path: str):
            return


handle_cgi_request(environ.get("PATH_INFO"))
handle_cgi_request(environ.get("PATH_INFO"), environ.get("QUERY_STRING"))

M git-gmi/git.py => git-gmi/git.py +41 -14
@@ 2,6 2,9 @@ from pygit2 import *
import mimetypes
from const import *

mimetypes.add_type("text/gemini", ".gmi")
mimetypes.add_type("text/gemini", ".gemini")


class GitGmiRepo:
    def __init__(self, name: str, path: str):


@@ 9,25 12,37 @@ class GitGmiRepo:
        self.path = path
        try:
            self.repo = Repository(path)
        except FileNotFoundError:
            print(f"Error: repository {path} not found")
        except GitError:
            raise FileNotFoundError(f"Error: no such repo: {name}")

    def generate_header(self):
        header = (
            f"# {self.name}\n"
            f"=> {CGI_PATH} {GIT_GMI_SITE_TITLE}\n"
            f"=> {CGI_PATH}{self.name}/summary summary\n"
            f"=> {CGI_PATH}{self.name}/tree/master/ tree\n"
            f"=> {CGI_PATH}{self.name}/log log\n\n"
        )
        return header

    def view_summary(self) -> str:
        response = f"{STATUS_SUCCESS} {META_GEMINI}\n" + self.generate_header()
        tree = self.get_tree("master")
        trls = self.list_tree(tree)
        found_readme = False
        for item in trls:
            if item["type"] == "file" and item["name"].lower().split(".")[0] == (
                "readme"
            if (
                item["type"] == "file"
                and item["name"].lower().split(".")[0] == ("readme")
                and not found_readme
            ):
                # mimetypes.guess_type() returns tuple (type, encoding)
                # only the first one of which we care about
                response = (
                    f"{STATUS_SUCCESS} {mimetypes.guess_type(item['name'])[0]}\n"
                    "=> tree/master/ tree\n"
                    "=> log/ log\n\n"
                found_readme = True
                response += (
                    f"## {item['name']} | {item['size']} bytes\n"
                    f"{item['blob'].data.decode('utf-8')}"
                )
                response += item["blob"].data.decode("utf-8")
                break
        if not found_readme:
            response += "## No readme found."
        return response

    def get_commit_log(self) -> list:


@@ 48,7 63,7 @@ class GitGmiRepo:
        return log  # reverse chronical order

    def view_log(self) -> str:
        response = f"{STATUS_SUCCESS} {META_GEMINI}\n"
        response = f"{STATUS_SUCCESS} {META_GEMINI}\n" + self.generate_header()
        log = self.get_commit_log()
        for cmt in log:
            response += f"## {cmt['short_id']} - {cmt['author']}\n{cmt['msg']}\n\n"


@@ 123,7 138,7 @@ class GitGmiRepo:
    def view_tree(self, branch: str, location=[]) -> str:
        # actual Gemini response
        # consists of a header and a body
        response = f"{STATUS_SUCCESS} {META_GEMINI}\n"
        response = f"{STATUS_SUCCESS} {META_GEMINI}\n" + self.generate_header()
        tree = self.get_tree(branch)
        contents = self.list_tree(tree, location)
        for item in contents:


@@ 151,6 166,18 @@ class GitGmiRepo:

    def view_blob(self, branch: str, location=[]) -> str:
        blob = self.get_blob(branch, location)
        response = (
            f"{STATUS_SUCCESS} {META_GEMINI}\n"
            + self.generate_header()
            + f"## {self.name}/{'/'.join(location)} | {blob.size} bytes\n\n"
            f"=> {blob.name}?raw view raw\n\n"
            f"```\n"
        )
        response += blob.data.decode("utf-8") + "\n```"
        return response

    def view_raw_blob(self, branch: str, location=[]) -> str:
        blob = self.get_blob(branch, location)
        guessed_mimetype = mimetypes.guess_type(blob.name)[0] or "text/plain"
        response = f"{STATUS_SUCCESS} {guessed_mimetype}\n"
        response += blob.data.decode("utf-8")