gitme.py
Python script, ASCII text executable
1import os 2import flask 3import git 4import mimetypes 5import magic 6from markupsafe import escape 7 8import config 9 10app = flask.Flask(__name__) 11import gitHTTP 12 13mime = magic.Magic(mime=True) 14 15 16def humanSize(value, decimals=2, scale=1024, units=("B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB")): 17for unit in units: 18if value < scale: 19break 20value /= scale 21if int(value) == value: 22# do not return decimals, if the value is already round 23return int(value), unit 24return round(value * 10**decimals) / 10**decimals, unit 25 26 27def guessMIME(path): 28if os.path.isdir(path): 29mimetype = "inode/directory" 30elif mime.from_file(path): 31mimetype = mime.from_file(path) 32else: 33mimetype = "application/octet-stream" 34return mimetype 35 36 37def convertToHTML(path): 38with open(path, "r") as f: 39contents = f.read() 40return contents 41 42 43@app.route("/") 44def main(): 45return flask.render_template("home.html", title="gitme") 46 47 48@app.route("/accounts/", methods=["GET", "POST"]) 49def login(): 50return flask.render_template("login.html", title="gitme") 51 52 53@app.route("/<username>/") 54def userProfile(username): 55return flask.render_template("teapot.html"), 418 56 57 58@app.route("/<username>/<repository>/") 59def repositoryIndex(username, repository): 60return flask.redirect("./tree", code=302) 61 62 63@app.route("/<username>/<repository>/raw/<branch>/<path:subpath>") 64def repositoryRaw(username, repository, branch, subpath): 65serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository)) 66 67app.logger.info(f"Loading {serverRepoLocation}") 68 69if not os.path.exists(serverRepoLocation): 70app.logger.error(f"Cannot load {serverRepoLocation}") 71return flask.render_template("not-found.html"), 404 72 73repo = git.Repo(serverRepoLocation) 74try: 75repo.git.checkout(branch) 76except git.exc.GitCommandError: 77return flask.render_template("not-found.html"), 404 78 79return flask.send_from_directory(config.REPOS_PATH, os.path.join(username, repository, subpath)) 80 81 82@app.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""}) 83@app.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""}) 84@app.route("/<username>/<repository>/tree/<branch>/<path:subpath>") 85def repositoryTree(username, repository, branch, subpath): 86serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository)) 87 88app.logger.info(f"Loading {serverRepoLocation}") 89 90if not os.path.exists(serverRepoLocation): 91app.logger.error(f"Cannot load {serverRepoLocation}") 92return flask.render_template("not-found.html"), 404 93 94repo = git.Repo(serverRepoLocation) 95if not branch: 96branch = repo.heads[0].name 97return flask.redirect(f"./{branch}", code=302) 98try: 99repo.git.checkout(branch) 100except git.exc.GitCommandError: 101return flask.render_template("not-found.html"), 404 102 103branches = repo.heads 104if os.path.isdir(os.path.join(serverRepoLocation, subpath)): 105files = [] 106blobs = [] 107 108for entry in os.listdir(os.path.join(serverRepoLocation, subpath)): 109if not os.path.basename(entry) == ".git": 110files.append(os.path.join(subpath, entry)) 111 112infos = [] 113 114for file in files: 115path = os.path.join(serverRepoLocation, file) 116mimetype = guessMIME(path) 117 118info = { 119"name": os.path.basename(file), 120"serverPath": path, 121"relativePath": file, 122"link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file), 123"size": humanSize(os.path.getsize(path)), 124"mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}", 125} 126 127specialIcon = config.matchIcon(os.path.basename(file)) 128if specialIcon: 129info["icon"] = specialIcon 130elif os.path.isdir(path): 131info["icon"] = config.folderIcon 132elif mimetypes.guess_type(path)[0] in config.fileIcons: 133info["icon"] = config.fileIcons[mimetypes.guess_type(path)[0]] 134else: 135info["icon"] = config.unknownIcon 136 137if os.path.isdir(path): 138infos.insert(0, info) 139else: 140infos.append(info) 141 142return flask.render_template( 143"repo-tree.html", 144username=username, 145repository=repository, 146files=infos, 147subpath=os.path.join("/", subpath), 148branches=branches, 149current=branch 150) 151else: 152path = os.path.join(serverRepoLocation, subpath) 153 154if not os.path.exists(path): 155return flask.render_template("not-found.html"), 404 156 157mimetype = guessMIME(path) 158mode = mimetype.split("/", 1)[0] 159size = humanSize(os.path.getsize(path)) 160 161specialIcon = config.matchIcon(os.path.basename(path)) 162if specialIcon: 163icon = specialIcon 164elif os.path.isdir(path): 165icon = config.folderIcon 166elif mimetypes.guess_type(path)[0] in config.fileIcons: 167icon = config.fileIcons[mimetypes.guess_type(path)[0]] 168else: 169icon = config.unknownIcon 170 171contents = None 172if mode == "text": 173contents = convertToHTML(path) 174 175return flask.render_template( 176"repo-file.html", 177username=username, 178repository=repository, 179file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath), 180branches=branches, 181current=branch, 182mode=mode, 183mimetype=mimetype, 184size=size, 185icon=icon, 186subpath=os.path.join("/", subpath), 187basename=os.path.basename(path), 188contents=contents 189) 190 191 192@app.route("/<username>/<repository>/forum/") 193def repositoryForum(username, repository): 194return flask.render_template("repo-forum.html", username=username, repository=repository) 195 196 197@app.route("/<username>/<repository>/docs/") 198def repositoryDocs(username, repository): 199return flask.render_template("repo-docs.html", username=username, repository=repository) 200 201 202@app.route("/<username>/<repository>/releases/") 203def repositoryReleases(username, repository): 204return flask.render_template("repo-releases.html", username=username, repository=repository) 205 206 207@app.route("/<username>/<repository>/branches/") 208def repositoryBranches(username, repository): 209return flask.render_template("repo-branches.html", username=username, repository=repository) 210 211 212@app.route("/<username>/<repository>/people/") 213def repositoryPeople(username, repository): 214return flask.render_template("repo-people.html", username=username, repository=repository) 215 216 217@app.route("/<username>/<repository>/activity/") 218def repositoryActivity(username, repository): 219return flask.render_template("repo-activity.html", username=username, repository=repository) 220 221 222@app.route("/<username>/<repository>/ci/") 223def repositoryCI(username, repository): 224return flask.render_template("repo-ci.html", username=username, repository=repository) 225 226 227@app.route("/<username>/<repository>/settings/") 228def repositorySettings(username, repository): 229flask.abort(401) 230return flask.render_template("repo-settings.html", username=username, repository=repository) 231 232 233@app.errorhandler(404) 234def e404(error): 235return flask.render_template("not-found.html"), 404 236 237 238@app.errorhandler(401) 239def e401(error): 240return flask.render_template("unauthorised.html"), 401 241 242 243@app.errorhandler(403) 244def e403(error): 245return flask.render_template("forbidden.html"), 403 246 247 248@app.errorhandler(418) 249def e418(error): 250return flask.render_template("teapot.html"), 418 251 252