Python script, Unicode text, UTF-8 text executable
        
            1
            __version__ = "0.2.0" 
        
            2
            import os 
        
            4
            import shutil 
        
            5
            import random 
        
            6
            import subprocess 
        
            7
            import platform 
        
            8
            import git 
        
            9
            import mimetypes 
        
            10
            import magic 
        
            11
            import flask 
        
            12
            import cairosvg 
        
            13
            import celery 
        
            14
            import shlex 
        
            15
            from functools import wraps 
        
            16
            from datetime import datetime 
        
            17
            from enum import Enum 
        
            18
            from cairosvg import svg2png 
        
            19
            from flask_sqlalchemy import SQLAlchemy 
        
            20
            from flask_bcrypt import Bcrypt 
        
            21
            from markupsafe import escape, Markup 
        
            22
            from flask_migrate import Migrate 
        
            23
            from PIL import Image 
        
            24
            from flask_httpauth import HTTPBasicAuth 
        
            25
            import config 
        
            26
            from flask_babel import Babel, gettext, ngettext, force_locale 
        
            27
            _ = gettext 
        
            29
            n_ = gettext 
        
            30
            app = flask.Flask(__name__) 
        
            32
            app.config.from_mapping( 
        
            33
                    CELERY=dict( 
        
            34
                            broker_url=config.REDIS_URI, 
        
            35
                            result_backend=config.REDIS_URI, 
        
            36
                            task_ignore_result=True, 
        
            37
                    ), 
        
            38
            ) 
        
            39
            auth = HTTPBasicAuth() 
        
            41
            app.config["SQLALCHEMY_DATABASE_URI"] = config.DB_URI 
        
            43
            app.config["SECRET_KEY"] = config.DB_PASSWORD 
        
            44
            app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False 
        
            45
            app.config["BABEL_TRANSLATION_DIRECTORIES"] = "i18n" 
        
            46
            app.config["MAX_CONTENT_LENGTH"] = config.MAX_PAYLOAD_SIZE 
        
            47
            db = SQLAlchemy(app) 
        
            49
            bcrypt = Bcrypt(app) 
        
            50
            migrate = Migrate(app, db) 
        
            51
            from misc_utils import * 
        
            53
            import git_http 
        
            55
            import jinja_utils 
        
            56
            import celery_tasks 
        
            57
            from celery import Celery, Task 
        
            58
            import celery_integration 
        
            59
            import pathlib 
        
            60
            from models import * 
        
            62
            babel = Babel(app) 
        
            64
            def get_locale(): 
        
            67
                if flask.request.cookies.get("language"): 
        
            68
                    return flask.request.cookies.get("language") 
        
            69
                return flask.request.accept_languages.best_match(config.available_locales) 
        
            70
            babel.init_app(app, locale_selector=get_locale) 
        
            73
            with app.app_context(): 
        
            75
                locale_names = {} 
        
            76
                for language in config.available_locales: 
        
            77
                    with force_locale(language): 
        
            78
                        # NOTE: Translate this to the language's name in that language, for example in French you would use français 
        
            79
                        locale_names[language] = gettext("English") 
        
            80
            worker = celery_integration.init_celery_app(app) 
        
            82
            repositories = flask.Blueprint("repository", __name__, template_folder="templates/repository/") 
        
            84
            app.jinja_env.add_extension("jinja2.ext.do") 
        
            86
            app.jinja_env.add_extension("jinja2.ext.loopcontrols") 
        
            87
            app.jinja_env.add_extension("jinja2.ext.debug") 
        
            88
            @app.context_processor 
        
            91
            def default(): 
        
            92
                username = flask.session.get("username") 
        
            93
                user_object = User.query.filter_by(username=username).first() 
        
            95
                return { 
        
            97
                    "logged_in_user": username, 
        
            98
                    "user_object": user_object, 
        
            99
                    "Notification": Notification, 
        
            100
                    "unread": UserNotification.query.filter_by(user_username=username).filter( 
        
            101
                            UserNotification.attention_level > 0).count(), 
        
            102
                    "config": config, 
        
            103
                    "Markup": Markup, 
        
            104
                    "locale_names": locale_names, 
        
            105
                } 
        
            106
            @app.route("/") 
        
            109
            def main(): 
        
            110
                if flask.session.get("username"): 
        
            111
                    return flask.render_template("home.html") 
        
            112
                else: 
        
            113
                    return flask.render_template("no-home.html") 
        
            114
            @app.route("/userstyle") 
        
            117
            def userstyle(): 
        
            118
                if flask.session.get("username") and os.path.exists( 
        
            119
                        os.path.join(config.REPOS_PATH, flask.session.get("username"), ".config", 
        
            120
                                     "theme.css")): 
        
            121
                    return flask.send_from_directory( 
        
            122
                        os.path.join(config.REPOS_PATH, flask.session.get("username"), ".config"), 
        
            123
                        "theme.css") 
        
            124
                else: 
        
            125
                    return flask.Response("", mimetype="text/css") 
        
            126
            @app.route("/about/") 
        
            129
            def about(): 
        
            130
                return flask.render_template("about.html", platform=platform, version=__version__) 
        
            131
            @app.route("/language", methods=["POST"]) 
        
            134
            def set_locale(): 
        
            135
                response = flask.redirect(flask.request.referrer if flask.request.referrer else "/", 
        
            136
                                          code=303) 
        
            137
                if not flask.request.form.get("language"): 
        
            138
                    response.delete_cookie("language") 
        
            139
                else: 
        
            140
                    response.set_cookie("language", flask.request.form.get("language")) 
        
            141
                return response 
        
            143
            @app.route("/cookie-dismiss") 
        
            146
            def dismiss_banner(): 
        
            147
                response = flask.redirect(flask.request.referrer if flask.request.referrer else "/", 
        
            148
                                          code=303) 
        
            149
                response.set_cookie("cookie-banner", "1") 
        
            150
                return response 
        
            151
            @app.route("/help/") 
        
            154
            def help_index(): 
        
            155
                return flask.render_template("help.html", faqs=config.faqs) 
        
            156
            @app.route("/settings/", methods=["GET", "POST"]) 
        
            159
            def settings(): 
        
            160
                if not flask.session.get("username"): 
        
            161
                    flask.abort(401) 
        
            162
                if flask.request.method == "GET": 
        
            163
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            164
                    return flask.render_template("user-settings.html", user=user) 
        
            166
                else: 
        
            167
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            168
                    user.display_name = flask.request.form["displayname"] 
        
            170
                    user.URL = flask.request.form["url"] 
        
            171
                    user.company = flask.request.form["company"] 
        
            172
                    user.company_URL = flask.request.form["companyurl"] 
        
            173
                    user.email = flask.request.form.get("email") if flask.request.form.get( 
        
            174
                            "email") else None 
        
            175
                    user.location = flask.request.form["location"] 
        
            176
                    user.show_mail = True if flask.request.form.get("showmail") else False 
        
            177
                    user.bio = flask.request.form.get("bio") 
        
            178
                    db.session.commit() 
        
            180
                    flask.flash( 
        
            182
                            Markup("<iconify-icon icon='mdi:check'></iconify-icon>" + _("Settings saved")), 
        
            183
                            category="success") 
        
            184
                    return flask.redirect(f"/{flask.session.get('username')}", code=303) 
        
            185
            @app.route("/favourites/", methods=["GET", "POST"]) 
        
            188
            def favourites(): 
        
            189
                if not flask.session.get("username"): 
        
            190
                    flask.abort(401) 
        
            191
                if flask.request.method == "GET": 
        
            192
                    relationships = RepoFavourite.query.filter_by( 
        
            193
                            user_username=flask.session.get("username")) 
        
            194
                    return flask.render_template("favourites.html", favourites=relationships) 
        
            196
            @app.route("/favourites/<int:id>", methods=["POST"]) 
        
            199
            def favourite_edit(id): 
        
            200
                if not flask.session.get("username"): 
        
            201
                    flask.abort(401) 
        
            202
                favourite = db.session.get(RepoFavourite, id) 
        
            203
                if favourite.user_username != flask.session.get("username"): 
        
            204
                    flask.abort(403) 
        
            205
                data = flask.request.form 
        
            206
                print(data) 
        
            207
                favourite.notify_commit = js_to_bool(data.get("commit")) 
        
            208
                favourite.notify_forum = js_to_bool(data.get("forum")) 
        
            209
                favourite.notify_pr = js_to_bool(data.get("pull_request")) 
        
            210
                favourite.notify_admin = js_to_bool(data.get("administrative")) 
        
            211
                print(favourite.notify_commit, favourite.notify_forum, favourite.notify_pr, 
        
            212
                      favourite.notify_admin) 
        
            213
                db.session.commit() 
        
            214
                return flask.render_template_string( 
        
            215
                        """ 
        
            216
                    <tr hx-post="/favourites/{{ favourite.id }}" hx-trigger="change" hx-include="#commit-{{ favourite.id }}, #forum-{{ favourite.id }}, #pull_request-{{ favourite.id }}, #administrative-{{ favourite.id }}" hx-headers='{"Content-Type": "application/json"}' hx-swap="outerHTML"> 
        
            217
                        <td><a href="{{ favourite.repo.route }}">{{ favourite.repo.owner.username }}/{{ favourite.repo.name }}</a></td> 
        
            218
                        <td style="text-align: center;"><input type="checkbox" name="commit" id="commit-{{ favourite.id }}" value="true" {% if favourite.notify_commit %}checked{% endif %}></td> 
        
            219
                        <td style="text-align: center;"><input type="checkbox" name="forum" id="forum-{{ favourite.id }}" value="true" {% if favourite.notify_forum %}checked{% endif %}></td> 
        
            220
                        <td style="text-align: center;"><input type="checkbox" name="pull_request" id="pull_request-{{ favourite.id }}" value="true" {% if favourite.notify_pr %}checked{% endif %}></td> 
        
            221
                        <td style="text-align: center;"><input type="checkbox" name="administrative" id="administrative-{{ favourite.id }}" value="true" {% if favourite.notify_admin %}checked{% endif %}></td> 
        
            222
                    </tr> 
        
            223
                    """, 
        
            224
                        favourite=favourite 
        
            225
                ) 
        
            226
            @app.route("/notifications/", methods=["GET", "POST"]) 
        
            229
            def notifications(): 
        
            230
                if not flask.session.get("username"): 
        
            231
                    flask.abort(401) 
        
            232
                if flask.request.method == "GET": 
        
            233
                    return flask.render_template("notifications.html", 
        
            234
                                                 notifications=UserNotification.query.filter_by( 
        
            235
                                                         user_username=flask.session.get("username") 
        
            236
                                                    ).order_by(UserNotification.id.desc()), 
        
            237
                                                 db=db, Commit=Commit 
        
            238
                                                 ) 
        
            239
            @app.route("/notifications/<int:notification_id>/read", methods=["POST"]) 
        
            242
            def mark_read(notification_id): 
        
            243
                if not flask.session.get("username"): 
        
            244
                    flask.abort(401) 
        
            245
                notification = UserNotification.query.filter_by(id=notification_id).first() 
        
            246
                if notification.user_username != flask.session.get("username"): 
        
            247
                    flask.abort(403) 
        
            248
                notification.mark_read() 
        
            249
                db.session.commit() 
        
            250
                return flask.render_template_string( 
        
            251
                    "<button hx-post='/notifications/{{ notification.id }}/unread' hx-swap='outerHTML'>Mark as unread</button>", 
        
            252
                    notification=notification), 200 
        
            253
            @app.route("/notifications/<int:notification_id>/unread", methods=["POST"]) 
        
            256
            def mark_unread(notification_id): 
        
            257
                if not flask.session.get("username"): 
        
            258
                    flask.abort(401) 
        
            259
                notification = UserNotification.query.filter_by(id=notification_id).first() 
        
            260
                if notification.user_username != flask.session.get("username"): 
        
            261
                    flask.abort(403) 
        
            262
                notification.mark_unread() 
        
            263
                db.session.commit() 
        
            264
                return flask.render_template_string( 
        
            265
                    "<button hx-post='/notifications/{{ notification.id }}/read' hx-swap='outerHTML'>Mark as read</button>", 
        
            266
                    notification=notification), 200 
        
            267
            @app.route("/notifications/mark-all-read", methods=["POST"]) 
        
            270
            def mark_all_read(): 
        
            271
                if not flask.session.get("username"): 
        
            272
                    flask.abort(401) 
        
            273
                notifications = UserNotification.query.filter_by( 
        
            275
                        user_username=flask.session.get("username")) 
        
            276
                for notification in notifications: 
        
            277
                    notification.mark_read() 
        
            278
                db.session.commit() 
        
            279
                return flask.redirect("/notifications/", code=303) 
        
            280
            @app.route("/accounts/", methods=["GET", "POST"]) 
        
            283
            def login(): 
        
            284
                if flask.request.method == "GET": 
        
            285
                    return flask.render_template("login.html") 
        
            286
                else: 
        
            287
                    if "login" in flask.request.form: 
        
            288
                        username = flask.request.form["username"] 
        
            289
                        password = flask.request.form["password"] 
        
            290
                        user = User.query.filter_by(username=username).first() 
        
            292
                        if user and bcrypt.check_password_hash(user.password_hashed, password): 
        
            294
                            flask.session["username"] = user.username 
        
            295
                            flask.flash( 
        
            296
                                    Markup("<iconify-icon icon='mdi:account'></iconify-icon>" + _( 
        
            297
                                            "Successfully logged in as {username}").format( 
        
            298
                                        username=username)), 
        
            299
                                    category="success") 
        
            300
                            return flask.redirect("/", code=303) 
        
            301
                        elif not user: 
        
            302
                            flask.flash(Markup( 
        
            303
                                    "<iconify-icon icon='mdi:account-question'></iconify-icon>" + _( 
        
            304
                                            "User not found")), 
        
            305
                                    category="alert") 
        
            306
                            return flask.render_template("login.html") 
        
            307
                        else: 
        
            308
                            flask.flash(Markup( 
        
            309
                                    "<iconify-icon icon='mdi:account-question'></iconify-icon>" + _( 
        
            310
                                            "Invalid password")), 
        
            311
                                    category="error") 
        
            312
                            return flask.render_template("login.html") 
        
            313
                    if "signup" in flask.request.form: 
        
            314
                        username = flask.request.form["username"] 
        
            315
                        password = flask.request.form["password"] 
        
            316
                        password2 = flask.request.form["password2"] 
        
            317
                        email = flask.request.form.get("email") 
        
            318
                        email2 = flask.request.form.get("email2")  # repeat email is a honeypot 
        
            319
                        name = flask.request.form.get("name") 
        
            320
                        if not only_chars(username, 
        
            322
                                          "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"): 
        
            323
                            flask.flash(Markup( 
        
            324
                                    _("Usernames may only contain Latin alphabet, numbers, '-' and '_'")), 
        
            325
                                    category="error") 
        
            326
                            return flask.render_template("login.html") 
        
            327
                        if username in config.RESERVED_NAMES: 
        
            329
                            flask.flash( 
        
            330
                                    Markup( 
        
            331
                                            "<iconify-icon icon='mdi:account-error'></iconify-icon>" + _( 
        
            332
                                                    "Sorry, {username} is a system path").format( 
        
            333
                                                    username=username)), 
        
            334
                                    category="error") 
        
            335
                            return flask.render_template("login.html") 
        
            336
                        user_check = User.query.filter_by(username=username).first() 
        
            338
                        if user_check or email2:  # make the honeypot look like a normal error 
        
            339
                            flask.flash( 
        
            340
                                    Markup( 
        
            341
                                            "<iconify-icon icon='mdi:account-error'></iconify-icon>" + _( 
        
            342
                                                    "The username {username} is taken").format( 
        
            343
                                                    username=username)), 
        
            344
                                    category="error") 
        
            345
                            return flask.render_template("login.html") 
        
            346
                        if password2 != password: 
        
            348
                            flask.flash(Markup("<iconify-icon icon='mdi:key-alert'></iconify-icon>" + _( 
        
            349
                                    "Make sure the passwords match")), 
        
            350
                                        category="error") 
        
            351
                            return flask.render_template("login.html") 
        
            352
                        user = User(username, password, email, name) 
        
            354
                        db.session.add(user) 
        
            355
                        db.session.commit() 
        
            356
                        flask.session["username"] = user.username 
        
            357
                        flask.flash(Markup( 
        
            358
                                "<iconify-icon icon='mdi:account'></iconify-icon>" + _( 
        
            359
                                        "Successfully created and logged in as {username}").format( 
        
            360
                                        username=username)), 
        
            361
                                category="success") 
        
            362
                        notification = Notification({"type": "welcome"}) 
        
            364
                        db.session.add(notification) 
        
            365
                        db.session.commit() 
        
            366
                        return flask.redirect("/", code=303) 
        
            368
            @app.route("/newrepo/", methods=["GET", "POST"]) 
        
            371
            def new_repo(): 
        
            372
                if not flask.session.get("username"): 
        
            373
                    flask.abort(401) 
        
            374
                if flask.request.method == "GET": 
        
            375
                    return flask.render_template("new-repo.html") 
        
            376
                else: 
        
            377
                    name = flask.request.form["name"] 
        
            378
                    visibility = int(flask.request.form["visibility"]) 
        
            379
                    if not only_chars(name, 
        
            381
                                      "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"): 
        
            382
                        flask.flash(Markup( 
        
            383
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _( 
        
            384
                                        "Repository names may only contain Latin alphabet, numbers, '-' and '_'")), 
        
            385
                                category="error") 
        
            386
                        return flask.render_template("new-repo.html") 
        
            387
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            389
                    repo = Repo(user, name, visibility) 
        
            391
                    db.session.add(repo) 
        
            392
                    db.session.commit() 
        
            393
                    flask.flash(Markup(_("Successfully created repository {name}").format(name=name)), 
        
            395
                                category="success") 
        
            396
                    return flask.redirect(repo.route, code=303) 
        
            397
            @app.route("/logout") 
        
            400
            def logout(): 
        
            401
                flask.session.clear() 
        
            402
                flask.flash(Markup( 
        
            403
                        "<iconify-icon icon='mdi:account'></iconify-icon>" + _("Successfully logged out")), 
        
            404
                        category="info") 
        
            405
                return flask.redirect("/", code=303) 
        
            406
            @app.route("/<username>/", methods=["GET", "POST"]) 
        
            409
            def user_profile(username): 
        
            410
                old_relationship = UserFollow.query.filter_by( 
        
            411
                        follower_username=flask.session.get("username"), 
        
            412
                        followed_username=username).first() 
        
            413
                if flask.request.method == "GET": 
        
            414
                    user = User.query.filter_by(username=username).first() 
        
            415
                    match flask.request.args.get("action"): 
        
            416
                        case "repositories": 
        
            417
                            repos = Repo.query.filter_by(owner_name=username, visibility=2) 
        
            418
                            return flask.render_template("user-profile-repositories.html", user=user, 
        
            419
                                                         repos=repos, 
        
            420
                                                         relationship=old_relationship) 
        
            421
                        case "followers": 
        
            422
                            return flask.render_template("user-profile-followers.html", user=user, 
        
            423
                                                         relationship=old_relationship) 
        
            424
                        case "follows": 
        
            425
                            return flask.render_template("user-profile-follows.html", user=user, 
        
            426
                                                         relationship=old_relationship) 
        
            427
                        case _: 
        
            428
                            return flask.render_template("user-profile-overview.html", user=user, 
        
            429
                                                         relationship=old_relationship) 
        
            430
                elif flask.request.method == "POST": 
        
            432
                    match flask.request.args.get("action"): 
        
            433
                        case "follow": 
        
            434
                            if username == flask.session.get("username"): 
        
            435
                                flask.abort(403) 
        
            436
                            if old_relationship: 
        
            437
                                db.session.delete(old_relationship) 
        
            438
                            else: 
        
            439
                                relationship = UserFollow( 
        
            440
                                        flask.session.get("username"), 
        
            441
                                        username 
        
            442
                                ) 
        
            443
                                db.session.add(relationship) 
        
            444
                                db.session.commit() 
        
            445
                                user = db.session.get(User, username) 
        
            447
                                author = db.session.get(User, flask.session.get("username")) 
        
            448
                                notification = Notification({"type": "update", "version": "0.0.0"}) 
        
            449
                                db.session.add(notification) 
        
            450
                                db.session.commit() 
        
            451
                            db.session.commit() 
        
            453
                            return flask.redirect("?", code=303) 
        
            454
            @app.route("/<username>/<repository>/") 
        
            457
            def repository_index(username, repository): 
        
            458
                return flask.redirect("./tree", code=302) 
        
            459
            @app.route("/info/<username>/avatar") 
        
            462
            def user_avatar(username): 
        
            463
                server_userdata_location = os.path.join(config.USERDATA_PATH, username) 
        
            464
                if not os.path.exists(server_userdata_location): 
        
            465
                    return flask.render_template("not-found.html"), 404 
        
            466
                return flask.send_from_directory(server_userdata_location, "avatar.png") 
        
            468
            @app.route("/info/<username>/avatar", methods=["POST"]) 
        
            471
            def user_avatar_upload(username): 
        
            472
                server_userdata_location = os.path.join(config.USERDATA_PATH, username) 
        
            473
                if not os.path.exists(server_userdata_location): 
        
            475
                    flask.abort(404) 
        
            476
                if not flask.session.get("username") == username: 
        
            477
                    flask.abort(403) 
        
            478
                # Convert image to PNG 
        
            480
                try: 
        
            481
                    image = Image.open(flask.request.files["avatar"]) 
        
            482
                except: 
        
            483
                    flask.abort(400) 
        
            484
                image.save(os.path.join(server_userdata_location, "avatar.png")) 
        
            485
                return flask.redirect(f"/{username}", code=303) 
        
            487
            @app.route("/<username>/<repository>/raw/<branch>/<path:subpath>") 
        
            490
            def repository_raw(username, repository, branch, subpath): 
        
            491
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            492
                if not os.path.exists(server_repo_location): 
        
            493
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            494
                    flask.abort(404) 
        
            495
                if not (get_visibility(username, repository) or get_permission_level( 
        
            496
                        flask.session.get("username"), username, 
        
            497
                        repository) is not None): 
        
            498
                    flask.abort(403) 
        
            499
                app.logger.info(f"Loading {server_repo_location}") 
        
            501
                if not os.path.exists(server_repo_location): 
        
            503
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            504
                    return flask.render_template("not-found.html"), 404 
        
            505
                repo = git.Repo(server_repo_location) 
        
            507
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            508
                if not repo_data.default_branch: 
        
            509
                    if repo.heads: 
        
            510
                        repo_data.default_branch = repo.heads[0].name 
        
            511
                    else: 
        
            512
                        return flask.render_template("empty.html", 
        
            513
                                                     remote=f"http://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            514
                if not branch: 
        
            515
                    branch = repo_data.default_branch 
        
            516
                    return flask.redirect(f"./{branch}", code=302) 
        
            517
                if branch.startswith("tag:"): 
        
            519
                    ref = f"tags/{branch[4:]}" 
        
            520
                elif branch.startswith("~"): 
        
            521
                    ref = branch[1:] 
        
            522
                else: 
        
            523
                    ref = f"heads/{branch}" 
        
            524
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            526
                try: 
        
            528
                    repo.git.checkout("-f", ref) 
        
            529
                except git.exc.GitCommandError: 
        
            530
                    return flask.render_template("not-found.html"), 404 
        
            531
                return flask.send_from_directory(config.REPOS_PATH, 
        
            533
                                                 os.path.join(username, repository, subpath)) 
        
            534
            @repositories.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""}) 
        
            537
            @repositories.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""}) 
        
            538
            @repositories.route("/<username>/<repository>/tree/<branch>/<path:subpath>") 
        
            539
            def repository_tree(username, repository, branch, subpath): 
        
            540
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            541
                if not os.path.exists(server_repo_location): 
        
            542
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            543
                    flask.abort(404) 
        
            544
                if not (get_visibility(username, repository) or get_permission_level( 
        
            545
                        flask.session.get("username"), username, 
        
            546
                        repository) is not None): 
        
            547
                    flask.abort(403) 
        
            548
                app.logger.info(f"Loading {server_repo_location}") 
        
            550
                repo = git.Repo(server_repo_location) 
        
            552
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            553
                if not repo_data.default_branch: 
        
            554
                    if repo.heads: 
        
            555
                        repo_data.default_branch = repo.heads[0].name 
        
            556
                    else: 
        
            557
                        return flask.render_template("empty.html", 
        
            558
                                                     remote=f"{config.www_protocol}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            559
                if not branch: 
        
            560
                    branch = repo_data.default_branch 
        
            561
                    return flask.redirect(f"./{branch}", code=302) 
        
            562
                if branch.startswith("tag:"): 
        
            564
                    ref = f"tags/{branch[4:]}" 
        
            565
                elif branch.startswith("~"): 
        
            566
                    ref = branch[1:] 
        
            567
                else: 
        
            568
                    ref = f"heads/{branch}" 
        
            569
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            571
                try: 
        
            573
                    repo.git.checkout("-f", ref) 
        
            574
                except git.exc.GitCommandError: 
        
            575
                    return flask.render_template("not-found.html"), 404 
        
            576
                branches = repo.heads 
        
            578
                all_refs = [] 
        
            580
                for ref in repo.heads: 
        
            581
                    all_refs.append((ref, "head")) 
        
            582
                for ref in repo.tags: 
        
            583
                    all_refs.append((ref, "tag")) 
        
            584
                if os.path.isdir(os.path.join(server_repo_location, subpath)): 
        
            586
                    files = [] 
        
            587
                    blobs = [] 
        
            588
                    for entry in os.listdir(os.path.join(server_repo_location, subpath)): 
        
            590
                        if not os.path.basename(entry) == ".git": 
        
            591
                            files.append(os.path.join(subpath, entry)) 
        
            592
                    infos = [] 
        
            594
                    for file in files: 
        
            596
                        path = os.path.join(server_repo_location, file) 
        
            597
                        mimetype = guess_mime(path) 
        
            598
                        text = git_command(server_repo_location, None, "log", "--format='%H\n'", 
        
            600
                                           shlex.quote(file)).decode() 
        
            601
                        sha = text.split("\n")[0] 
        
            603
                        identifier = f"/{username}/{repository}/{sha}" 
        
            604
                        last_commit = db.session.get(Commit, identifier) 
        
            606
                        info = { 
        
            608
                            "name": os.path.basename(file), 
        
            609
                            "serverPath": path, 
        
            610
                            "relativePath": file, 
        
            611
                            "link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file), 
        
            612
                            "size": human_size(os.path.getsize(path)), 
        
            613
                            "mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}", 
        
            614
                            "commit": last_commit, 
        
            615
                            "shaSize": 7, 
        
            616
                        } 
        
            617
                        special_icon = config.match_icon(os.path.basename(file)) 
        
            619
                        if special_icon: 
        
            620
                            info["icon"] = special_icon 
        
            621
                        elif os.path.isdir(path): 
        
            622
                            info["icon"] = config.folder_icon 
        
            623
                        elif mimetypes.guess_type(path)[0] in config.file_icons: 
        
            624
                            info["icon"] = config.file_icons[mimetypes.guess_type(path)[0]] 
        
            625
                        else: 
        
            626
                            info["icon"] = config.unknown_icon 
        
            627
                        if os.path.isdir(path): 
        
            629
                            infos.insert(0, info) 
        
            630
                        else: 
        
            631
                            infos.append(info) 
        
            632
                    return flask.render_template( 
        
            634
                            "repo-tree.html", 
        
            635
                            username=username, 
        
            636
                            repository=repository, 
        
            637
                            files=infos, 
        
            638
                            subpath=os.path.join("/", subpath), 
        
            639
                            branches=all_refs, 
        
            640
                            current=branch, 
        
            641
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            642
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            643
                    ) 
        
            644
                else: 
        
            645
                    path = os.path.join(server_repo_location, subpath) 
        
            646
                    if not os.path.exists(path): 
        
            648
                        return flask.render_template("not-found.html"), 404 
        
            649
                    mimetype = guess_mime(path) 
        
            651
                    mode = mimetype.split("/", 1)[0] 
        
            652
                    size = human_size(os.path.getsize(path)) 
        
            653
                    special_icon = config.match_icon(os.path.basename(path)) 
        
            655
                    if special_icon: 
        
            656
                        icon = special_icon 
        
            657
                    elif os.path.isdir(path): 
        
            658
                        icon = config.folder_icon 
        
            659
                    elif mimetypes.guess_type(path)[0] in config.file_icons: 
        
            660
                        icon = config.file_icons[mimetypes.guess_type(path)[0]] 
        
            661
                    else: 
        
            662
                        icon = config.unknown_icon 
        
            663
                    contents = None 
        
            665
                    if mode == "text": 
        
            666
                        contents = convert_to_html(path) 
        
            667
                    return flask.render_template( 
        
            669
                            "repo-file.html", 
        
            670
                            username=username, 
        
            671
                            repository=repository, 
        
            672
                            file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath), 
        
            673
                            branches=all_refs, 
        
            674
                            current=branch, 
        
            675
                            mode=mode, 
        
            676
                            mimetype=mimetype, 
        
            677
                            detailedtype=magic.from_file(path), 
        
            678
                            size=size, 
        
            679
                            icon=icon, 
        
            680
                            subpath=os.path.join("/", subpath), 
        
            681
                            extension=pathlib.Path(path).suffix, 
        
            682
                            basename=os.path.basename(path), 
        
            683
                            contents=contents, 
        
            684
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            685
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            686
                    ) 
        
            687
            @repositories.route("/<username>/<repository>/commit/<sha>") 
        
            690
            def repository_commit(username, repository, sha): 
        
            691
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            692
                if not os.path.exists(server_repo_location): 
        
            693
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            694
                    flask.abort(404) 
        
            695
                if not (get_visibility(username, repository) or get_permission_level( 
        
            696
                        flask.session.get("username"), username, 
        
            697
                        repository) is not None): 
        
            698
                    flask.abort(403) 
        
            699
                app.logger.info(f"Loading {server_repo_location}") 
        
            701
                if not os.path.exists(server_repo_location): 
        
            703
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            704
                    return flask.render_template("not-found.html"), 404 
        
            705
                repo = git.Repo(server_repo_location) 
        
            707
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            708
                files = git_command(os.path.join(server_repo_location, ".git"), None, "diff-tree", "-r", 
        
            710
                                    "--name-only", "--no-commit-id", sha).decode().split("\n")[:-1] 
        
            711
                print(files) 
        
            713
                return flask.render_template( 
        
            715
                        "repo-commit.html", 
        
            716
                        username=username, 
        
            717
                        repository=repository, 
        
            718
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            719
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            720
                        diff={file: git_command(os.path.join(server_repo_location, ".git"), None, "diff", 
        
            721
                                                str(sha) + "^!", "--", file).decode().split("\n") for 
        
            722
                              file in files}, 
        
            723
                        data=db.session.get(Commit, f"/{username}/{repository}/{sha}"), 
        
            724
                ) 
        
            725
            @repositories.route("/<username>/<repository>/forum/") 
        
            728
            def repository_forum(username, repository): 
        
            729
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            730
                if not os.path.exists(server_repo_location): 
        
            731
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            732
                    flask.abort(404) 
        
            733
                if not (get_visibility(username, repository) or get_permission_level( 
        
            734
                        flask.session.get("username"), username, 
        
            735
                        repository) is not None): 
        
            736
                    flask.abort(403) 
        
            737
                app.logger.info(f"Loading {server_repo_location}") 
        
            739
                if not os.path.exists(server_repo_location): 
        
            741
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            742
                    return flask.render_template("not-found.html"), 404 
        
            743
                repo = git.Repo(server_repo_location) 
        
            745
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            746
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            747
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            748
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            749
                return flask.render_template( 
        
            751
                        "repo-forum.html", 
        
            752
                        username=username, 
        
            753
                        repository=repository, 
        
            754
                        repo_data=repo_data, 
        
            755
                        relationships=relationships, 
        
            756
                        repo=repo, 
        
            757
                        user_relationship=user_relationship, 
        
            758
                        Post=Post, 
        
            759
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            760
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            761
                        default_branch=repo_data.default_branch 
        
            762
                ) 
        
            763
            @repositories.route("/<username>/<repository>/forum/topic/<int:id>") 
        
            766
            def repository_forum_topic(username, repository, id): 
        
            767
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            768
                if not os.path.exists(server_repo_location): 
        
            769
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            770
                    flask.abort(404) 
        
            771
                if not (get_visibility(username, repository) or get_permission_level( 
        
            772
                        flask.session.get("username"), username, 
        
            773
                        repository) is not None): 
        
            774
                    flask.abort(403) 
        
            775
                app.logger.info(f"Loading {server_repo_location}") 
        
            777
                if not os.path.exists(server_repo_location): 
        
            779
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            780
                    return flask.render_template("not-found.html"), 404 
        
            781
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            783
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            784
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            785
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            786
                post = Post.query.filter_by(id=id).first() 
        
            788
                return flask.render_template( 
        
            790
                        "repo-topic.html", 
        
            791
                        username=username, 
        
            792
                        repository=repository, 
        
            793
                        repo_data=repo_data, 
        
            794
                        relationships=relationships, 
        
            795
                        user_relationship=user_relationship, 
        
            796
                        post=post, 
        
            797
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            798
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            799
                        default_branch=repo_data.default_branch 
        
            800
                ) 
        
            801
            @repositories.route("/<username>/<repository>/forum/new", methods=["POST", "GET"]) 
        
            804
            def repository_forum_new(username, repository): 
        
            805
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            806
                if not os.path.exists(server_repo_location): 
        
            807
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            808
                    flask.abort(404) 
        
            809
                if not (get_visibility(username, repository) or get_permission_level( 
        
            810
                        flask.session.get("username"), username, 
        
            811
                        repository) is not None): 
        
            812
                    flask.abort(403) 
        
            813
                app.logger.info(f"Loading {server_repo_location}") 
        
            815
                if not os.path.exists(server_repo_location): 
        
            817
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            818
                    return flask.render_template("not-found.html"), 404 
        
            819
                repo = git.Repo(server_repo_location) 
        
            821
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            822
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            823
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            824
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            825
                post = Post(user, repo_data, None, flask.request.form["subject"], 
        
            827
                            flask.request.form["message"]) 
        
            828
                db.session.add(post) 
        
            830
                db.session.commit() 
        
            831
                return flask.redirect( 
        
            833
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            834
                                      post_id=post.number), 
        
            835
                        code=303) 
        
            836
            @repositories.route("/<username>/<repository>/forum/<int:post_id>") 
        
            839
            def repository_forum_thread(username, repository, post_id): 
        
            840
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            841
                if not os.path.exists(server_repo_location): 
        
            842
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            843
                    flask.abort(404) 
        
            844
                if not (get_visibility(username, repository) or get_permission_level( 
        
            845
                        flask.session.get("username"), username, 
        
            846
                        repository) is not None): 
        
            847
                    flask.abort(403) 
        
            848
                app.logger.info(f"Loading {server_repo_location}") 
        
            850
                if not os.path.exists(server_repo_location): 
        
            852
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            853
                    return flask.render_template("not-found.html"), 404 
        
            854
                repo = git.Repo(server_repo_location) 
        
            856
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            857
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            858
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            859
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            860
                return flask.render_template( 
        
            862
                        "repo-forum-thread.html", 
        
            863
                        username=username, 
        
            864
                        repository=repository, 
        
            865
                        repo_data=repo_data, 
        
            866
                        relationships=relationships, 
        
            867
                        repo=repo, 
        
            868
                        Post=Post, 
        
            869
                        user_relationship=user_relationship, 
        
            870
                        post_id=post_id, 
        
            871
                        max_post_nesting=4, 
        
            872
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            873
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            874
                        parent=Post.query.filter_by(repo=repo_data, number=post_id).first(), 
        
            875
                        has_permission=not ((not get_permission_level(flask.session.get("username"), username, 
        
            876
                                                                 repository)) and db.session.get(Post, 
        
            877
                                                                                                 f"/{username}/{repository}/{post_id}").owner.username != flask.session.get("username")), 
        
            878
                ) 
        
            879
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/change-state", 
        
            882
                                methods=["POST"]) 
        
            883
            def repository_forum_change_state(username, repository, post_id): 
        
            884
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            885
                if not os.path.exists(server_repo_location): 
        
            886
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            887
                    flask.abort(404) 
        
            888
                if (not get_permission_level(flask.session.get("username"), username, repository)) and db.session.get(Post, f"/{username}/{repository}/{post_id}").owner.username != flask.session.get("username"): 
        
            889
                    flask.abort(403) 
        
            890
                app.logger.info(f"Loading {server_repo_location}") 
        
            892
                repo = git.Repo(server_repo_location) 
        
            894
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            895
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            896
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            897
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            898
                post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            900
                if not post: 
        
            902
                    flask.abort(404) 
        
            903
                post.state = int(flask.request.form["new-state"]) 
        
            905
                db.session.commit() 
        
            907
                return flask.redirect( 
        
            909
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            910
                                      post_id=post_id), 
        
            911
                        code=303) 
        
            912
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/reply", methods=["POST"]) 
        
            915
            def repository_forum_reply(username, repository, post_id): 
        
            916
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            917
                if not os.path.exists(server_repo_location): 
        
            918
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            919
                    flask.abort(404) 
        
            920
                if not (get_visibility(username, repository) or get_permission_level( 
        
            921
                        flask.session.get("username"), username, 
        
            922
                        repository) is not None): 
        
            923
                    flask.abort(403) 
        
            924
                app.logger.info(f"Loading {server_repo_location}") 
        
            926
                if not os.path.exists(server_repo_location): 
        
            928
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            929
                    return flask.render_template("not-found.html"), 404 
        
            930
                repo = git.Repo(server_repo_location) 
        
            932
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            933
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            934
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            935
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            936
                if not user: 
        
            937
                    flask.abort(401) 
        
            938
                parent = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            940
                post = Post(user, repo_data, parent, flask.request.form["subject"], 
        
            941
                            flask.request.form["message"]) 
        
            942
                db.session.add(post) 
        
            944
                post.update_date() 
        
            945
                db.session.commit() 
        
            946
                return flask.redirect( 
        
            948
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            949
                                      post_id=post_id), 
        
            950
                        code=303) 
        
            951
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/voteup", 
        
            954
                                defaults={"score": 1}) 
        
            955
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/votedown", 
        
            956
                                defaults={"score": -1}) 
        
            957
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/votes", defaults={"score": 0}) 
        
            958
            def repository_forum_vote(username, repository, post_id, score): 
        
            959
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            960
                if not os.path.exists(server_repo_location): 
        
            961
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            962
                    flask.abort(404) 
        
            963
                if not (get_visibility(username, repository) or get_permission_level( 
        
            964
                        flask.session.get("username"), username, 
        
            965
                        repository) is not None): 
        
            966
                    flask.abort(403) 
        
            967
                app.logger.info(f"Loading {server_repo_location}") 
        
            969
                if not os.path.exists(server_repo_location): 
        
            971
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            972
                    return flask.render_template("not-found.html"), 404 
        
            973
                repo = git.Repo(server_repo_location) 
        
            975
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            976
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            977
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            978
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            979
                if not user: 
        
            980
                    flask.abort(401) 
        
            981
                post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            983
                if score: 
        
            985
                    old_relationship = PostVote.query.filter_by(user_username=user.username, 
        
            986
                                                                post_identifier=post.identifier).first() 
        
            987
                    if old_relationship: 
        
            988
                        if score == old_relationship.vote_score: 
        
            989
                            db.session.delete(old_relationship) 
        
            990
                            post.vote_sum -= old_relationship.vote_score 
        
            991
                        else: 
        
            992
                            post.vote_sum -= old_relationship.vote_score 
        
            993
                            post.vote_sum += score 
        
            994
                            old_relationship.vote_score = score 
        
            995
                    else: 
        
            996
                        relationship = PostVote(user, post, score) 
        
            997
                        post.vote_sum += score 
        
            998
                        db.session.add(relationship) 
        
            999
                    db.session.commit() 
        
            1001
                user_vote = PostVote.query.filter_by(user_username=user.username, 
        
            1003
                                                     post_identifier=post.identifier).first() 
        
            1004
                response = flask.make_response( 
        
            1005
                        str(post.vote_sum) + " " + str(user_vote.vote_score if user_vote else 0)) 
        
            1006
                response.content_type = "text/plain" 
        
            1007
                return response 
        
            1009
            @repositories.route("/<username>/<repository>/favourite") 
        
            1012
            def repository_favourite(username, repository): 
        
            1013
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1014
                if not os.path.exists(server_repo_location): 
        
            1015
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1016
                    flask.abort(404) 
        
            1017
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1018
                        flask.session.get("username"), username, 
        
            1019
                        repository) is not None): 
        
            1020
                    flask.abort(403) 
        
            1021
                app.logger.info(f"Loading {server_repo_location}") 
        
            1023
                if not os.path.exists(server_repo_location): 
        
            1025
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1026
                    return flask.render_template("not-found.html"), 404 
        
            1027
                repo = git.Repo(server_repo_location) 
        
            1029
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1030
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1031
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            1032
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            1033
                if not user: 
        
            1034
                    flask.abort(401) 
        
            1035
                old_relationship = RepoFavourite.query.filter_by(user_username=user.username, 
        
            1037
                                                                 repo_route=repo_data.route).first() 
        
            1038
                if old_relationship: 
        
            1039
                    db.session.delete(old_relationship) 
        
            1040
                else: 
        
            1041
                    relationship = RepoFavourite(user, repo_data) 
        
            1042
                    db.session.add(relationship) 
        
            1043
                db.session.commit() 
        
            1045
                return flask.redirect(flask.url_for("favourites"), code=303) 
        
            1047
            @repositories.route("/<username>/<repository>/users/", methods=["GET", "POST"]) 
        
            1050
            def repository_users(username, repository): 
        
            1051
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1052
                if not os.path.exists(server_repo_location): 
        
            1053
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1054
                    flask.abort(404) 
        
            1055
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1056
                        flask.session.get("username"), username, 
        
            1057
                        repository) is not None): 
        
            1058
                    flask.abort(403) 
        
            1059
                app.logger.info(f"Loading {server_repo_location}") 
        
            1061
                if not os.path.exists(server_repo_location): 
        
            1063
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1064
                    return flask.render_template("not-found.html"), 404 
        
            1065
                repo = git.Repo(server_repo_location) 
        
            1067
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1068
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1069
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            1070
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            1071
                if flask.request.method == "GET": 
        
            1073
                    return flask.render_template( 
        
            1074
                            "repo-users.html", 
        
            1075
                            username=username, 
        
            1076
                            repository=repository, 
        
            1077
                            repo_data=repo_data, 
        
            1078
                            relationships=relationships, 
        
            1079
                            repo=repo, 
        
            1080
                            user_relationship=user_relationship, 
        
            1081
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1082
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1083
                    ) 
        
            1084
                else: 
        
            1085
                    if get_permission_level(flask.session.get("username"), username, repository) != 2: 
        
            1086
                        flask.abort(401) 
        
            1087
                    if flask.request.form.get("new-username"): 
        
            1089
                        # Create new relationship 
        
            1090
                        new_user = User.query.filter_by( 
        
            1091
                                username=flask.request.form.get("new-username")).first() 
        
            1092
                        relationship = RepoAccess(new_user, repo_data, flask.request.form.get("new-level")) 
        
            1093
                        db.session.add(relationship) 
        
            1094
                        db.session.commit() 
        
            1095
                    if flask.request.form.get("update-username"): 
        
            1096
                        # Create new relationship 
        
            1097
                        updated_user = User.query.filter_by( 
        
            1098
                                username=flask.request.form.get("update-username")).first() 
        
            1099
                        relationship = RepoAccess.query.filter_by(repo=repo_data, user=updated_user).first() 
        
            1100
                        if flask.request.form.get("update-level") == -1: 
        
            1101
                            relationship.delete() 
        
            1102
                        else: 
        
            1103
                            relationship.access_level = flask.request.form.get("update-level") 
        
            1104
                        db.session.commit() 
        
            1105
                    return flask.redirect( 
        
            1107
                            app.url_for(".repository_users", username=username, repository=repository)) 
        
            1108
            @repositories.route("/<username>/<repository>/branches/") 
        
            1111
            def repository_branches(username, repository): 
        
            1112
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1113
                if not os.path.exists(server_repo_location): 
        
            1114
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1115
                    flask.abort(404) 
        
            1116
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1117
                        flask.session.get("username"), username, 
        
            1118
                        repository) is not None): 
        
            1119
                    flask.abort(403) 
        
            1120
                app.logger.info(f"Loading {server_repo_location}") 
        
            1122
                if not os.path.exists(server_repo_location): 
        
            1124
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1125
                    return flask.render_template("not-found.html"), 404 
        
            1126
                repo = git.Repo(server_repo_location) 
        
            1128
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1129
                return flask.render_template( 
        
            1131
                        "repo-branches.html", 
        
            1132
                        username=username, 
        
            1133
                        repository=repository, 
        
            1134
                        repo_data=repo_data, 
        
            1135
                        repo=repo, 
        
            1136
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1137
                        is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1138
                ) 
        
            1139
            @repositories.route("/<username>/<repository>/log/", defaults={"branch": None}) 
        
            1142
            @repositories.route("/<username>/<repository>/log/<branch>/") 
        
            1143
            def repository_log(username, repository, branch): 
        
            1144
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1145
                if not os.path.exists(server_repo_location): 
        
            1146
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1147
                    flask.abort(404) 
        
            1148
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1149
                        flask.session.get("username"), username, 
        
            1150
                        repository) is not None): 
        
            1151
                    flask.abort(403) 
        
            1152
                app.logger.info(f"Loading {server_repo_location}") 
        
            1154
                if not os.path.exists(server_repo_location): 
        
            1156
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1157
                    return flask.render_template("not-found.html"), 404 
        
            1158
                repo = git.Repo(server_repo_location) 
        
            1160
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1161
                if not repo_data.default_branch: 
        
            1162
                    if repo.heads: 
        
            1163
                        repo_data.default_branch = repo.heads[0].name 
        
            1164
                    else: 
        
            1165
                        return flask.render_template("empty.html", 
        
            1166
                                                     remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            1167
                if not branch: 
        
            1168
                    branch = repo_data.default_branch 
        
            1169
                    return flask.redirect(f"./{branch}", code=302) 
        
            1170
                if branch.startswith("tag:"): 
        
            1172
                    ref = f"tags/{branch[4:]}" 
        
            1173
                elif branch.startswith("~"): 
        
            1174
                    ref = branch[1:] 
        
            1175
                else: 
        
            1176
                    ref = f"heads/{branch}" 
        
            1177
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            1179
                try: 
        
            1181
                    repo.git.checkout("-f", ref) 
        
            1182
                except git.exc.GitCommandError: 
        
            1183
                    return flask.render_template("not-found.html"), 404 
        
            1184
                branches = repo.heads 
        
            1186
                all_refs = [] 
        
            1188
                for ref in repo.heads: 
        
            1189
                    all_refs.append((ref, "head")) 
        
            1190
                for ref in repo.tags: 
        
            1191
                    all_refs.append((ref, "tag")) 
        
            1192
                commit_list = [f"/{username}/{repository}/{sha}" for sha in 
        
            1194
                               git_command(server_repo_location, None, "log", 
        
            1195
                                           "--format='%H'").decode().split("\n")] 
        
            1196
                commits = Commit.query.filter(Commit.identifier.in_(commit_list)) 
        
            1198
                return flask.render_template( 
        
            1200
                        "repo-log.html", 
        
            1201
                        username=username, 
        
            1202
                        repository=repository, 
        
            1203
                        branches=all_refs, 
        
            1204
                        current=branch, 
        
            1205
                        repo_data=repo_data, 
        
            1206
                        repo=repo, 
        
            1207
                        commits=commits, 
        
            1208
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1209
                        is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1210
                ) 
        
            1211
            @repositories.route("/<username>/<repository>/prs/", methods=["GET", "POST"]) 
        
            1214
            def repository_prs(username, repository): 
        
            1215
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1216
                if not os.path.exists(server_repo_location): 
        
            1217
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1218
                    flask.abort(404) 
        
            1219
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1220
                        flask.session.get("username"), username, 
        
            1221
                        repository) is not None): 
        
            1222
                    flask.abort(403) 
        
            1223
                app.logger.info(f"Loading {server_repo_location}") 
        
            1225
                if not os.path.exists(server_repo_location): 
        
            1227
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1228
                    return flask.render_template("not-found.html"), 404 
        
            1229
                if flask.request.method == "GET": 
        
            1231
                    repo = git.Repo(server_repo_location) 
        
            1232
                    repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1233
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1234
                    return flask.render_template( 
        
            1236
                            "repo-prs.html", 
        
            1237
                            username=username, 
        
            1238
                            repository=repository, 
        
            1239
                            repo_data=repo_data, 
        
            1240
                            repo=repo, 
        
            1241
                            PullRequest=PullRequest, 
        
            1242
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1243
                            is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            1244
                            default_branch=repo_data.default_branch, 
        
            1245
                            branches=repo.branches 
        
            1246
                    ) 
        
            1247
                else: 
        
            1249
                    repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1250
                    head = flask.request.form.get("head") 
        
            1251
                    head_route = flask.request.form.get("headroute") 
        
            1252
                    base = flask.request.form.get("base") 
        
            1253
                    if not head and base and head_route: 
        
            1255
                        return flask.redirect(".", 400) 
        
            1256
                    head_repo = git.Repo(os.path.join(config.REPOS_PATH, head_route.lstrip("/"))) 
        
            1258
                    base_repo = git.Repo(server_repo_location) 
        
            1259
                    print(head_repo) 
        
            1260
                    if head not in head_repo.branches or base not in base_repo.branches: 
        
            1262
                        flask.flash(Markup( 
        
            1263
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _("Bad branch name")), 
        
            1264
                                category="error") 
        
            1265
                        return flask.redirect(".", 303) 
        
            1266
                    head_data = db.session.get(Repo, head_route) 
        
            1268
                    if not head_data.visibility: 
        
            1269
                        flask.flash(Markup( 
        
            1270
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _( 
        
            1271
                                        "Head can't be restricted")), 
        
            1272
                                category="error") 
        
            1273
                        return flask.redirect(".", 303) 
        
            1274
                    pull_request = PullRequest(repo_data, head, head_data, base, 
        
            1276
                                               db.session.get(User, flask.session["username"])) 
        
            1277
                    db.session.add(pull_request) 
        
            1279
                    db.session.commit() 
        
            1280
                    return flask.redirect(".", 303) 
        
            1282
            @repositories.route("/<username>/<repository>/prs/merge", methods=["POST"]) 
        
            1285
            def repository_prs_merge(username, repository): 
        
            1286
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1287
                if not os.path.exists(server_repo_location): 
        
            1288
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1289
                    flask.abort(404) 
        
            1290
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1291
                        flask.session.get("username"), username, 
        
            1292
                        repository) is not None): 
        
            1293
                    flask.abort(403) 
        
            1294
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1296
                    flask.abort(401) 
        
            1297
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1299
                repo = git.Repo(server_repo_location) 
        
            1300
                id = flask.request.form.get("id") 
        
            1301
                pull_request = db.session.get(PullRequest, id) 
        
            1303
                if pull_request: 
        
            1305
                    result = celery_tasks.merge_heads.delay( 
        
            1306
                            pull_request.head_route, 
        
            1307
                            pull_request.head_branch, 
        
            1308
                            pull_request.base_route, 
        
            1309
                            pull_request.base_branch, 
        
            1310
                            simulate=True 
        
            1311
                    ) 
        
            1312
                    task_result = worker.AsyncResult(result.id) 
        
            1313
                    return flask.redirect(f"/task/{result.id}?pr-id={id}", 303) 
        
            1315
                    # db.session.delete(pull_request) 
        
            1316
                    # db.session.commit() 
        
            1317
                else: 
        
            1318
                    flask.abort(400) 
        
            1319
            @repositories.route("/<username>/<repository>/prs/<int:id>/merge") 
        
            1322
            def repository_prs_merge_stage_two(username, repository, id): 
        
            1323
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1324
                if not os.path.exists(server_repo_location): 
        
            1325
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1326
                    flask.abort(404) 
        
            1327
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1328
                        flask.session.get("username"), username, 
        
            1329
                        repository) is not None): 
        
            1330
                    flask.abort(403) 
        
            1331
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1333
                    flask.abort(401) 
        
            1334
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1336
                repo = git.Repo(server_repo_location) 
        
            1337
                pull_request = db.session.get(PullRequest, id) 
        
            1339
                if pull_request: 
        
            1341
                    result = celery_tasks.merge_heads.delay( 
        
            1342
                            pull_request.head_route, 
        
            1343
                            pull_request.head_branch, 
        
            1344
                            pull_request.base_route, 
        
            1345
                            pull_request.base_branch, 
        
            1346
                            simulate=False 
        
            1347
                    ) 
        
            1348
                    task_result = worker.AsyncResult(result.id) 
        
            1349
                    pull_request.state = 1 
        
            1351
                    db.session.commit() 
        
            1352
                    return flask.redirect(f"/task/{result.id}?pr-id={id}", 303) 
        
            1354
                    # db.session.delete(pull_request) 
        
            1355
                else: 
        
            1356
                    flask.abort(400) 
        
            1357
            @app.route("/task/<task_id>") 
        
            1360
            def task_monitor(task_id): 
        
            1361
                task_result = worker.AsyncResult(task_id) 
        
            1362
                print(task_result.status) 
        
            1363
                return flask.render_template("task-monitor.html", result=task_result) 
        
            1365
            @repositories.route("/<username>/<repository>/prs/delete", methods=["POST"]) 
        
            1368
            def repository_prs_delete(username, repository): 
        
            1369
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1370
                if not os.path.exists(server_repo_location): 
        
            1371
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1372
                    flask.abort(404) 
        
            1373
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1374
                        flask.session.get("username"), username, 
        
            1375
                        repository) is not None): 
        
            1376
                    flask.abort(403) 
        
            1377
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1379
                    flask.abort(401) 
        
            1380
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1382
                repo = git.Repo(server_repo_location) 
        
            1383
                id = flask.request.form.get("id") 
        
            1384
                pull_request = db.session.get(PullRequest, id) 
        
            1386
                if pull_request: 
        
            1388
                    pull_request.state = 2 
        
            1389
                    db.session.commit() 
        
            1390
                return flask.redirect(".", 303) 
        
            1392
            @repositories.route("/<username>/<repository>/settings/") 
        
            1395
            def repository_settings(username, repository): 
        
            1396
                if get_permission_level(flask.session.get("username"), username, repository) != 2: 
        
            1397
                    flask.abort(401) 
        
            1398
                return flask.render_template("repo-settings.html", username=username, repository=repository) 
        
            1400
            @app.errorhandler(404) 
        
            1403
            def e404(error): 
        
            1404
                return flask.render_template("not-found.html"), 404 
        
            1405
            @app.errorhandler(401) 
        
            1408
            def e401(error): 
        
            1409
                return flask.render_template("unauthorised.html"), 401 
        
            1410
            @app.errorhandler(403) 
        
            1413
            def e403(error): 
        
            1414
                return flask.render_template("forbidden.html"), 403 
        
            1415
            @app.errorhandler(418) 
        
            1418
            def e418(error): 
        
            1419
                return flask.render_template("teapot.html"), 418 
        
            1420
            @app.errorhandler(405) 
        
            1423
            def e405(error): 
        
            1424
                return flask.render_template("method-not-allowed.html"), 405 
        
            1425
            if __name__ == "__main__": 
        
            1428
                app.run(debug=True, port=8080, host="0.0.0.0") 
        
            1429
            app.register_blueprint(repositories) 
        
            1431