app.py
    
    Python script, ASCII text executable
        
            1
            import os 
        
            2
            import random 
        
            3
            import subprocess 
        
            4
            from functools import wraps 
        
            5
             
        
            6
            import cairosvg 
        
            7
            import flask 
        
            8
            from flask_sqlalchemy import SQLAlchemy 
        
            9
            import git 
        
            10
            import mimetypes 
        
            11
            import magic 
        
            12
            from flask_bcrypt import Bcrypt 
        
            13
            from markupsafe import escape, Markup 
        
            14
            from flask_migrate import Migrate 
        
            15
            from datetime import datetime 
        
            16
            from enum import Enum 
        
            17
            import shutil 
        
            18
            from PIL import Image 
        
            19
            from cairosvg import svg2png 
        
            20
            import platform 
        
            21
             
        
            22
            import config 
        
            23
             
        
            24
            app = flask.Flask(__name__) 
        
            25
             
        
            26
            from flask_httpauth import HTTPBasicAuth 
        
            27
            auth = HTTPBasicAuth() 
        
            28
             
        
            29
            app.config["SQLALCHEMY_DATABASE_URI"] = config.DB_URI 
        
            30
            app.config["SECRET_KEY"] = config.DB_PASSWORD 
        
            31
            app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False 
        
            32
            db = SQLAlchemy(app) 
        
            33
            bcrypt = Bcrypt(app) 
        
            34
            migrate = Migrate(app, db) 
        
            35
             
        
            36
             
        
            37
            def gitCommand(repo, data, *args): 
        
            38
                if not os.path.isdir(repo): 
        
            39
                    raise FileNotFoundError("Repo not found") 
        
            40
                env = os.environ.copy() 
        
            41
             
        
            42
                command = ["git", *args] 
        
            43
             
        
            44
                proc = subprocess.Popen(" ".join(command), cwd=repo, env=env, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) 
        
            45
                print(command) 
        
            46
             
        
            47
                if data: 
        
            48
                    proc.stdin.write(data) 
        
            49
             
        
            50
                out, err = proc.communicate() 
        
            51
                return out 
        
            52
             
        
            53
             
        
            54
            def onlyChars(string, chars): 
        
            55
                for i in string: 
        
            56
                    if i not in chars: 
        
            57
                        return False 
        
            58
                return True 
        
            59
             
        
            60
             
        
            61
            with app.app_context(): 
        
            62
                class RepoAccess(db.Model): 
        
            63
                    id = db.Column(db.Integer, primary_key=True) 
        
            64
                    userUsername = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 
        
            65
                    repoRoute = db.Column(db.String(98), db.ForeignKey("repo.route"), nullable=False) 
        
            66
                    accessLevel = db.Column(db.SmallInteger(), nullable=False)  # 0 read-only, 1 read-write, 2 admin 
        
            67
             
        
            68
                    user = db.relationship("User", back_populates="repoAccess") 
        
            69
                    repo = db.relationship("Repo", back_populates="repoAccess") 
        
            70
             
        
            71
                    __table_args__ = (db.UniqueConstraint("userUsername", "repoRoute", name="_user_repo_uc"),) 
        
            72
             
        
            73
                    def __init__(self, user, repo, level): 
        
            74
                        self.userUsername = user.username 
        
            75
                        self.repoRoute = repo.route 
        
            76
                        self.accessLevel = level 
        
            77
             
        
            78
             
        
            79
                class User(db.Model): 
        
            80
                    username = db.Column(db.String(32), unique=True, nullable=False, primary_key=True) 
        
            81
                    displayName = db.Column(db.Unicode(128), unique=False, nullable=True) 
        
            82
                    bio = db.Column(db.Unicode(512), unique=False, nullable=True) 
        
            83
                    passwordHashed = db.Column(db.String(60), nullable=False) 
        
            84
                    email = db.Column(db.String(254), nullable=True) 
        
            85
                    company = db.Column(db.Unicode(64), nullable=True) 
        
            86
                    companyURL = db.Column(db.String(256), nullable=True) 
        
            87
                    URL = db.Column(db.String(256), nullable=True) 
        
            88
                    showMail = db.Column(db.Boolean, default=False, nullable=False) 
        
            89
                    location = db.Column(db.Unicode(64), nullable=True) 
        
            90
                    creationDate = db.Column(db.DateTime, default=datetime.utcnow) 
        
            91
             
        
            92
                    repositories = db.relationship("Repo", back_populates="owner") 
        
            93
                    repoAccess = db.relationship("RepoAccess", back_populates="user") 
        
            94
             
        
            95
                    def __init__(self, username, password, email=None, displayName=None): 
        
            96
                        self.username = username 
        
            97
                        self.passwordHashed = bcrypt.generate_password_hash(password, config.HASHING_ROUNDS).decode("utf-8") 
        
            98
                        self.email = email 
        
            99
                        self.displayName = displayName 
        
            100
             
        
            101
                        # Create the user's directory 
        
            102
                        if not os.path.exists(os.path.join(config.REPOS_PATH, username)): 
        
            103
                            os.makedirs(os.path.join(config.REPOS_PATH, username)) 
        
            104
                        if not os.path.exists(os.path.join(config.USERDATA_PATH, username)): 
        
            105
                            os.makedirs(os.path.join(config.USERDATA_PATH, username)) 
        
            106
             
        
            107
                        avatarName = random.choice(os.listdir(config.DEFAULT_AVATARS_PATH)) 
        
            108
                        if os.path.join(config.DEFAULT_AVATARS_PATH, avatarName).endswith(".svg"): 
        
            109
                            cairosvg.svg2png(url=os.path.join(config.DEFAULT_AVATARS_PATH, avatarName), write_to="/tmp/roundabout-avatar.png") 
        
            110
                            avatar = Image.open("/tmp/roundabout-avatar.png") 
        
            111
                        else: 
        
            112
                            avatar = Image.open(os.path.join(config.DEFAULT_AVATARS_PATH, avatarName)) 
        
            113
                        avatar.thumbnail(config.AVATAR_SIZE) 
        
            114
                        avatar.save(os.path.join(config.USERDATA_PATH, username, "avatar.png")) 
        
            115
             
        
            116
             
        
            117
                class Repo(db.Model): 
        
            118
                    route = db.Column(db.String(98), unique=True, nullable=False, primary_key=True) 
        
            119
                    ownerName = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 
        
            120
                    name = db.Column(db.String(64), nullable=False) 
        
            121
                    owner = db.relationship("User", back_populates="repositories") 
        
            122
                    visibility = db.Column(db.SmallInteger(), nullable=False) 
        
            123
                    info = db.Column(db.Unicode(512), nullable=True) 
        
            124
                    URL = db.Column(db.String(256), nullable=True) 
        
            125
                    creationDate = db.Column(db.DateTime, default=datetime.utcnow) 
        
            126
             
        
            127
                    defaultBranch = db.Column(db.String(64), nullable=True, default="") 
        
            128
             
        
            129
                    commits = db.relationship("Commit", back_populates="repo") 
        
            130
                    repoAccess = db.relationship("RepoAccess", back_populates="repo") 
        
            131
             
        
            132
                    def __init__(self, owner, name, visibility): 
        
            133
                        self.route = f"/{owner.username}/{name}" 
        
            134
                        self.name = name 
        
            135
                        self.ownerName = owner.username 
        
            136
                        self.owner = owner 
        
            137
                        self.visibility = visibility 
        
            138
             
        
            139
                        # Add the owner as an admin 
        
            140
                        repoAccess = RepoAccess(owner, self, 2) 
        
            141
                        db.session.add(repoAccess) 
        
            142
             
        
            143
             
        
            144
                class Commit(db.Model): 
        
            145
                    identifier = db.Column(db.String(227), unique=True, nullable=False, primary_key=True) 
        
            146
                    sha = db.Column(db.String(128), nullable=False) 
        
            147
                    repoName = db.Column(db.String(97), db.ForeignKey("repo.route"), nullable=False) 
        
            148
                    ownerName = db.Column(db.String(32), db.ForeignKey("user.username"), nullable=False) 
        
            149
                    ownerIdentity = db.Column(db.String(321)) 
        
            150
                    receiveDate = db.Column(db.DateTime, default=datetime.utcnow) 
        
            151
                    authorDate = db.Column(db.DateTime) 
        
            152
                    message = db.Column(db.UnicodeText) 
        
            153
                    repo = db.relationship("Repo", back_populates="commits") 
        
            154
             
        
            155
                    def __init__(self, sha, owner, repo, date, message, ownerIdentity): 
        
            156
                        self.identifier = f"/{owner.username}/{repo.name}/{sha}" 
        
            157
                        self.sha = sha 
        
            158
                        self.repoName = repo.route 
        
            159
                        self.repo = repo 
        
            160
                        self.ownerName = owner.username 
        
            161
                        self.owner = owner 
        
            162
                        self.authorDate = datetime.fromtimestamp(int(date)) 
        
            163
                        self.message = message 
        
            164
                        self.ownerIdentity = ownerIdentity 
        
            165
             
        
            166
             
        
            167
            def getPermissionLevel(loggedIn, username, repository): 
        
            168
                user = User.query.filter_by(username=loggedIn).first() 
        
            169
                repo = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            170
             
        
            171
                if user and repo: 
        
            172
                    permission = RepoAccess.query.filter_by(user=user, repo=repo).first() 
        
            173
                    if permission: 
        
            174
                        return permission.accessLevel 
        
            175
             
        
            176
                return None 
        
            177
             
        
            178
             
        
            179
            def getVisibility(username, repository): 
        
            180
                repo = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            181
             
        
            182
                if repo: 
        
            183
                    return repo.visibility 
        
            184
             
        
            185
                return None 
        
            186
             
        
            187
             
        
            188
            import gitHTTP 
        
            189
             
        
            190
             
        
            191
            def humanSize(value, decimals=2, scale=1024, units=("B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB")): 
        
            192
                for unit in units: 
        
            193
                    if value < scale: 
        
            194
                        break 
        
            195
                    value /= scale 
        
            196
                if int(value) == value: 
        
            197
                    # do not return decimals, if the value is already round 
        
            198
                    return int(value), unit 
        
            199
                return round(value * 10**decimals) / 10**decimals, unit 
        
            200
             
        
            201
             
        
            202
            def guessMIME(path): 
        
            203
                if os.path.isdir(path): 
        
            204
                    mimetype = "inode/directory" 
        
            205
                elif magic.from_file(path, mime=True): 
        
            206
                    mimetype = magic.from_file(path, mime=True) 
        
            207
                else: 
        
            208
                    mimetype = "application/octet-stream" 
        
            209
                return mimetype 
        
            210
             
        
            211
             
        
            212
            def convertToHTML(path): 
        
            213
                with open(path, "r") as f: 
        
            214
                    contents = f.read() 
        
            215
                return contents 
        
            216
             
        
            217
             
        
            218
            @app.context_processor 
        
            219
            def default(): 
        
            220
                username = flask.session.get("username") 
        
            221
             
        
            222
                return {"loggedInUser": username} 
        
            223
             
        
            224
             
        
            225
            @app.route("/") 
        
            226
            def main(): 
        
            227
                return flask.render_template("home.html") 
        
            228
             
        
            229
             
        
            230
            @app.route("/about/") 
        
            231
            def about(): 
        
            232
                return flask.render_template("about.html", platform=platform) 
        
            233
             
        
            234
             
        
            235
            @app.route("/settings/", methods=["GET", "POST"]) 
        
            236
            def settings(): 
        
            237
                if flask.request.method == "GET": 
        
            238
                    if not flask.session.get("username"): 
        
            239
                        flask.abort(401) 
        
            240
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            241
             
        
            242
                    return flask.render_template("user-settings.html", user=user) 
        
            243
                else: 
        
            244
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            245
             
        
            246
                    user.displayName = flask.request.form["displayname"] 
        
            247
                    user.URL = flask.request.form["url"] 
        
            248
                    user.company = flask.request.form["company"] 
        
            249
                    user.companyURL = flask.request.form["companyurl"] 
        
            250
                    user.location = flask.request.form["location"] 
        
            251
                    user.showMail = flask.request.form.get("showmail", user.showMail) 
        
            252
             
        
            253
                    db.session.commit() 
        
            254
             
        
            255
                    flask.flash(Markup("<iconify-icon icon='mdi:check'></iconify-icon>Settings saved"), category="success") 
        
            256
                    return flask.redirect(f"/{flask.session.get('username')}", code=303) 
        
            257
             
        
            258
             
        
            259
            @app.route("/accounts/", methods=["GET", "POST"]) 
        
            260
            def login(): 
        
            261
                if flask.request.method == "GET": 
        
            262
                    return flask.render_template("login.html") 
        
            263
                else: 
        
            264
                    if "login" in flask.request.form: 
        
            265
                        username = flask.request.form["username"] 
        
            266
                        password = flask.request.form["password"] 
        
            267
             
        
            268
                        user = User.query.filter_by(username=username).first() 
        
            269
             
        
            270
                        if user and bcrypt.check_password_hash(user.passwordHashed, password): 
        
            271
                            flask.session["username"] = user.username 
        
            272
                            flask.flash(Markup(f"<iconify-icon icon='mdi:account'></iconify-icon>Successfully logged in as {username}"), category="success") 
        
            273
                            return flask.redirect("/", code=303) 
        
            274
                        elif not user: 
        
            275
                            flask.flash(Markup("<iconify-icon icon='mdi:account-question'></iconify-icon>User not found"), category="alert") 
        
            276
                            return flask.render_template("login.html") 
        
            277
                        else: 
        
            278
                            flask.flash(Markup("<iconify-icon icon='mdi:account-question'></iconify-icon>Invalid password"), category="error") 
        
            279
                            return flask.render_template("login.html") 
        
            280
                    if "signup" in flask.request.form: 
        
            281
                        username = flask.request.form["username"] 
        
            282
                        password = flask.request.form["password"] 
        
            283
                        password2 = flask.request.form["password2"] 
        
            284
                        email = flask.request.form.get("email") 
        
            285
                        email2 = flask.request.form.get("email2")        # repeat email is a honeypot 
        
            286
                        name = flask.request.form.get("name") 
        
            287
             
        
            288
                        if not onlyChars(username, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"): 
        
            289
                            flask.flash(Markup("<iconify-icon icon='mdi:account-error'></iconify-icon>Usernames may only contain Latin alphabet, numbers, '-' and '_'"), category="error") 
        
            290
                            return flask.render_template("login.html") 
        
            291
             
        
            292
                        if username in config.RESERVED_NAMES: 
        
            293
                            flask.flash(Markup(f"<iconify-icon icon='mdi:account-error'></iconify-icon>Sorry, {username} is a system path"), category="error") 
        
            294
                            return flask.render_template("login.html") 
        
            295
             
        
            296
                        userCheck = User.query.filter_by(username=username).first() 
        
            297
                        if userCheck: 
        
            298
                            flask.flash(Markup(f"<iconify-icon icon='mdi:account-error'></iconify-icon>The username {username} is taken"), category="error") 
        
            299
                            return flask.render_template("login.html") 
        
            300
             
        
            301
                        if password2 != password: 
        
            302
                            flask.flash(Markup("<iconify-icon icon='mdi:key-alert'></iconify-icon>Make sure the passwords match"), category="error") 
        
            303
                            return flask.render_template("login.html") 
        
            304
             
        
            305
                        user = User(username, password, email, name) 
        
            306
                        db.session.add(user) 
        
            307
                        db.session.commit() 
        
            308
                        flask.session["username"] = user.username 
        
            309
                        flask.flash(Markup(f"<iconify-icon icon='mdi:account'></iconify-icon>Successfully created and logged in as {username}"), category="success") 
        
            310
                        return flask.redirect("/", code=303) 
        
            311
             
        
            312
             
        
            313
            @app.route("/newrepo/", methods=["GET", "POST"]) 
        
            314
            def newRepo(): 
        
            315
                if flask.request.method == "GET": 
        
            316
                    return flask.render_template("new-repo.html") 
        
            317
                else: 
        
            318
                    name = flask.request.form["name"] 
        
            319
                    visibility = int(flask.request.form["visibility"]) 
        
            320
             
        
            321
                    if not onlyChars(name, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"): 
        
            322
                        flask.flash(Markup( 
        
            323
                            "<iconify-icon icon='mdi:error'></iconify-icon>Repository names may only contain Latin alphabet, numbers, '-' and '_'"), 
        
            324
                                    category="error") 
        
            325
                        return flask.render_template("new-repo.html") 
        
            326
             
        
            327
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            328
             
        
            329
                    repo = Repo(user, name, visibility) 
        
            330
                    db.session.add(repo) 
        
            331
                    db.session.commit() 
        
            332
             
        
            333
                    if not os.path.exists(os.path.join(config.REPOS_PATH, repo.route)): 
        
            334
                        subprocess.run(["git", "init", repo.name], cwd=os.path.join(config.REPOS_PATH, flask.session.get("username"))) 
        
            335
             
        
            336
                    flask.flash(Markup(f"<iconify-icon icon='mdi:folder'></iconify-icon>Successfully created repository {name}"), category="success") 
        
            337
                    return flask.redirect(repo.route, code=303) 
        
            338
             
        
            339
             
        
            340
            @app.route("/logout") 
        
            341
            def logout(): 
        
            342
                flask.session.clear() 
        
            343
                flask.flash(Markup(f"<iconify-icon icon='mdi:account'></iconify-icon>Successfully logged out"), category="info") 
        
            344
                return flask.redirect("/", code=303) 
        
            345
             
        
            346
             
        
            347
            @app.route("/<username>/") 
        
            348
            def userProfile(username): 
        
            349
                user = User.query.filter_by(username=username).first() 
        
            350
                repos = Repo.query.filter_by(ownerName=username, visibility=2) 
        
            351
                return flask.render_template("user-profile.html", user=user, repos=repos) 
        
            352
             
        
            353
             
        
            354
            @app.route("/<username>/<repository>/") 
        
            355
            def repositoryIndex(username, repository): 
        
            356
                return flask.redirect("./tree", code=302) 
        
            357
             
        
            358
             
        
            359
            @app.route("/<username>/<repository>/raw/<branch>/<path:subpath>") 
        
            360
            def repositoryRaw(username, repository, branch, subpath): 
        
            361
                if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("user"), username, repository) is not None): 
        
            362
                    flask.abort(403) 
        
            363
             
        
            364
                serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository)) 
        
            365
             
        
            366
                app.logger.info(f"Loading {serverRepoLocation}") 
        
            367
             
        
            368
                if not os.path.exists(serverRepoLocation): 
        
            369
                    app.logger.error(f"Cannot load {serverRepoLocation}") 
        
            370
                    return flask.render_template("not-found.html"), 404 
        
            371
             
        
            372
                repo = git.Repo(serverRepoLocation) 
        
            373
                try: 
        
            374
                    repo.git.checkout(branch) 
        
            375
                except git.exc.GitCommandError: 
        
            376
                    return flask.render_template("not-found.html"), 404 
        
            377
             
        
            378
                return flask.send_from_directory(config.REPOS_PATH, os.path.join(username, repository, subpath)) 
        
            379
             
        
            380
             
        
            381
            @app.route("/info/<username>/avatar") 
        
            382
            def userAvatar(username): 
        
            383
                serverUserdataLocation = os.path.join(config.USERDATA_PATH, username) 
        
            384
             
        
            385
                if not os.path.exists(serverUserdataLocation): 
        
            386
                    return flask.render_template("not-found.html"), 404 
        
            387
             
        
            388
                return flask.send_from_directory(serverUserdataLocation, "avatar.png") 
        
            389
             
        
            390
             
        
            391
            @app.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""}) 
        
            392
            @app.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""}) 
        
            393
            @app.route("/<username>/<repository>/tree/<branch>/<path:subpath>") 
        
            394
            def repositoryTree(username, repository, branch, subpath): 
        
            395
                if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username, repository) is not None): 
        
            396
                    flask.abort(403) 
        
            397
             
        
            398
                serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository)) 
        
            399
             
        
            400
                app.logger.info(f"Loading {serverRepoLocation}") 
        
            401
             
        
            402
                if not os.path.exists(serverRepoLocation): 
        
            403
                    app.logger.error(f"Cannot load {serverRepoLocation}") 
        
            404
                    return flask.render_template("not-found.html"), 404 
        
            405
             
        
            406
                repo = git.Repo(serverRepoLocation) 
        
            407
                repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            408
                if not repoData.defaultBranch: 
        
            409
                    if repo.heads: 
        
            410
                        repoData.defaultBranch = repo.heads[0].name 
        
            411
                    else: 
        
            412
                        return flask.render_template("empty.html", remote=f"http://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            413
                if not branch: 
        
            414
                    branch = repoData.defaultBranch 
        
            415
                    return flask.redirect(f"./{branch}", code=302) 
        
            416
                else: 
        
            417
                    try: 
        
            418
                        repo.git.checkout("-f", branch) 
        
            419
                    except git.exc.GitCommandError: 
        
            420
                        return flask.render_template("not-found.html"), 404 
        
            421
             
        
            422
                branches = repo.heads 
        
            423
             
        
            424
                if os.path.isdir(os.path.join(serverRepoLocation, subpath)): 
        
            425
                    files = [] 
        
            426
                    blobs = [] 
        
            427
             
        
            428
                    for entry in os.listdir(os.path.join(serverRepoLocation, subpath)): 
        
            429
                        if not os.path.basename(entry) == ".git": 
        
            430
                            files.append(os.path.join(subpath, entry)) 
        
            431
             
        
            432
                    infos = [] 
        
            433
             
        
            434
                    for file in files: 
        
            435
                        path = os.path.join(serverRepoLocation, file) 
        
            436
                        mimetype = guessMIME(path) 
        
            437
             
        
            438
                        text = gitCommand(serverRepoLocation, None, "log", "--format='%H\n'", file).decode() 
        
            439
             
        
            440
                        sha = text.split("\n")[0] 
        
            441
                        identifier = f"/{username}/{repository}/{sha}" 
        
            442
                        lastCommit = Commit.query.filter_by(identifier=identifier).first() 
        
            443
             
        
            444
                        info = { 
        
            445
                            "name": os.path.basename(file), 
        
            446
                            "serverPath": path, 
        
            447
                            "relativePath": file, 
        
            448
                            "link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file), 
        
            449
                            "size": humanSize(os.path.getsize(path)), 
        
            450
                            "mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}", 
        
            451
                            "commit": lastCommit, 
        
            452
                            "shaSize": 7, 
        
            453
                        } 
        
            454
             
        
            455
                        specialIcon = config.matchIcon(os.path.basename(file)) 
        
            456
                        if specialIcon: 
        
            457
                            info["icon"] = specialIcon 
        
            458
                        elif os.path.isdir(path): 
        
            459
                            info["icon"] = config.folderIcon 
        
            460
                        elif mimetypes.guess_type(path)[0] in config.fileIcons: 
        
            461
                            info["icon"] = config.fileIcons[mimetypes.guess_type(path)[0]] 
        
            462
                        else: 
        
            463
                            info["icon"] = config.unknownIcon 
        
            464
             
        
            465
                        if os.path.isdir(path): 
        
            466
                            infos.insert(0, info) 
        
            467
                        else: 
        
            468
                            infos.append(info) 
        
            469
             
        
            470
                    return flask.render_template( 
        
            471
                            "repo-tree.html", 
        
            472
                            username=username, 
        
            473
                            repository=repository, 
        
            474
                            files=infos, 
        
            475
                            subpath=os.path.join("/", subpath), 
        
            476
                            branches=branches, 
        
            477
                            current=branch 
        
            478
                    ) 
        
            479
                else: 
        
            480
                    path = os.path.join(serverRepoLocation, subpath) 
        
            481
             
        
            482
                    if not os.path.exists(path): 
        
            483
                        return flask.render_template("not-found.html"), 404 
        
            484
             
        
            485
                    mimetype = guessMIME(path) 
        
            486
                    mode = mimetype.split("/", 1)[0] 
        
            487
                    size = humanSize(os.path.getsize(path)) 
        
            488
             
        
            489
                    specialIcon = config.matchIcon(os.path.basename(path)) 
        
            490
                    if specialIcon: 
        
            491
                        icon = specialIcon 
        
            492
                    elif os.path.isdir(path): 
        
            493
                        icon = config.folderIcon 
        
            494
                    elif mimetypes.guess_type(path)[0] in config.fileIcons: 
        
            495
                        icon = config.fileIcons[mimetypes.guess_type(path)[0]] 
        
            496
                    else: 
        
            497
                        icon = config.unknownIcon 
        
            498
             
        
            499
                    contents = None 
        
            500
                    if mode == "text": 
        
            501
                        contents = convertToHTML(path) 
        
            502
             
        
            503
                    return flask.render_template( 
        
            504
                            "repo-file.html", 
        
            505
                            username=username, 
        
            506
                            repository=repository, 
        
            507
                            file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath), 
        
            508
                            branches=branches, 
        
            509
                            current=branch, 
        
            510
                            mode=mode, 
        
            511
                            mimetype=mimetype, 
        
            512
                            detailedtype=magic.from_file(path), 
        
            513
                            size=size, 
        
            514
                            icon=icon, 
        
            515
                            subpath=os.path.join("/", subpath), 
        
            516
                            basename=os.path.basename(path), 
        
            517
                            contents=contents 
        
            518
                    ) 
        
            519
             
        
            520
             
        
            521
            @app.route("/<username>/<repository>/forum/") 
        
            522
            def repositoryForum(username, repository): 
        
            523
                if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username, repository) is not None): 
        
            524
                    flask.abort(403) 
        
            525
             
        
            526
                return flask.render_template("repo-forum.html", username=username, repository=repository) 
        
            527
             
        
            528
             
        
            529
            @app.route("/<username>/<repository>/branches/") 
        
            530
            def repositoryBranches(username, repository): 
        
            531
                if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username, repository) is not None): 
        
            532
                    flask.abort(403) 
        
            533
             
        
            534
                return flask.render_template("repo-branches.html", username=username, repository=repository) 
        
            535
             
        
            536
             
        
            537
            @app.route("/<username>/<repository>/log/") 
        
            538
            def repositoryLog(username, repository): 
        
            539
                if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username, repository) is not None): 
        
            540
                    flask.abort(403) 
        
            541
             
        
            542
                return flask.render_template("repo-log.html", username=username, repository=repository) 
        
            543
             
        
            544
             
        
            545
            @app.route("/<username>/<repository>/settings/") 
        
            546
            def repositorySettings(username, repository): 
        
            547
                if getPermissionLevel(flask.session.get("username"), username, repository) != 2: 
        
            548
                    flask.abort(401) 
        
            549
             
        
            550
                return flask.render_template("repo-settings.html", username=username, repository=repository) 
        
            551
             
        
            552
             
        
            553
            @app.errorhandler(404) 
        
            554
            def e404(error): 
        
            555
                return flask.render_template("not-found.html"), 404 
        
            556
             
        
            557
             
        
            558
            @app.errorhandler(401) 
        
            559
            def e401(error): 
        
            560
                return flask.render_template("unauthorised.html"), 401 
        
            561
             
        
            562
             
        
            563
            @app.errorhandler(403) 
        
            564
            def e403(error): 
        
            565
                return flask.render_template("forbidden.html"), 403 
        
            566
             
        
            567
             
        
            568
            @app.errorhandler(418) 
        
            569
            def e418(error): 
        
            570
                return flask.render_template("teapot.html"), 418 
        
            571
             
        
            572