Python script, Unicode text, UTF-8 text executable
        
            1
            __version__ = "0.1.2" 
        
            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 models import * 
        
            53
            from misc_utils import * 
        
            54
            import git_http 
        
            56
            import jinja_utils 
        
            57
            import celery_tasks 
        
            58
            from celery import Celery, Task 
        
            59
            import celery_integration 
        
            60
            import pathlib 
        
            61
            babel = Babel(app) 
        
            63
            def get_locale(): 
        
            66
                if flask.request.cookies.get("language"): 
        
            67
                    return flask.request.cookies.get("language") 
        
            68
                return flask.request.accept_languages.best_match(config.available_locales) 
        
            69
            babel.init_app(app, locale_selector=get_locale) 
        
            72
            with app.app_context(): 
        
            74
                locale_names = {} 
        
            75
                for language in config.available_locales: 
        
            76
                    with force_locale(language): 
        
            77
                        # NOTE: Translate this to the language's name in that language, for example in French you would use français 
        
            78
                        locale_names[language] = gettext("English") 
        
            79
            worker = celery_integration.init_celery_app(app) 
        
            81
            repositories = flask.Blueprint("repository", __name__, template_folder="templates/repository/") 
        
            83
            app.jinja_env.add_extension("jinja2.ext.do") 
        
            85
            app.jinja_env.add_extension("jinja2.ext.loopcontrols") 
        
            86
            app.jinja_env.add_extension("jinja2.ext.debug") 
        
            87
            @app.context_processor 
        
            90
            def default(): 
        
            91
                username = flask.session.get("username") 
        
            92
                user_object = User.query.filter_by(username=username).first() 
        
            94
                return { 
        
            96
                    "logged_in_user": username, 
        
            97
                    "user_object": user_object, 
        
            98
                    "Notification": Notification, 
        
            99
                    "unread": UserNotification.query.filter_by(user_username=username).filter( 
        
            100
                            UserNotification.attention_level > 0).count(), 
        
            101
                    "config": config, 
        
            102
                    "Markup": Markup, 
        
            103
                    "locale_names": locale_names, 
        
            104
                } 
        
            105
            @app.route("/") 
        
            108
            def main(): 
        
            109
                if flask.session.get("username"): 
        
            110
                    return flask.render_template("home.html") 
        
            111
                else: 
        
            112
                    return flask.render_template("no-home.html") 
        
            113
            @app.route("/userstyle") 
        
            116
            def userstyle(): 
        
            117
                if flask.session.get("username") and os.path.exists( 
        
            118
                        os.path.join(config.REPOS_PATH, flask.session.get("username"), ".config", 
        
            119
                                     "theme.css")): 
        
            120
                    return flask.send_from_directory( 
        
            121
                        os.path.join(config.REPOS_PATH, flask.session.get("username"), ".config"), 
        
            122
                        "theme.css") 
        
            123
                else: 
        
            124
                    return flask.Response("", mimetype="text/css") 
        
            125
            @app.route("/about/") 
        
            128
            def about(): 
        
            129
                return flask.render_template("about.html", platform=platform, version=__version__) 
        
            130
            @app.route("/language", methods=["POST"]) 
        
            133
            def set_locale(): 
        
            134
                response = flask.redirect(flask.request.referrer if flask.request.referrer else "/", 
        
            135
                                          code=303) 
        
            136
                if not flask.request.form.get("language"): 
        
            137
                    response.delete_cookie("language") 
        
            138
                else: 
        
            139
                    response.set_cookie("language", flask.request.form.get("language")) 
        
            140
                return response 
        
            142
            @app.route("/cookie-dismiss") 
        
            145
            def dismiss_banner(): 
        
            146
                response = flask.redirect(flask.request.referrer if flask.request.referrer else "/", 
        
            147
                                          code=303) 
        
            148
                response.set_cookie("cookie-banner", "1") 
        
            149
                return response 
        
            150
            @app.route("/help/") 
        
            153
            def help_index(): 
        
            154
                return flask.render_template("help.html", faqs=config.faqs) 
        
            155
            @app.route("/settings/", methods=["GET", "POST"]) 
        
            158
            def settings(): 
        
            159
                if not flask.session.get("username"): 
        
            160
                    flask.abort(401) 
        
            161
                if flask.request.method == "GET": 
        
            162
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            163
                    return flask.render_template("user-settings.html", user=user) 
        
            165
                else: 
        
            166
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            167
                    user.display_name = flask.request.form["displayname"] 
        
            169
                    user.URL = flask.request.form["url"] 
        
            170
                    user.company = flask.request.form["company"] 
        
            171
                    user.company_URL = flask.request.form["companyurl"] 
        
            172
                    user.email = flask.request.form.get("email") if flask.request.form.get( 
        
            173
                            "email") else None 
        
            174
                    user.location = flask.request.form["location"] 
        
            175
                    user.show_mail = True if flask.request.form.get("showmail") else False 
        
            176
                    user.bio = flask.request.form.get("bio") 
        
            177
                    db.session.commit() 
        
            179
                    flask.flash( 
        
            181
                            Markup("<iconify-icon icon='mdi:check'></iconify-icon>" + _("Settings saved")), 
        
            182
                            category="success") 
        
            183
                    return flask.redirect(f"/{flask.session.get('username')}", code=303) 
        
            184
            @app.route("/favourites/", methods=["GET", "POST"]) 
        
            187
            def favourites(): 
        
            188
                if not flask.session.get("username"): 
        
            189
                    flask.abort(401) 
        
            190
                if flask.request.method == "GET": 
        
            191
                    relationships = RepoFavourite.query.filter_by( 
        
            192
                            user_username=flask.session.get("username")) 
        
            193
                    return flask.render_template("favourites.html", favourites=relationships) 
        
            195
            @app.route("/favourites/<int:id>", methods=["POST"]) 
        
            198
            def favourite_edit(id): 
        
            199
                if not flask.session.get("username"): 
        
            200
                    flask.abort(401) 
        
            201
                favourite = db.session.get(RepoFavourite, id) 
        
            202
                if favourite.user_username != flask.session.get("username"): 
        
            203
                    flask.abort(403) 
        
            204
                data = flask.request.form 
        
            205
                print(data) 
        
            206
                favourite.notify_commit = js_to_bool(data.get("commit")) 
        
            207
                favourite.notify_forum = js_to_bool(data.get("forum")) 
        
            208
                favourite.notify_pr = js_to_bool(data.get("pull_request")) 
        
            209
                favourite.notify_admin = js_to_bool(data.get("administrative")) 
        
            210
                print(favourite.notify_commit, favourite.notify_forum, favourite.notify_pr, 
        
            211
                      favourite.notify_admin) 
        
            212
                db.session.commit() 
        
            213
                return flask.render_template_string( 
        
            214
                        """ 
        
            215
                    <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"> 
        
            216
                        <td><a href="{{ favourite.repo.route }}">{{ favourite.repo.owner.username }}/{{ favourite.repo.name }}</a></td> 
        
            217
                        <td style="text-align: center;"><input type="checkbox" name="commit" id="commit-{{ favourite.id }}" value="true" {% if favourite.notify_commit %}checked{% endif %}></td> 
        
            218
                        <td style="text-align: center;"><input type="checkbox" name="forum" id="forum-{{ favourite.id }}" value="true" {% if favourite.notify_forum %}checked{% endif %}></td> 
        
            219
                        <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> 
        
            220
                        <td style="text-align: center;"><input type="checkbox" name="administrative" id="administrative-{{ favourite.id }}" value="true" {% if favourite.notify_admin %}checked{% endif %}></td> 
        
            221
                    </tr> 
        
            222
                    """, 
        
            223
                        favourite=favourite 
        
            224
                ) 
        
            225
            @app.route("/notifications/", methods=["GET", "POST"]) 
        
            228
            def notifications(): 
        
            229
                if not flask.session.get("username"): 
        
            230
                    flask.abort(401) 
        
            231
                if flask.request.method == "GET": 
        
            232
                    return flask.render_template("notifications.html", 
        
            233
                                                 notifications=UserNotification.query.filter_by( 
        
            234
                                                         user_username=flask.session.get("username") 
        
            235
                                                    ).order_by(UserNotification.id.desc()) 
        
            236
                                                 ) 
        
            237
            @app.route("/notifications/<int:notification_id>/read", methods=["POST"]) 
        
            240
            def mark_read(notification_id): 
        
            241
                if not flask.session.get("username"): 
        
            242
                    flask.abort(401) 
        
            243
                notification = UserNotification.query.filter_by(id=notification_id).first() 
        
            244
                if notification.user_username != flask.session.get("username"): 
        
            245
                    flask.abort(403) 
        
            246
                notification.mark_read() 
        
            247
                db.session.commit() 
        
            248
                return flask.render_template_string( 
        
            249
                    "<button hx-post='/notifications/{{ notification.id }}/unread' hx-swap='outerHTML'>Mark as unread</button>", 
        
            250
                    notification=notification), 200 
        
            251
            @app.route("/notifications/<int:notification_id>/unread", methods=["POST"]) 
        
            254
            def mark_unread(notification_id): 
        
            255
                if not flask.session.get("username"): 
        
            256
                    flask.abort(401) 
        
            257
                notification = UserNotification.query.filter_by(id=notification_id).first() 
        
            258
                if notification.user_username != flask.session.get("username"): 
        
            259
                    flask.abort(403) 
        
            260
                notification.mark_unread() 
        
            261
                db.session.commit() 
        
            262
                return flask.render_template_string( 
        
            263
                    "<button hx-post='/notifications/{{ notification.id }}/read' hx-swap='outerHTML'>Mark as read</button>", 
        
            264
                    notification=notification), 200 
        
            265
            @app.route("/notifications/mark-all-read", methods=["POST"]) 
        
            268
            def mark_all_read(): 
        
            269
                if not flask.session.get("username"): 
        
            270
                    flask.abort(401) 
        
            271
                notifications = UserNotification.query.filter_by( 
        
            273
                        user_username=flask.session.get("username")) 
        
            274
                for notification in notifications: 
        
            275
                    notification.mark_read() 
        
            276
                db.session.commit() 
        
            277
                return flask.redirect("/notifications/", code=303) 
        
            278
            @app.route("/accounts/", methods=["GET", "POST"]) 
        
            281
            def login(): 
        
            282
                if flask.request.method == "GET": 
        
            283
                    return flask.render_template("login.html") 
        
            284
                else: 
        
            285
                    if "login" in flask.request.form: 
        
            286
                        username = flask.request.form["username"] 
        
            287
                        password = flask.request.form["password"] 
        
            288
                        user = User.query.filter_by(username=username).first() 
        
            290
                        if user and bcrypt.check_password_hash(user.password_hashed, password): 
        
            292
                            flask.session["username"] = user.username 
        
            293
                            flask.flash( 
        
            294
                                    Markup("<iconify-icon icon='mdi:account'></iconify-icon>" + _( 
        
            295
                                            "Successfully logged in as {username}").format( 
        
            296
                                        username=username)), 
        
            297
                                    category="success") 
        
            298
                            return flask.redirect("/", code=303) 
        
            299
                        elif not user: 
        
            300
                            flask.flash(Markup( 
        
            301
                                    "<iconify-icon icon='mdi:account-question'></iconify-icon>" + _( 
        
            302
                                            "User not found")), 
        
            303
                                    category="alert") 
        
            304
                            return flask.render_template("login.html") 
        
            305
                        else: 
        
            306
                            flask.flash(Markup( 
        
            307
                                    "<iconify-icon icon='mdi:account-question'></iconify-icon>" + _( 
        
            308
                                            "Invalid password")), 
        
            309
                                    category="error") 
        
            310
                            return flask.render_template("login.html") 
        
            311
                    if "signup" in flask.request.form: 
        
            312
                        username = flask.request.form["username"] 
        
            313
                        password = flask.request.form["password"] 
        
            314
                        password2 = flask.request.form["password2"] 
        
            315
                        email = flask.request.form.get("email") 
        
            316
                        email2 = flask.request.form.get("email2")  # repeat email is a honeypot 
        
            317
                        name = flask.request.form.get("name") 
        
            318
                        if not only_chars(username, 
        
            320
                                          "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"): 
        
            321
                            flask.flash(Markup( 
        
            322
                                    _("Usernames may only contain Latin alphabet, numbers, '-' and '_'")), 
        
            323
                                    category="error") 
        
            324
                            return flask.render_template("login.html") 
        
            325
                        if username in config.RESERVED_NAMES: 
        
            327
                            flask.flash( 
        
            328
                                    Markup( 
        
            329
                                            "<iconify-icon icon='mdi:account-error'></iconify-icon>" + _( 
        
            330
                                                    "Sorry, {username} is a system path").format( 
        
            331
                                                    username=username)), 
        
            332
                                    category="error") 
        
            333
                            return flask.render_template("login.html") 
        
            334
                        user_check = User.query.filter_by(username=username).first() 
        
            336
                        if user_check or email2:  # make the honeypot look like a normal error 
        
            337
                            flask.flash( 
        
            338
                                    Markup( 
        
            339
                                            "<iconify-icon icon='mdi:account-error'></iconify-icon>" + _( 
        
            340
                                                    "The username {username} is taken").format( 
        
            341
                                                    username=username)), 
        
            342
                                    category="error") 
        
            343
                            return flask.render_template("login.html") 
        
            344
                        if password2 != password: 
        
            346
                            flask.flash(Markup("<iconify-icon icon='mdi:key-alert'></iconify-icon>" + _( 
        
            347
                                    "Make sure the passwords match")), 
        
            348
                                        category="error") 
        
            349
                            return flask.render_template("login.html") 
        
            350
                        user = User(username, password, email, name) 
        
            352
                        db.session.add(user) 
        
            353
                        db.session.commit() 
        
            354
                        flask.session["username"] = user.username 
        
            355
                        flask.flash(Markup( 
        
            356
                                "<iconify-icon icon='mdi:account'></iconify-icon>" + _( 
        
            357
                                        "Successfully created and logged in as {username}").format( 
        
            358
                                        username=username)), 
        
            359
                                category="success") 
        
            360
                        notification = Notification({"type": "welcome"}) 
        
            362
                        db.session.add(notification) 
        
            363
                        db.session.commit() 
        
            364
                        result = celery_tasks.send_notification.delay(notification.id, [username], 1) 
        
            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
                                result = celery_tasks.send_notification.delay(notification.id, [username], 
        
            453
                                                                              1) 
        
            454
                            db.session.commit() 
        
            456
                            return flask.redirect("?", code=303) 
        
            457
            @app.route("/<username>/<repository>/") 
        
            460
            def repository_index(username, repository): 
        
            461
                return flask.redirect("./tree", code=302) 
        
            462
            @app.route("/info/<username>/avatar") 
        
            465
            def user_avatar(username): 
        
            466
                serverUserdataLocation = os.path.join(config.USERDATA_PATH, username) 
        
            467
                if not os.path.exists(serverUserdataLocation): 
        
            469
                    return flask.render_template("not-found.html"), 404 
        
            470
                return flask.send_from_directory(serverUserdataLocation, "avatar.png") 
        
            472
            @app.route("/<username>/<repository>/raw/<branch>/<path:subpath>") 
        
            475
            def repository_raw(username, repository, branch, subpath): 
        
            476
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            477
                if not os.path.exists(server_repo_location): 
        
            478
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            479
                    flask.abort(404) 
        
            480
                if not (get_visibility(username, repository) or get_permission_level( 
        
            481
                        flask.session.get("username"), username, 
        
            482
                        repository) is not None): 
        
            483
                    flask.abort(403) 
        
            484
                app.logger.info(f"Loading {server_repo_location}") 
        
            486
                if not os.path.exists(server_repo_location): 
        
            488
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            489
                    return flask.render_template("not-found.html"), 404 
        
            490
                repo = git.Repo(server_repo_location) 
        
            492
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            493
                if not repo_data.default_branch: 
        
            494
                    if repo.heads: 
        
            495
                        repo_data.default_branch = repo.heads[0].name 
        
            496
                    else: 
        
            497
                        return flask.render_template("empty.html", 
        
            498
                                                     remote=f"http://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            499
                if not branch: 
        
            500
                    branch = repo_data.default_branch 
        
            501
                    return flask.redirect(f"./{branch}", code=302) 
        
            502
                if branch.startswith("tag:"): 
        
            504
                    ref = f"tags/{branch[4:]}" 
        
            505
                elif branch.startswith("~"): 
        
            506
                    ref = branch[1:] 
        
            507
                else: 
        
            508
                    ref = f"heads/{branch}" 
        
            509
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            511
                try: 
        
            513
                    repo.git.checkout("-f", ref) 
        
            514
                except git.exc.GitCommandError: 
        
            515
                    return flask.render_template("not-found.html"), 404 
        
            516
                return flask.send_from_directory(config.REPOS_PATH, 
        
            518
                                                 os.path.join(username, repository, subpath)) 
        
            519
            @repositories.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""}) 
        
            522
            @repositories.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""}) 
        
            523
            @repositories.route("/<username>/<repository>/tree/<branch>/<path:subpath>") 
        
            524
            def repository_tree(username, repository, branch, subpath): 
        
            525
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            526
                if not os.path.exists(server_repo_location): 
        
            527
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            528
                    flask.abort(404) 
        
            529
                if not (get_visibility(username, repository) or get_permission_level( 
        
            530
                        flask.session.get("username"), username, 
        
            531
                        repository) is not None): 
        
            532
                    flask.abort(403) 
        
            533
                app.logger.info(f"Loading {server_repo_location}") 
        
            535
                repo = git.Repo(server_repo_location) 
        
            537
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            538
                if not repo_data.default_branch: 
        
            539
                    if repo.heads: 
        
            540
                        repo_data.default_branch = repo.heads[0].name 
        
            541
                    else: 
        
            542
                        return flask.render_template("empty.html", 
        
            543
                                                     remote=f"{config.www_protocol}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            544
                if not branch: 
        
            545
                    branch = repo_data.default_branch 
        
            546
                    return flask.redirect(f"./{branch}", code=302) 
        
            547
                if branch.startswith("tag:"): 
        
            549
                    ref = f"tags/{branch[4:]}" 
        
            550
                elif branch.startswith("~"): 
        
            551
                    ref = branch[1:] 
        
            552
                else: 
        
            553
                    ref = f"heads/{branch}" 
        
            554
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            556
                try: 
        
            558
                    repo.git.checkout("-f", ref) 
        
            559
                except git.exc.GitCommandError: 
        
            560
                    return flask.render_template("not-found.html"), 404 
        
            561
                branches = repo.heads 
        
            563
                all_refs = [] 
        
            565
                for ref in repo.heads: 
        
            566
                    all_refs.append((ref, "head")) 
        
            567
                for ref in repo.tags: 
        
            568
                    all_refs.append((ref, "tag")) 
        
            569
                if os.path.isdir(os.path.join(server_repo_location, subpath)): 
        
            571
                    files = [] 
        
            572
                    blobs = [] 
        
            573
                    for entry in os.listdir(os.path.join(server_repo_location, subpath)): 
        
            575
                        if not os.path.basename(entry) == ".git": 
        
            576
                            files.append(os.path.join(subpath, entry)) 
        
            577
                    infos = [] 
        
            579
                    for file in files: 
        
            581
                        path = os.path.join(server_repo_location, file) 
        
            582
                        mimetype = guess_mime(path) 
        
            583
                        text = git_command(server_repo_location, None, "log", "--format='%H\n'", 
        
            585
                                           shlex.quote(file)).decode() 
        
            586
                        sha = text.split("\n")[0] 
        
            588
                        identifier = f"/{username}/{repository}/{sha}" 
        
            589
                        last_commit = db.session.get(Commit, identifier) 
        
            591
                        info = { 
        
            593
                            "name": os.path.basename(file), 
        
            594
                            "serverPath": path, 
        
            595
                            "relativePath": file, 
        
            596
                            "link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file), 
        
            597
                            "size": human_size(os.path.getsize(path)), 
        
            598
                            "mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}", 
        
            599
                            "commit": last_commit, 
        
            600
                            "shaSize": 7, 
        
            601
                        } 
        
            602
                        special_icon = config.match_icon(os.path.basename(file)) 
        
            604
                        if special_icon: 
        
            605
                            info["icon"] = special_icon 
        
            606
                        elif os.path.isdir(path): 
        
            607
                            info["icon"] = config.folder_icon 
        
            608
                        elif mimetypes.guess_type(path)[0] in config.file_icons: 
        
            609
                            info["icon"] = config.file_icons[mimetypes.guess_type(path)[0]] 
        
            610
                        else: 
        
            611
                            info["icon"] = config.unknown_icon 
        
            612
                        if os.path.isdir(path): 
        
            614
                            infos.insert(0, info) 
        
            615
                        else: 
        
            616
                            infos.append(info) 
        
            617
                    return flask.render_template( 
        
            619
                            "repo-tree.html", 
        
            620
                            username=username, 
        
            621
                            repository=repository, 
        
            622
                            files=infos, 
        
            623
                            subpath=os.path.join("/", subpath), 
        
            624
                            branches=all_refs, 
        
            625
                            current=branch, 
        
            626
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            627
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            628
                    ) 
        
            629
                else: 
        
            630
                    path = os.path.join(server_repo_location, subpath) 
        
            631
                    if not os.path.exists(path): 
        
            633
                        return flask.render_template("not-found.html"), 404 
        
            634
                    mimetype = guess_mime(path) 
        
            636
                    mode = mimetype.split("/", 1)[0] 
        
            637
                    size = human_size(os.path.getsize(path)) 
        
            638
                    special_icon = config.match_icon(os.path.basename(path)) 
        
            640
                    if special_icon: 
        
            641
                        icon = special_icon 
        
            642
                    elif os.path.isdir(path): 
        
            643
                        icon = config.folder_icon 
        
            644
                    elif mimetypes.guess_type(path)[0] in config.file_icons: 
        
            645
                        icon = config.file_icons[mimetypes.guess_type(path)[0]] 
        
            646
                    else: 
        
            647
                        icon = config.unknown_icon 
        
            648
                    contents = None 
        
            650
                    if mode == "text": 
        
            651
                        contents = convert_to_html(path) 
        
            652
                    return flask.render_template( 
        
            654
                            "repo-file.html", 
        
            655
                            username=username, 
        
            656
                            repository=repository, 
        
            657
                            file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath), 
        
            658
                            branches=all_refs, 
        
            659
                            current=branch, 
        
            660
                            mode=mode, 
        
            661
                            mimetype=mimetype, 
        
            662
                            detailedtype=magic.from_file(path), 
        
            663
                            size=size, 
        
            664
                            icon=icon, 
        
            665
                            subpath=os.path.join("/", subpath), 
        
            666
                            extension=pathlib.Path(path).suffix, 
        
            667
                            basename=os.path.basename(path), 
        
            668
                            contents=contents, 
        
            669
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            670
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            671
                    ) 
        
            672
            @repositories.route("/<username>/<repository>/commit/<sha>") 
        
            675
            def repository_commit(username, repository, sha): 
        
            676
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            677
                if not os.path.exists(server_repo_location): 
        
            678
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            679
                    flask.abort(404) 
        
            680
                if not (get_visibility(username, repository) or get_permission_level( 
        
            681
                        flask.session.get("username"), username, 
        
            682
                        repository) is not None): 
        
            683
                    flask.abort(403) 
        
            684
                app.logger.info(f"Loading {server_repo_location}") 
        
            686
                if not os.path.exists(server_repo_location): 
        
            688
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            689
                    return flask.render_template("not-found.html"), 404 
        
            690
                repo = git.Repo(server_repo_location) 
        
            692
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            693
                files = git_command(os.path.join(server_repo_location, ".git"), None, "diff-tree", "-r", 
        
            695
                                    "--name-only", "--no-commit-id", sha).decode().split("\n")[:-1] 
        
            696
                print(files) 
        
            698
                return flask.render_template( 
        
            700
                        "repo-commit.html", 
        
            701
                        username=username, 
        
            702
                        repository=repository, 
        
            703
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            704
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            705
                        diff={file: git_command(os.path.join(server_repo_location, ".git"), None, "diff", 
        
            706
                                                str(sha) + "^!", "--", file).decode().split("\n") for 
        
            707
                              file in files}, 
        
            708
                        data=db.session.get(Commit, f"/{username}/{repository}/{sha}"), 
        
            709
                ) 
        
            710
            @repositories.route("/<username>/<repository>/forum/") 
        
            713
            def repository_forum(username, repository): 
        
            714
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            715
                if not os.path.exists(server_repo_location): 
        
            716
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            717
                    flask.abort(404) 
        
            718
                if not (get_visibility(username, repository) or get_permission_level( 
        
            719
                        flask.session.get("username"), username, 
        
            720
                        repository) is not None): 
        
            721
                    flask.abort(403) 
        
            722
                app.logger.info(f"Loading {server_repo_location}") 
        
            724
                if not os.path.exists(server_repo_location): 
        
            726
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            727
                    return flask.render_template("not-found.html"), 404 
        
            728
                repo = git.Repo(server_repo_location) 
        
            730
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            731
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            732
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            733
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            734
                return flask.render_template( 
        
            736
                        "repo-forum.html", 
        
            737
                        username=username, 
        
            738
                        repository=repository, 
        
            739
                        repo_data=repo_data, 
        
            740
                        relationships=relationships, 
        
            741
                        repo=repo, 
        
            742
                        user_relationship=user_relationship, 
        
            743
                        Post=Post, 
        
            744
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            745
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            746
                        default_branch=repo_data.default_branch 
        
            747
                ) 
        
            748
            @repositories.route("/<username>/<repository>/forum/topic/<int:id>") 
        
            751
            def repository_forum_topic(username, repository, id): 
        
            752
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            753
                if not os.path.exists(server_repo_location): 
        
            754
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            755
                    flask.abort(404) 
        
            756
                if not (get_visibility(username, repository) or get_permission_level( 
        
            757
                        flask.session.get("username"), username, 
        
            758
                        repository) is not None): 
        
            759
                    flask.abort(403) 
        
            760
                app.logger.info(f"Loading {server_repo_location}") 
        
            762
                if not os.path.exists(server_repo_location): 
        
            764
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            765
                    return flask.render_template("not-found.html"), 404 
        
            766
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            768
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            769
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            770
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            771
                post = Post.query.filter_by(id=id).first() 
        
            773
                return flask.render_template( 
        
            775
                        "repo-topic.html", 
        
            776
                        username=username, 
        
            777
                        repository=repository, 
        
            778
                        repo_data=repo_data, 
        
            779
                        relationships=relationships, 
        
            780
                        user_relationship=user_relationship, 
        
            781
                        post=post, 
        
            782
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            783
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            784
                        default_branch=repo_data.default_branch 
        
            785
                ) 
        
            786
            @repositories.route("/<username>/<repository>/forum/new", methods=["POST", "GET"]) 
        
            789
            def repository_forum_new(username, repository): 
        
            790
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            791
                if not os.path.exists(server_repo_location): 
        
            792
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            793
                    flask.abort(404) 
        
            794
                if not (get_visibility(username, repository) or get_permission_level( 
        
            795
                        flask.session.get("username"), username, 
        
            796
                        repository) is not None): 
        
            797
                    flask.abort(403) 
        
            798
                app.logger.info(f"Loading {server_repo_location}") 
        
            800
                if not os.path.exists(server_repo_location): 
        
            802
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            803
                    return flask.render_template("not-found.html"), 404 
        
            804
                repo = git.Repo(server_repo_location) 
        
            806
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            807
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            808
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            809
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            810
                post = Post(user, repo_data, None, flask.request.form["subject"], 
        
            812
                            flask.request.form["message"]) 
        
            813
                db.session.add(post) 
        
            815
                db.session.commit() 
        
            816
                return flask.redirect( 
        
            818
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            819
                                      post_id=post.number), 
        
            820
                        code=303) 
        
            821
            @repositories.route("/<username>/<repository>/forum/<int:post_id>") 
        
            824
            def repository_forum_thread(username, repository, post_id): 
        
            825
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            826
                if not os.path.exists(server_repo_location): 
        
            827
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            828
                    flask.abort(404) 
        
            829
                if not (get_visibility(username, repository) or get_permission_level( 
        
            830
                        flask.session.get("username"), username, 
        
            831
                        repository) is not None): 
        
            832
                    flask.abort(403) 
        
            833
                app.logger.info(f"Loading {server_repo_location}") 
        
            835
                if not os.path.exists(server_repo_location): 
        
            837
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            838
                    return flask.render_template("not-found.html"), 404 
        
            839
                repo = git.Repo(server_repo_location) 
        
            841
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            842
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            843
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            844
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            845
                return flask.render_template( 
        
            847
                        "repo-forum-thread.html", 
        
            848
                        username=username, 
        
            849
                        repository=repository, 
        
            850
                        repo_data=repo_data, 
        
            851
                        relationships=relationships, 
        
            852
                        repo=repo, 
        
            853
                        Post=Post, 
        
            854
                        user_relationship=user_relationship, 
        
            855
                        post_id=post_id, 
        
            856
                        max_post_nesting=4, 
        
            857
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            858
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            859
                        parent=Post.query.filter_by(repo=repo_data, number=post_id).first(), 
        
            860
                ) 
        
            861
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/change-state", 
        
            864
                                methods=["POST"]) 
        
            865
            def repository_forum_change_state(username, repository, post_id): 
        
            866
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            867
                if not os.path.exists(server_repo_location): 
        
            868
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            869
                    flask.abort(404) 
        
            870
                if not (get_visibility(username, repository) or get_permission_level( 
        
            871
                        flask.session.get("username"), username, 
        
            872
                        repository) is not None): 
        
            873
                    flask.abort(403) 
        
            874
                app.logger.info(f"Loading {server_repo_location}") 
        
            876
                repo = git.Repo(server_repo_location) 
        
            878
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            879
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            880
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            881
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            882
                post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            884
                if not post: 
        
            886
                    flask.abort(404) 
        
            887
                post.state = int(flask.request.form["new-state"]) 
        
            889
                db.session.commit() 
        
            891
                return flask.redirect( 
        
            893
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            894
                                      post_id=post_id), 
        
            895
                        code=303) 
        
            896
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/reply", methods=["POST"]) 
        
            899
            def repository_forum_reply(username, repository, post_id): 
        
            900
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            901
                if not os.path.exists(server_repo_location): 
        
            902
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            903
                    flask.abort(404) 
        
            904
                if not (get_visibility(username, repository) or get_permission_level( 
        
            905
                        flask.session.get("username"), username, 
        
            906
                        repository) is not None): 
        
            907
                    flask.abort(403) 
        
            908
                app.logger.info(f"Loading {server_repo_location}") 
        
            910
                if not os.path.exists(server_repo_location): 
        
            912
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            913
                    return flask.render_template("not-found.html"), 404 
        
            914
                repo = git.Repo(server_repo_location) 
        
            916
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            917
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            918
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            919
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            920
                if not user: 
        
            921
                    flask.abort(401) 
        
            922
                parent = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            924
                post = Post(user, repo_data, parent, flask.request.form["subject"], 
        
            925
                            flask.request.form["message"]) 
        
            926
                db.session.add(post) 
        
            928
                post.update_date() 
        
            929
                db.session.commit() 
        
            930
                return flask.redirect( 
        
            932
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            933
                                      post_id=post_id), 
        
            934
                        code=303) 
        
            935
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/voteup", 
        
            938
                                defaults={"score": 1}) 
        
            939
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/votedown", 
        
            940
                                defaults={"score": -1}) 
        
            941
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/votes", defaults={"score": 0}) 
        
            942
            def repository_forum_vote(username, repository, post_id, score): 
        
            943
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            944
                if not os.path.exists(server_repo_location): 
        
            945
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            946
                    flask.abort(404) 
        
            947
                if not (get_visibility(username, repository) or get_permission_level( 
        
            948
                        flask.session.get("username"), username, 
        
            949
                        repository) is not None): 
        
            950
                    flask.abort(403) 
        
            951
                app.logger.info(f"Loading {server_repo_location}") 
        
            953
                if not os.path.exists(server_repo_location): 
        
            955
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            956
                    return flask.render_template("not-found.html"), 404 
        
            957
                repo = git.Repo(server_repo_location) 
        
            959
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            960
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            961
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            962
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            963
                if not user: 
        
            964
                    flask.abort(401) 
        
            965
                post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            967
                if score: 
        
            969
                    old_relationship = PostVote.query.filter_by(user_username=user.username, 
        
            970
                                                                post_identifier=post.identifier).first() 
        
            971
                    if old_relationship: 
        
            972
                        if score == old_relationship.vote_score: 
        
            973
                            db.session.delete(old_relationship) 
        
            974
                            post.vote_sum -= old_relationship.vote_score 
        
            975
                        else: 
        
            976
                            post.vote_sum -= old_relationship.vote_score 
        
            977
                            post.vote_sum += score 
        
            978
                            old_relationship.vote_score = score 
        
            979
                    else: 
        
            980
                        relationship = PostVote(user, post, score) 
        
            981
                        post.vote_sum += score 
        
            982
                        db.session.add(relationship) 
        
            983
                    db.session.commit() 
        
            985
                user_vote = PostVote.query.filter_by(user_username=user.username, 
        
            987
                                                     post_identifier=post.identifier).first() 
        
            988
                response = flask.make_response( 
        
            989
                        str(post.vote_sum) + " " + str(user_vote.vote_score if user_vote else 0)) 
        
            990
                response.content_type = "text/plain" 
        
            991
                return response 
        
            993
            @repositories.route("/<username>/<repository>/favourite") 
        
            996
            def repository_favourite(username, repository): 
        
            997
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            998
                if not os.path.exists(server_repo_location): 
        
            999
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1000
                    flask.abort(404) 
        
            1001
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1002
                        flask.session.get("username"), username, 
        
            1003
                        repository) is not None): 
        
            1004
                    flask.abort(403) 
        
            1005
                app.logger.info(f"Loading {server_repo_location}") 
        
            1007
                if not os.path.exists(server_repo_location): 
        
            1009
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1010
                    return flask.render_template("not-found.html"), 404 
        
            1011
                repo = git.Repo(server_repo_location) 
        
            1013
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1014
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1015
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            1016
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            1017
                if not user: 
        
            1018
                    flask.abort(401) 
        
            1019
                old_relationship = RepoFavourite.query.filter_by(user_username=user.username, 
        
            1021
                                                                 repo_route=repo_data.route).first() 
        
            1022
                if old_relationship: 
        
            1023
                    db.session.delete(old_relationship) 
        
            1024
                else: 
        
            1025
                    relationship = RepoFavourite(user, repo_data) 
        
            1026
                    db.session.add(relationship) 
        
            1027
                db.session.commit() 
        
            1029
                return flask.redirect(flask.url_for("favourites"), code=303) 
        
            1031
            @repositories.route("/<username>/<repository>/users/", methods=["GET", "POST"]) 
        
            1034
            def repository_users(username, repository): 
        
            1035
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1036
                if not os.path.exists(server_repo_location): 
        
            1037
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1038
                    flask.abort(404) 
        
            1039
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1040
                        flask.session.get("username"), username, 
        
            1041
                        repository) is not None): 
        
            1042
                    flask.abort(403) 
        
            1043
                app.logger.info(f"Loading {server_repo_location}") 
        
            1045
                if not os.path.exists(server_repo_location): 
        
            1047
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1048
                    return flask.render_template("not-found.html"), 404 
        
            1049
                repo = git.Repo(server_repo_location) 
        
            1051
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1052
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1053
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            1054
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            1055
                if flask.request.method == "GET": 
        
            1057
                    return flask.render_template( 
        
            1058
                            "repo-users.html", 
        
            1059
                            username=username, 
        
            1060
                            repository=repository, 
        
            1061
                            repo_data=repo_data, 
        
            1062
                            relationships=relationships, 
        
            1063
                            repo=repo, 
        
            1064
                            user_relationship=user_relationship, 
        
            1065
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1066
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1067
                    ) 
        
            1068
                else: 
        
            1069
                    if get_permission_level(flask.session.get("username"), username, repository) != 2: 
        
            1070
                        flask.abort(401) 
        
            1071
                    if flask.request.form.get("new-username"): 
        
            1073
                        # Create new relationship 
        
            1074
                        new_user = User.query.filter_by( 
        
            1075
                                username=flask.request.form.get("new-username")).first() 
        
            1076
                        relationship = RepoAccess(new_user, repo_data, flask.request.form.get("new-level")) 
        
            1077
                        db.session.add(relationship) 
        
            1078
                        db.session.commit() 
        
            1079
                    if flask.request.form.get("update-username"): 
        
            1080
                        # Create new relationship 
        
            1081
                        updated_user = User.query.filter_by( 
        
            1082
                                username=flask.request.form.get("update-username")).first() 
        
            1083
                        relationship = RepoAccess.query.filter_by(repo=repo_data, user=updated_user).first() 
        
            1084
                        if flask.request.form.get("update-level") == -1: 
        
            1085
                            relationship.delete() 
        
            1086
                        else: 
        
            1087
                            relationship.access_level = flask.request.form.get("update-level") 
        
            1088
                        db.session.commit() 
        
            1089
                    return flask.redirect( 
        
            1091
                            app.url_for(".repository_users", username=username, repository=repository)) 
        
            1092
            @repositories.route("/<username>/<repository>/branches/") 
        
            1095
            def repository_branches(username, repository): 
        
            1096
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1097
                if not os.path.exists(server_repo_location): 
        
            1098
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1099
                    flask.abort(404) 
        
            1100
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1101
                        flask.session.get("username"), username, 
        
            1102
                        repository) is not None): 
        
            1103
                    flask.abort(403) 
        
            1104
                app.logger.info(f"Loading {server_repo_location}") 
        
            1106
                if not os.path.exists(server_repo_location): 
        
            1108
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1109
                    return flask.render_template("not-found.html"), 404 
        
            1110
                repo = git.Repo(server_repo_location) 
        
            1112
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1113
                return flask.render_template( 
        
            1115
                        "repo-branches.html", 
        
            1116
                        username=username, 
        
            1117
                        repository=repository, 
        
            1118
                        repo_data=repo_data, 
        
            1119
                        repo=repo, 
        
            1120
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1121
                        is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1122
                ) 
        
            1123
            @repositories.route("/<username>/<repository>/log/", defaults={"branch": None}) 
        
            1126
            @repositories.route("/<username>/<repository>/log/<branch>/") 
        
            1127
            def repository_log(username, repository, branch): 
        
            1128
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1129
                if not os.path.exists(server_repo_location): 
        
            1130
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1131
                    flask.abort(404) 
        
            1132
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1133
                        flask.session.get("username"), username, 
        
            1134
                        repository) is not None): 
        
            1135
                    flask.abort(403) 
        
            1136
                app.logger.info(f"Loading {server_repo_location}") 
        
            1138
                if not os.path.exists(server_repo_location): 
        
            1140
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1141
                    return flask.render_template("not-found.html"), 404 
        
            1142
                repo = git.Repo(server_repo_location) 
        
            1144
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1145
                if not repo_data.default_branch: 
        
            1146
                    if repo.heads: 
        
            1147
                        repo_data.default_branch = repo.heads[0].name 
        
            1148
                    else: 
        
            1149
                        return flask.render_template("empty.html", 
        
            1150
                                                     remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            1151
                if not branch: 
        
            1152
                    branch = repo_data.default_branch 
        
            1153
                    return flask.redirect(f"./{branch}", code=302) 
        
            1154
                if branch.startswith("tag:"): 
        
            1156
                    ref = f"tags/{branch[4:]}" 
        
            1157
                elif branch.startswith("~"): 
        
            1158
                    ref = branch[1:] 
        
            1159
                else: 
        
            1160
                    ref = f"heads/{branch}" 
        
            1161
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            1163
                try: 
        
            1165
                    repo.git.checkout("-f", ref) 
        
            1166
                except git.exc.GitCommandError: 
        
            1167
                    return flask.render_template("not-found.html"), 404 
        
            1168
                branches = repo.heads 
        
            1170
                all_refs = [] 
        
            1172
                for ref in repo.heads: 
        
            1173
                    all_refs.append((ref, "head")) 
        
            1174
                for ref in repo.tags: 
        
            1175
                    all_refs.append((ref, "tag")) 
        
            1176
                commit_list = [f"/{username}/{repository}/{sha}" for sha in 
        
            1178
                               git_command(server_repo_location, None, "log", 
        
            1179
                                           "--format='%H'").decode().split("\n")] 
        
            1180
                commits = Commit.query.filter(Commit.identifier.in_(commit_list)) 
        
            1182
                return flask.render_template( 
        
            1184
                        "repo-log.html", 
        
            1185
                        username=username, 
        
            1186
                        repository=repository, 
        
            1187
                        branches=all_refs, 
        
            1188
                        current=branch, 
        
            1189
                        repo_data=repo_data, 
        
            1190
                        repo=repo, 
        
            1191
                        commits=commits, 
        
            1192
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1193
                        is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1194
                ) 
        
            1195
            @repositories.route("/<username>/<repository>/prs/", methods=["GET", "POST"]) 
        
            1198
            def repository_prs(username, repository): 
        
            1199
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1200
                if not os.path.exists(server_repo_location): 
        
            1201
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1202
                    flask.abort(404) 
        
            1203
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1204
                        flask.session.get("username"), username, 
        
            1205
                        repository) is not None): 
        
            1206
                    flask.abort(403) 
        
            1207
                app.logger.info(f"Loading {server_repo_location}") 
        
            1209
                if not os.path.exists(server_repo_location): 
        
            1211
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1212
                    return flask.render_template("not-found.html"), 404 
        
            1213
                if flask.request.method == "GET": 
        
            1215
                    repo = git.Repo(server_repo_location) 
        
            1216
                    repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1217
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1218
                    return flask.render_template( 
        
            1220
                            "repo-prs.html", 
        
            1221
                            username=username, 
        
            1222
                            repository=repository, 
        
            1223
                            repo_data=repo_data, 
        
            1224
                            repo=repo, 
        
            1225
                            PullRequest=PullRequest, 
        
            1226
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1227
                            is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            1228
                            default_branch=repo_data.default_branch, 
        
            1229
                            branches=repo.branches 
        
            1230
                    ) 
        
            1231
                else: 
        
            1233
                    repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1234
                    head = flask.request.form.get("head") 
        
            1235
                    head_route = flask.request.form.get("headroute") 
        
            1236
                    base = flask.request.form.get("base") 
        
            1237
                    if not head and base and head_route: 
        
            1239
                        return flask.redirect(".", 400) 
        
            1240
                    head_repo = git.Repo(os.path.join(config.REPOS_PATH, head_route.lstrip("/"))) 
        
            1242
                    base_repo = git.Repo(server_repo_location) 
        
            1243
                    print(head_repo) 
        
            1244
                    if head not in head_repo.branches or base not in base_repo.branches: 
        
            1246
                        flask.flash(Markup( 
        
            1247
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _("Bad branch name")), 
        
            1248
                                category="error") 
        
            1249
                        return flask.redirect(".", 303) 
        
            1250
                    head_data = db.session.get(Repo, head_route) 
        
            1252
                    if not head_data.visibility: 
        
            1253
                        flask.flash(Markup( 
        
            1254
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _( 
        
            1255
                                        "Head can't be restricted")), 
        
            1256
                                category="error") 
        
            1257
                        return flask.redirect(".", 303) 
        
            1258
                    pull_request = PullRequest(repo_data, head, head_data, base, 
        
            1260
                                               db.session.get(User, flask.session["username"])) 
        
            1261
                    db.session.add(pull_request) 
        
            1263
                    db.session.commit() 
        
            1264
                    return flask.redirect(".", 303) 
        
            1266
            @repositories.route("/<username>/<repository>/prs/merge", methods=["POST"]) 
        
            1269
            def repository_prs_merge(username, repository): 
        
            1270
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1271
                if not os.path.exists(server_repo_location): 
        
            1272
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1273
                    flask.abort(404) 
        
            1274
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1275
                        flask.session.get("username"), username, 
        
            1276
                        repository) is not None): 
        
            1277
                    flask.abort(403) 
        
            1278
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1280
                    flask.abort(401) 
        
            1281
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1283
                repo = git.Repo(server_repo_location) 
        
            1284
                id = flask.request.form.get("id") 
        
            1285
                pull_request = db.session.get(PullRequest, id) 
        
            1287
                if pull_request: 
        
            1289
                    result = celery_tasks.merge_heads.delay( 
        
            1290
                            pull_request.head_route, 
        
            1291
                            pull_request.head_branch, 
        
            1292
                            pull_request.base_route, 
        
            1293
                            pull_request.base_branch, 
        
            1294
                            simulate=True 
        
            1295
                    ) 
        
            1296
                    task_result = worker.AsyncResult(result.id) 
        
            1297
                    return flask.redirect(f"/task/{result.id}?pr-id={id}", 303) 
        
            1299
                    # db.session.delete(pull_request) 
        
            1300
                    # db.session.commit() 
        
            1301
                else: 
        
            1302
                    flask.abort(400) 
        
            1303
            @repositories.route("/<username>/<repository>/prs/<int:id>/merge") 
        
            1306
            def repository_prs_merge_stage_two(username, repository, id): 
        
            1307
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1308
                if not os.path.exists(server_repo_location): 
        
            1309
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1310
                    flask.abort(404) 
        
            1311
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1312
                        flask.session.get("username"), username, 
        
            1313
                        repository) is not None): 
        
            1314
                    flask.abort(403) 
        
            1315
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1317
                    flask.abort(401) 
        
            1318
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1320
                repo = git.Repo(server_repo_location) 
        
            1321
                pull_request = db.session.get(PullRequest, id) 
        
            1323
                if pull_request: 
        
            1325
                    result = celery_tasks.merge_heads.delay( 
        
            1326
                            pull_request.head_route, 
        
            1327
                            pull_request.head_branch, 
        
            1328
                            pull_request.base_route, 
        
            1329
                            pull_request.base_branch, 
        
            1330
                            simulate=False 
        
            1331
                    ) 
        
            1332
                    task_result = worker.AsyncResult(result.id) 
        
            1333
                    pull_request.state = 1 
        
            1335
                    db.session.commit() 
        
            1336
                    return flask.redirect(f"/task/{result.id}?pr-id={id}", 303) 
        
            1338
                    # db.session.delete(pull_request) 
        
            1339
                else: 
        
            1340
                    flask.abort(400) 
        
            1341
            @app.route("/task/<task_id>") 
        
            1344
            def task_monitor(task_id): 
        
            1345
                task_result = worker.AsyncResult(task_id) 
        
            1346
                print(task_result.status) 
        
            1347
                return flask.render_template("task-monitor.html", result=task_result) 
        
            1349
            @repositories.route("/<username>/<repository>/prs/delete", methods=["POST"]) 
        
            1352
            def repository_prs_delete(username, repository): 
        
            1353
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1354
                if not os.path.exists(server_repo_location): 
        
            1355
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1356
                    flask.abort(404) 
        
            1357
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1358
                        flask.session.get("username"), username, 
        
            1359
                        repository) is not None): 
        
            1360
                    flask.abort(403) 
        
            1361
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1363
                    flask.abort(401) 
        
            1364
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1366
                repo = git.Repo(server_repo_location) 
        
            1367
                id = flask.request.form.get("id") 
        
            1368
                pull_request = db.session.get(PullRequest, id) 
        
            1370
                if pull_request: 
        
            1372
                    pull_request.state = 2 
        
            1373
                    db.session.commit() 
        
            1374
                return flask.redirect(".", 303) 
        
            1376
            @repositories.route("/<username>/<repository>/settings/") 
        
            1379
            def repository_settings(username, repository): 
        
            1380
                if get_permission_level(flask.session.get("username"), username, repository) != 2: 
        
            1381
                    flask.abort(401) 
        
            1382
                return flask.render_template("repo-settings.html", username=username, repository=repository) 
        
            1384
            @app.errorhandler(404) 
        
            1387
            def e404(error): 
        
            1388
                return flask.render_template("not-found.html"), 404 
        
            1389
            @app.errorhandler(401) 
        
            1392
            def e401(error): 
        
            1393
                return flask.render_template("unauthorised.html"), 401 
        
            1394
            @app.errorhandler(403) 
        
            1397
            def e403(error): 
        
            1398
                return flask.render_template("forbidden.html"), 403 
        
            1399
            @app.errorhandler(418) 
        
            1402
            def e418(error): 
        
            1403
                return flask.render_template("teapot.html"), 418 
        
            1404
            @app.errorhandler(405) 
        
            1407
            def e405(error): 
        
            1408
                return flask.render_template("method-not-allowed.html"), 405 
        
            1409
            if __name__ == "__main__": 
        
            1412
                app.run(debug=True, port=8080, host="0.0.0.0") 
        
            1413
            app.register_blueprint(repositories) 
        
            1415