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(os.path.join(config.REPOS_PATH, flask.session.get("username"), ".config", "theme.css")): 
        
            118
                    return flask.send_from_directory(os.path.join(config.REPOS_PATH, flask.session.get("username"), ".config"), "theme.css") 
        
            119
                else: 
        
            120
                    return flask.Response("", mimetype="text/css") 
        
            121
            @app.route("/about/") 
        
            124
            def about(): 
        
            125
                return flask.render_template("about.html", platform=platform, version=__version__) 
        
            126
            @app.route("/language", methods=["POST"]) 
        
            129
            def set_locale(): 
        
            130
                response = flask.redirect(flask.request.referrer if flask.request.referrer else "/", 
        
            131
                                          code=303) 
        
            132
                if not flask.request.form.get("language"): 
        
            133
                    response.delete_cookie("language") 
        
            134
                else: 
        
            135
                    response.set_cookie("language", flask.request.form.get("language")) 
        
            136
                return response 
        
            138
            @app.route("/cookie-dismiss") 
        
            141
            def dismiss_banner(): 
        
            142
                response = flask.redirect(flask.request.referrer if flask.request.referrer else "/", 
        
            143
                                          code=303) 
        
            144
                response.set_cookie("cookie-banner", "1") 
        
            145
                return response 
        
            146
            @app.route("/help/") 
        
            149
            def help_index(): 
        
            150
                return flask.render_template("help.html", faqs=config.faqs) 
        
            151
            @app.route("/settings/", methods=["GET", "POST"]) 
        
            154
            def settings(): 
        
            155
                if not flask.session.get("username"): 
        
            156
                    flask.abort(401) 
        
            157
                if flask.request.method == "GET": 
        
            158
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            159
                    return flask.render_template("user-settings.html", user=user) 
        
            161
                else: 
        
            162
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            163
                    user.display_name = flask.request.form["displayname"] 
        
            165
                    user.URL = flask.request.form["url"] 
        
            166
                    user.company = flask.request.form["company"] 
        
            167
                    user.company_URL = flask.request.form["companyurl"] 
        
            168
                    user.email = flask.request.form.get("email") if flask.request.form.get( 
        
            169
                        "email") else None 
        
            170
                    user.location = flask.request.form["location"] 
        
            171
                    user.show_mail = True if flask.request.form.get("showmail") else False 
        
            172
                    user.bio = flask.request.form.get("bio") 
        
            173
                    db.session.commit() 
        
            175
                    flask.flash( 
        
            177
                        Markup("<iconify-icon icon='mdi:check'></iconify-icon>" + _("Settings saved")), 
        
            178
                        category="success") 
        
            179
                    return flask.redirect(f"/{flask.session.get('username')}", code=303) 
        
            180
            @app.route("/favourites/", methods=["GET", "POST"]) 
        
            183
            def favourites(): 
        
            184
                if not flask.session.get("username"): 
        
            185
                    flask.abort(401) 
        
            186
                if flask.request.method == "GET": 
        
            187
                    relationships = RepoFavourite.query.filter_by( 
        
            188
                        user_username=flask.session.get("username")) 
        
            189
                    return flask.render_template("favourites.html", favourites=relationships) 
        
            191
            @app.route("/favourites/<int:id>", methods=["POST"]) 
        
            194
            def favourite_edit(id): 
        
            195
                if not flask.session.get("username"): 
        
            196
                    flask.abort(401) 
        
            197
                favourite = db.session.get(RepoFavourite, id) 
        
            198
                if favourite.user_username != flask.session.get("username"): 
        
            199
                    flask.abort(403) 
        
            200
                data = flask.request.form 
        
            201
                print(data) 
        
            202
                favourite.notify_commit = js_to_bool(data.get("commit")) 
        
            203
                favourite.notify_forum = js_to_bool(data.get("forum")) 
        
            204
                favourite.notify_pr = js_to_bool(data.get("pull_request")) 
        
            205
                favourite.notify_admin = js_to_bool(data.get("administrative")) 
        
            206
                print(favourite.notify_commit, favourite.notify_forum, favourite.notify_pr, favourite.notify_admin) 
        
            207
                db.session.commit() 
        
            208
                return flask.render_template_string( 
        
            209
                    """ 
        
            210
                    <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"> 
        
            211
                        <td><a href="{{ favourite.repo.route }}">{{ favourite.repo.owner.username }}/{{ favourite.repo.name }}</a></td> 
        
            212
                        <td style="text-align: center;"><input type="checkbox" name="commit" id="commit-{{ favourite.id }}" value="true" {% if favourite.notify_commit %}checked{% endif %}></td> 
        
            213
                        <td style="text-align: center;"><input type="checkbox" name="forum" id="forum-{{ favourite.id }}" value="true" {% if favourite.notify_forum %}checked{% endif %}></td> 
        
            214
                        <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> 
        
            215
                        <td style="text-align: center;"><input type="checkbox" name="administrative" id="administrative-{{ favourite.id }}" value="true" {% if favourite.notify_admin %}checked{% endif %}></td> 
        
            216
                    </tr> 
        
            217
                    """, 
        
            218
                    favourite=favourite 
        
            219
                ) 
        
            220
            @app.route("/notifications/", methods=["GET", "POST"]) 
        
            223
            def notifications(): 
        
            224
                if not flask.session.get("username"): 
        
            225
                    flask.abort(401) 
        
            226
                if flask.request.method == "GET": 
        
            227
                    return flask.render_template("notifications.html", 
        
            228
                                                 notifications=UserNotification.query.filter_by( 
        
            229
                                                         user_username=flask.session.get("username"))) 
        
            230
            @app.route("/notifications/<int:notification_id>/read", methods=["POST"]) 
        
            233
            def mark_read(notification_id): 
        
            234
                if not flask.session.get("username"): 
        
            235
                    flask.abort(401) 
        
            236
                notification = UserNotification.query.filter_by(id=notification_id).first() 
        
            237
                if notification.user_username != flask.session.get("username"): 
        
            238
                    flask.abort(403) 
        
            239
                notification.mark_read() 
        
            240
                db.session.commit() 
        
            241
                return flask.render_template_string("<button hx-post='/notifications/{{ notification.id }}/unread' hx-swap='outerHTML'>Mark as unread</button>", notification=notification), 200 
        
            242
            @app.route("/notifications/<int:notification_id>/unread", methods=["POST"]) 
        
            245
            def mark_unread(notification_id): 
        
            246
                if not flask.session.get("username"): 
        
            247
                    flask.abort(401) 
        
            248
                notification = UserNotification.query.filter_by(id=notification_id).first() 
        
            249
                if notification.user_username != flask.session.get("username"): 
        
            250
                    flask.abort(403) 
        
            251
                notification.mark_unread() 
        
            252
                db.session.commit() 
        
            253
                return flask.render_template_string("<button hx-post='/notifications/{{ notification.id }}/read' hx-swap='outerHTML'>Mark as read</button>", notification=notification), 200 
        
            254
            @app.route("/notifications/mark-all-read", methods=["POST"]) 
        
            257
            def mark_all_read(): 
        
            258
                if not flask.session.get("username"): 
        
            259
                    flask.abort(401) 
        
            260
                notifications = UserNotification.query.filter_by( 
        
            262
                                user_username=flask.session.get("username")) 
        
            263
                for notification in notifications: 
        
            264
                    notification.mark_read() 
        
            265
                db.session.commit() 
        
            266
                return flask.redirect("/notifications/", code=303) 
        
            267
            @app.route("/accounts/", methods=["GET", "POST"]) 
        
            270
            def login(): 
        
            271
                if flask.request.method == "GET": 
        
            272
                    return flask.render_template("login.html") 
        
            273
                else: 
        
            274
                    if "login" in flask.request.form: 
        
            275
                        username = flask.request.form["username"] 
        
            276
                        password = flask.request.form["password"] 
        
            277
                        user = User.query.filter_by(username=username).first() 
        
            279
                        if user and bcrypt.check_password_hash(user.password_hashed, password): 
        
            281
                            flask.session["username"] = user.username 
        
            282
                            flask.flash( 
        
            283
                                    Markup("<iconify-icon icon='mdi:account'></iconify-icon>" + _( 
        
            284
                                        "Successfully logged in as {username}").format(username=username)), 
        
            285
                                    category="success") 
        
            286
                            return flask.redirect("/", code=303) 
        
            287
                        elif not user: 
        
            288
                            flask.flash(Markup( 
        
            289
                                "<iconify-icon icon='mdi:account-question'></iconify-icon>" + _( 
        
            290
                                    "User not found")), 
        
            291
                                        category="alert") 
        
            292
                            return flask.render_template("login.html") 
        
            293
                        else: 
        
            294
                            flask.flash(Markup( 
        
            295
                                "<iconify-icon icon='mdi:account-question'></iconify-icon>" + _( 
        
            296
                                    "Invalid password")), 
        
            297
                                        category="error") 
        
            298
                            return flask.render_template("login.html") 
        
            299
                    if "signup" in flask.request.form: 
        
            300
                        username = flask.request.form["username"] 
        
            301
                        password = flask.request.form["password"] 
        
            302
                        password2 = flask.request.form["password2"] 
        
            303
                        email = flask.request.form.get("email") 
        
            304
                        email2 = flask.request.form.get("email2")  # repeat email is a honeypot 
        
            305
                        name = flask.request.form.get("name") 
        
            306
                        if not only_chars(username, 
        
            308
                                          "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"): 
        
            309
                            flask.flash(Markup( 
        
            310
                                    _("Usernames may only contain Latin alphabet, numbers, '-' and '_'")), 
        
            311
                                    category="error") 
        
            312
                            return flask.render_template("login.html") 
        
            313
                        if username in config.RESERVED_NAMES: 
        
            315
                            flask.flash( 
        
            316
                                    Markup( 
        
            317
                                            "<iconify-icon icon='mdi:account-error'></iconify-icon>" + _( 
        
            318
                                                "Sorry, {username} is a system path").format( 
        
            319
                                                username=username)), 
        
            320
                                    category="error") 
        
            321
                            return flask.render_template("login.html") 
        
            322
                        user_check = User.query.filter_by(username=username).first() 
        
            324
                        if user_check or email2:   # make the honeypot look like a normal error 
        
            325
                            flask.flash( 
        
            326
                                    Markup( 
        
            327
                                            "<iconify-icon icon='mdi:account-error'></iconify-icon>" + _( 
        
            328
                                                "The username {username} is taken").format( 
        
            329
                                                username=username)), 
        
            330
                                    category="error") 
        
            331
                            return flask.render_template("login.html") 
        
            332
                        if password2 != password: 
        
            334
                            flask.flash(Markup("<iconify-icon icon='mdi:key-alert'></iconify-icon>" + _( 
        
            335
                                "Make sure the passwords match")), 
        
            336
                                        category="error") 
        
            337
                            return flask.render_template("login.html") 
        
            338
                        user = User(username, password, email, name) 
        
            340
                        db.session.add(user) 
        
            341
                        db.session.commit() 
        
            342
                        flask.session["username"] = user.username 
        
            343
                        flask.flash(Markup( 
        
            344
                                "<iconify-icon icon='mdi:account'></iconify-icon>" + _( 
        
            345
                                    "Successfully created and logged in as {username}").format( 
        
            346
                                    username=username)), 
        
            347
                                category="success") 
        
            348
                        notification = Notification({"type": "welcome"}) 
        
            350
                        db.session.add(notification) 
        
            351
                        db.session.commit() 
        
            352
                        result = celery_tasks.send_notification.delay(notification.id, [username], 1) 
        
            354
                        return flask.redirect("/", code=303) 
        
            356
            @app.route("/newrepo/", methods=["GET", "POST"]) 
        
            359
            def new_repo(): 
        
            360
                if not flask.session.get("username"): 
        
            361
                    flask.abort(401) 
        
            362
                if flask.request.method == "GET": 
        
            363
                    return flask.render_template("new-repo.html") 
        
            364
                else: 
        
            365
                    name = flask.request.form["name"] 
        
            366
                    visibility = int(flask.request.form["visibility"]) 
        
            367
                    if not only_chars(name, 
        
            369
                                      "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"): 
        
            370
                        flask.flash(Markup( 
        
            371
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _( 
        
            372
                                    "Repository names may only contain Latin alphabet, numbers, '-' and '_'")), 
        
            373
                                category="error") 
        
            374
                        return flask.render_template("new-repo.html") 
        
            375
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            377
                    repo = Repo(user, name, visibility) 
        
            379
                    db.session.add(repo) 
        
            380
                    db.session.commit() 
        
            381
                    flask.flash(Markup(_("Successfully created repository {name}").format(name=name)), 
        
            383
                                category="success") 
        
            384
                    return flask.redirect(repo.route, code=303) 
        
            385
            @app.route("/logout") 
        
            388
            def logout(): 
        
            389
                flask.session.clear() 
        
            390
                flask.flash(Markup( 
        
            391
                    "<iconify-icon icon='mdi:account'></iconify-icon>" + _("Successfully logged out")), 
        
            392
                            category="info") 
        
            393
                return flask.redirect("/", code=303) 
        
            394
            @app.route("/<username>/", methods=["GET", "POST"]) 
        
            397
            def user_profile(username): 
        
            398
                old_relationship = UserFollow.query.filter_by( 
        
            399
                    follower_username=flask.session.get("username"), 
        
            400
                    followed_username=username).first() 
        
            401
                if flask.request.method == "GET": 
        
            402
                    user = User.query.filter_by(username=username).first() 
        
            403
                    match flask.request.args.get("action"): 
        
            404
                        case "repositories": 
        
            405
                            repos = Repo.query.filter_by(owner_name=username, visibility=2) 
        
            406
                            return flask.render_template("user-profile-repositories.html", user=user, 
        
            407
                                                         repos=repos, 
        
            408
                                                         relationship=old_relationship) 
        
            409
                        case "followers": 
        
            410
                            return flask.render_template("user-profile-followers.html", user=user, 
        
            411
                                                         relationship=old_relationship) 
        
            412
                        case "follows": 
        
            413
                            return flask.render_template("user-profile-follows.html", user=user, 
        
            414
                                                         relationship=old_relationship) 
        
            415
                        case _: 
        
            416
                            return flask.render_template("user-profile-overview.html", user=user, 
        
            417
                                                         relationship=old_relationship) 
        
            418
                elif flask.request.method == "POST": 
        
            420
                    match flask.request.args.get("action"): 
        
            421
                        case "follow": 
        
            422
                            if username == flask.session.get("username"): 
        
            423
                                flask.abort(403) 
        
            424
                            if old_relationship: 
        
            425
                                db.session.delete(old_relationship) 
        
            426
                            else: 
        
            427
                                relationship = UserFollow( 
        
            428
                                        flask.session.get("username"), 
        
            429
                                        username 
        
            430
                                ) 
        
            431
                                db.session.add(relationship) 
        
            432
                                db.session.commit() 
        
            433
                                user = db.session.get(User, username) 
        
            435
                                author = db.session.get(User, flask.session.get("username")) 
        
            436
                                notification = Notification({"type": "update", "version": "0.0.0"}) 
        
            437
                                db.session.add(notification) 
        
            438
                                db.session.commit() 
        
            439
                                result = celery_tasks.send_notification.delay(notification.id, [username], 
        
            441
                                                                              1) 
        
            442
                            db.session.commit() 
        
            444
                            return flask.redirect("?", code=303) 
        
            445
            @app.route("/<username>/<repository>/") 
        
            448
            def repository_index(username, repository): 
        
            449
                return flask.redirect("./tree", code=302) 
        
            450
            @app.route("/info/<username>/avatar") 
        
            453
            def user_avatar(username): 
        
            454
                serverUserdataLocation = os.path.join(config.USERDATA_PATH, username) 
        
            455
                if not os.path.exists(serverUserdataLocation): 
        
            457
                    return flask.render_template("not-found.html"), 404 
        
            458
                return flask.send_from_directory(serverUserdataLocation, "avatar.png") 
        
            460
            @app.route("/<username>/<repository>/raw/<branch>/<path:subpath>") 
        
            463
            def repository_raw(username, repository, branch, subpath): 
        
            464
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            465
                if not os.path.exists(server_repo_location): 
        
            466
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            467
                    flask.abort(404) 
        
            468
                if not (get_visibility(username, repository) or get_permission_level( 
        
            469
                        flask.session.get("username"), username, 
        
            470
                        repository) is not None): 
        
            471
                    flask.abort(403) 
        
            472
                app.logger.info(f"Loading {server_repo_location}") 
        
            474
                if not os.path.exists(server_repo_location): 
        
            476
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            477
                    return flask.render_template("not-found.html"), 404 
        
            478
                repo = git.Repo(server_repo_location) 
        
            480
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            481
                if not repo_data.default_branch: 
        
            482
                    if repo.heads: 
        
            483
                        repo_data.default_branch = repo.heads[0].name 
        
            484
                    else: 
        
            485
                        return flask.render_template("empty.html", 
        
            486
                                                     remote=f"http://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            487
                if not branch: 
        
            488
                    branch = repo_data.default_branch 
        
            489
                    return flask.redirect(f"./{branch}", code=302) 
        
            490
                if branch.startswith("tag:"): 
        
            492
                    ref = f"tags/{branch[4:]}" 
        
            493
                elif branch.startswith("~"): 
        
            494
                    ref = branch[1:] 
        
            495
                else: 
        
            496
                    ref = f"heads/{branch}" 
        
            497
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            499
                try: 
        
            501
                    repo.git.checkout("-f", ref) 
        
            502
                except git.exc.GitCommandError: 
        
            503
                    return flask.render_template("not-found.html"), 404 
        
            504
                return flask.send_from_directory(config.REPOS_PATH, 
        
            506
                                                 os.path.join(username, repository, subpath)) 
        
            507
            @repositories.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""}) 
        
            510
            @repositories.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""}) 
        
            511
            @repositories.route("/<username>/<repository>/tree/<branch>/<path:subpath>") 
        
            512
            def repository_tree(username, repository, branch, subpath): 
        
            513
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            514
                if not os.path.exists(server_repo_location): 
        
            515
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            516
                    flask.abort(404) 
        
            517
                if not (get_visibility(username, repository) or get_permission_level( 
        
            518
                        flask.session.get("username"), username, 
        
            519
                        repository) is not None): 
        
            520
                    flask.abort(403) 
        
            521
                app.logger.info(f"Loading {server_repo_location}") 
        
            523
                repo = git.Repo(server_repo_location) 
        
            525
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            526
                if not repo_data.default_branch: 
        
            527
                    if repo.heads: 
        
            528
                        repo_data.default_branch = repo.heads[0].name 
        
            529
                    else: 
        
            530
                        return flask.render_template("empty.html", 
        
            531
                                                     remote=f"{config.www_protocol}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            532
                if not branch: 
        
            533
                    branch = repo_data.default_branch 
        
            534
                    return flask.redirect(f"./{branch}", code=302) 
        
            535
                if branch.startswith("tag:"): 
        
            537
                    ref = f"tags/{branch[4:]}" 
        
            538
                elif branch.startswith("~"): 
        
            539
                    ref = branch[1:] 
        
            540
                else: 
        
            541
                    ref = f"heads/{branch}" 
        
            542
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            544
                try: 
        
            546
                    repo.git.checkout("-f", ref) 
        
            547
                except git.exc.GitCommandError: 
        
            548
                    return flask.render_template("not-found.html"), 404 
        
            549
                branches = repo.heads 
        
            551
                all_refs = [] 
        
            553
                for ref in repo.heads: 
        
            554
                    all_refs.append((ref, "head")) 
        
            555
                for ref in repo.tags: 
        
            556
                    all_refs.append((ref, "tag")) 
        
            557
                if os.path.isdir(os.path.join(server_repo_location, subpath)): 
        
            559
                    files = [] 
        
            560
                    blobs = [] 
        
            561
                    for entry in os.listdir(os.path.join(server_repo_location, subpath)): 
        
            563
                        if not os.path.basename(entry) == ".git": 
        
            564
                            files.append(os.path.join(subpath, entry)) 
        
            565
                    infos = [] 
        
            567
                    for file in files: 
        
            569
                        path = os.path.join(server_repo_location, file) 
        
            570
                        mimetype = guess_mime(path) 
        
            571
                        text = git_command(server_repo_location, None, "log", "--format='%H\n'", 
        
            573
                                           shlex.quote(file)).decode() 
        
            574
                        sha = text.split("\n")[0] 
        
            576
                        identifier = f"/{username}/{repository}/{sha}" 
        
            577
                        last_commit = db.session.get(Commit, identifier) 
        
            579
                        info = { 
        
            581
                            "name": os.path.basename(file), 
        
            582
                            "serverPath": path, 
        
            583
                            "relativePath": file, 
        
            584
                            "link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file), 
        
            585
                            "size": human_size(os.path.getsize(path)), 
        
            586
                            "mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}", 
        
            587
                            "commit": last_commit, 
        
            588
                            "shaSize": 7, 
        
            589
                        } 
        
            590
                        special_icon = config.match_icon(os.path.basename(file)) 
        
            592
                        if special_icon: 
        
            593
                            info["icon"] = special_icon 
        
            594
                        elif os.path.isdir(path): 
        
            595
                            info["icon"] = config.folder_icon 
        
            596
                        elif mimetypes.guess_type(path)[0] in config.file_icons: 
        
            597
                            info["icon"] = config.file_icons[mimetypes.guess_type(path)[0]] 
        
            598
                        else: 
        
            599
                            info["icon"] = config.unknown_icon 
        
            600
                        if os.path.isdir(path): 
        
            602
                            infos.insert(0, info) 
        
            603
                        else: 
        
            604
                            infos.append(info) 
        
            605
                    return flask.render_template( 
        
            607
                            "repo-tree.html", 
        
            608
                            username=username, 
        
            609
                            repository=repository, 
        
            610
                            files=infos, 
        
            611
                            subpath=os.path.join("/", subpath), 
        
            612
                            branches=all_refs, 
        
            613
                            current=branch, 
        
            614
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            615
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            616
                    ) 
        
            617
                else: 
        
            618
                    path = os.path.join(server_repo_location, subpath) 
        
            619
                    if not os.path.exists(path): 
        
            621
                        return flask.render_template("not-found.html"), 404 
        
            622
                    mimetype = guess_mime(path) 
        
            624
                    mode = mimetype.split("/", 1)[0] 
        
            625
                    size = human_size(os.path.getsize(path)) 
        
            626
                    special_icon = config.match_icon(os.path.basename(path)) 
        
            628
                    if special_icon: 
        
            629
                        icon = special_icon 
        
            630
                    elif os.path.isdir(path): 
        
            631
                        icon = config.folder_icon 
        
            632
                    elif mimetypes.guess_type(path)[0] in config.file_icons: 
        
            633
                        icon = config.file_icons[mimetypes.guess_type(path)[0]] 
        
            634
                    else: 
        
            635
                        icon = config.unknown_icon 
        
            636
                    contents = None 
        
            638
                    if mode == "text": 
        
            639
                        contents = convert_to_html(path) 
        
            640
                    return flask.render_template( 
        
            642
                            "repo-file.html", 
        
            643
                            username=username, 
        
            644
                            repository=repository, 
        
            645
                            file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath), 
        
            646
                            branches=all_refs, 
        
            647
                            current=branch, 
        
            648
                            mode=mode, 
        
            649
                            mimetype=mimetype, 
        
            650
                            detailedtype=magic.from_file(path), 
        
            651
                            size=size, 
        
            652
                            icon=icon, 
        
            653
                            subpath=os.path.join("/", subpath), 
        
            654
                            extension=pathlib.Path(path).suffix, 
        
            655
                            basename=os.path.basename(path), 
        
            656
                            contents=contents, 
        
            657
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            658
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            659
                    ) 
        
            660
            @repositories.route("/<username>/<repository>/commit/<sha>") 
        
            663
            def repository_commit(username, repository, sha): 
        
            664
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            665
                if not os.path.exists(server_repo_location): 
        
            666
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            667
                    flask.abort(404) 
        
            668
                if not (get_visibility(username, repository) or get_permission_level( 
        
            669
                        flask.session.get("username"), username, 
        
            670
                        repository) is not None): 
        
            671
                    flask.abort(403) 
        
            672
                app.logger.info(f"Loading {server_repo_location}") 
        
            674
                if not os.path.exists(server_repo_location): 
        
            676
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            677
                    return flask.render_template("not-found.html"), 404 
        
            678
                repo = git.Repo(server_repo_location) 
        
            680
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            681
                files = git_command(os.path.join(server_repo_location, ".git"), None, "diff-tree", "-r", 
        
            683
                                    "--name-only", "--no-commit-id", sha).decode().split("\n")[:-1] 
        
            684
                print(files) 
        
            686
                return flask.render_template( 
        
            688
                        "repo-commit.html", 
        
            689
                        username=username, 
        
            690
                        repository=repository, 
        
            691
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            692
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            693
                        diff={file: git_command(os.path.join(server_repo_location, ".git"), None, "diff", 
        
            694
                                                str(sha) + "^!", "--", file).decode().split("\n") for 
        
            695
                              file in files}, 
        
            696
                        data=db.session.get(Commit, f"/{username}/{repository}/{sha}"), 
        
            697
                ) 
        
            698
            @repositories.route("/<username>/<repository>/forum/") 
        
            701
            def repository_forum(username, repository): 
        
            702
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            703
                if not os.path.exists(server_repo_location): 
        
            704
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            705
                    flask.abort(404) 
        
            706
                if not (get_visibility(username, repository) or get_permission_level( 
        
            707
                        flask.session.get("username"), username, 
        
            708
                        repository) is not None): 
        
            709
                    flask.abort(403) 
        
            710
                app.logger.info(f"Loading {server_repo_location}") 
        
            712
                if not os.path.exists(server_repo_location): 
        
            714
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            715
                    return flask.render_template("not-found.html"), 404 
        
            716
                repo = git.Repo(server_repo_location) 
        
            718
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            719
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            720
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            721
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            722
                return flask.render_template( 
        
            724
                        "repo-forum.html", 
        
            725
                        username=username, 
        
            726
                        repository=repository, 
        
            727
                        repo_data=repo_data, 
        
            728
                        relationships=relationships, 
        
            729
                        repo=repo, 
        
            730
                        user_relationship=user_relationship, 
        
            731
                        Post=Post, 
        
            732
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            733
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            734
                        default_branch=repo_data.default_branch 
        
            735
                ) 
        
            736
            @repositories.route("/<username>/<repository>/forum/topic/<int:id>") 
        
            739
            def repository_forum_topic(username, repository, id): 
        
            740
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            741
                if not os.path.exists(server_repo_location): 
        
            742
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            743
                    flask.abort(404) 
        
            744
                if not (get_visibility(username, repository) or get_permission_level( 
        
            745
                        flask.session.get("username"), username, 
        
            746
                        repository) is not None): 
        
            747
                    flask.abort(403) 
        
            748
                app.logger.info(f"Loading {server_repo_location}") 
        
            750
                if not os.path.exists(server_repo_location): 
        
            752
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            753
                    return flask.render_template("not-found.html"), 404 
        
            754
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            756
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            757
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            758
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            759
                post = Post.query.filter_by(id=id).first() 
        
            761
                return flask.render_template( 
        
            763
                        "repo-topic.html", 
        
            764
                        username=username, 
        
            765
                        repository=repository, 
        
            766
                        repo_data=repo_data, 
        
            767
                        relationships=relationships, 
        
            768
                        user_relationship=user_relationship, 
        
            769
                        post=post, 
        
            770
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            771
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            772
                        default_branch=repo_data.default_branch 
        
            773
                ) 
        
            774
            @repositories.route("/<username>/<repository>/forum/new", methods=["POST", "GET"]) 
        
            777
            def repository_forum_new(username, repository): 
        
            778
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            779
                if not os.path.exists(server_repo_location): 
        
            780
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            781
                    flask.abort(404) 
        
            782
                if not (get_visibility(username, repository) or get_permission_level( 
        
            783
                        flask.session.get("username"), username, 
        
            784
                        repository) is not None): 
        
            785
                    flask.abort(403) 
        
            786
                app.logger.info(f"Loading {server_repo_location}") 
        
            788
                if not os.path.exists(server_repo_location): 
        
            790
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            791
                    return flask.render_template("not-found.html"), 404 
        
            792
                repo = git.Repo(server_repo_location) 
        
            794
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            795
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            796
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            797
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            798
                post = Post(user, repo_data, None, flask.request.form["subject"], 
        
            800
                            flask.request.form["message"]) 
        
            801
                db.session.add(post) 
        
            803
                db.session.commit() 
        
            804
                return flask.redirect( 
        
            806
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            807
                                      post_id=post.number), 
        
            808
                        code=303) 
        
            809
            @repositories.route("/<username>/<repository>/forum/<int:post_id>") 
        
            812
            def repository_forum_thread(username, repository, post_id): 
        
            813
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            814
                if not os.path.exists(server_repo_location): 
        
            815
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            816
                    flask.abort(404) 
        
            817
                if not (get_visibility(username, repository) or get_permission_level( 
        
            818
                        flask.session.get("username"), username, 
        
            819
                        repository) is not None): 
        
            820
                    flask.abort(403) 
        
            821
                app.logger.info(f"Loading {server_repo_location}") 
        
            823
                if not os.path.exists(server_repo_location): 
        
            825
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            826
                    return flask.render_template("not-found.html"), 404 
        
            827
                repo = git.Repo(server_repo_location) 
        
            829
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            830
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            831
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            832
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            833
                return flask.render_template( 
        
            835
                        "repo-forum-thread.html", 
        
            836
                        username=username, 
        
            837
                        repository=repository, 
        
            838
                        repo_data=repo_data, 
        
            839
                        relationships=relationships, 
        
            840
                        repo=repo, 
        
            841
                        Post=Post, 
        
            842
                        user_relationship=user_relationship, 
        
            843
                        post_id=post_id, 
        
            844
                        max_post_nesting=4, 
        
            845
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            846
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            847
                        parent=Post.query.filter_by(repo=repo_data, number=post_id).first(), 
        
            848
                ) 
        
            849
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/change-state", methods=["POST"]) 
        
            852
            def repository_forum_change_state(username, repository, post_id): 
        
            853
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            854
                if not os.path.exists(server_repo_location): 
        
            855
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            856
                    flask.abort(404) 
        
            857
                if not (get_visibility(username, repository) or get_permission_level( 
        
            858
                        flask.session.get("username"), username, 
        
            859
                        repository) is not None): 
        
            860
                    flask.abort(403) 
        
            861
                app.logger.info(f"Loading {server_repo_location}") 
        
            863
                repo = git.Repo(server_repo_location) 
        
            865
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            866
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            867
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            868
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            869
                post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            871
                if not post: 
        
            873
                    flask.abort(404) 
        
            874
                post.state = int(flask.request.form["new-state"]) 
        
            876
                db.session.commit() 
        
            878
                return flask.redirect( 
        
            880
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            881
                                      post_id=post_id), 
        
            882
                        code=303) 
        
            883
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/reply", methods=["POST"]) 
        
            886
            def repository_forum_reply(username, repository, post_id): 
        
            887
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            888
                if not os.path.exists(server_repo_location): 
        
            889
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            890
                    flask.abort(404) 
        
            891
                if not (get_visibility(username, repository) or get_permission_level( 
        
            892
                        flask.session.get("username"), username, 
        
            893
                        repository) is not None): 
        
            894
                    flask.abort(403) 
        
            895
                app.logger.info(f"Loading {server_repo_location}") 
        
            897
                if not os.path.exists(server_repo_location): 
        
            899
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            900
                    return flask.render_template("not-found.html"), 404 
        
            901
                repo = git.Repo(server_repo_location) 
        
            903
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            904
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            905
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            906
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            907
                if not user: 
        
            908
                    flask.abort(401) 
        
            909
                parent = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            911
                post = Post(user, repo_data, parent, flask.request.form["subject"], 
        
            912
                            flask.request.form["message"]) 
        
            913
                db.session.add(post) 
        
            915
                post.update_date() 
        
            916
                db.session.commit() 
        
            917
                return flask.redirect( 
        
            919
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            920
                                      post_id=post_id), 
        
            921
                        code=303) 
        
            922
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/voteup", 
        
            925
                                defaults={"score": 1}) 
        
            926
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/votedown", 
        
            927
                                defaults={"score": -1}) 
        
            928
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/votes", defaults={"score": 0}) 
        
            929
            def repository_forum_vote(username, repository, post_id, score): 
        
            930
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            931
                if not os.path.exists(server_repo_location): 
        
            932
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            933
                    flask.abort(404) 
        
            934
                if not (get_visibility(username, repository) or get_permission_level( 
        
            935
                        flask.session.get("username"), username, 
        
            936
                        repository) is not None): 
        
            937
                    flask.abort(403) 
        
            938
                app.logger.info(f"Loading {server_repo_location}") 
        
            940
                if not os.path.exists(server_repo_location): 
        
            942
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            943
                    return flask.render_template("not-found.html"), 404 
        
            944
                repo = git.Repo(server_repo_location) 
        
            946
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            947
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            948
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            949
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            950
                if not user: 
        
            951
                    flask.abort(401) 
        
            952
                post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            954
                if score: 
        
            956
                    old_relationship = PostVote.query.filter_by(user_username=user.username, 
        
            957
                                                                post_identifier=post.identifier).first() 
        
            958
                    if old_relationship: 
        
            959
                        if score == old_relationship.vote_score: 
        
            960
                            db.session.delete(old_relationship) 
        
            961
                            post.vote_sum -= old_relationship.vote_score 
        
            962
                        else: 
        
            963
                            post.vote_sum -= old_relationship.vote_score 
        
            964
                            post.vote_sum += score 
        
            965
                            old_relationship.vote_score = score 
        
            966
                    else: 
        
            967
                        relationship = PostVote(user, post, score) 
        
            968
                        post.vote_sum += score 
        
            969
                        db.session.add(relationship) 
        
            970
                    db.session.commit() 
        
            972
                user_vote = PostVote.query.filter_by(user_username=user.username, 
        
            974
                                                     post_identifier=post.identifier).first() 
        
            975
                response = flask.make_response( 
        
            976
                    str(post.vote_sum) + " " + str(user_vote.vote_score if user_vote else 0)) 
        
            977
                response.content_type = "text/plain" 
        
            978
                return response 
        
            980
            @repositories.route("/<username>/<repository>/favourite") 
        
            983
            def repository_favourite(username, repository): 
        
            984
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            985
                if not os.path.exists(server_repo_location): 
        
            986
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            987
                    flask.abort(404) 
        
            988
                if not (get_visibility(username, repository) or get_permission_level( 
        
            989
                        flask.session.get("username"), username, 
        
            990
                        repository) is not None): 
        
            991
                    flask.abort(403) 
        
            992
                app.logger.info(f"Loading {server_repo_location}") 
        
            994
                if not os.path.exists(server_repo_location): 
        
            996
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            997
                    return flask.render_template("not-found.html"), 404 
        
            998
                repo = git.Repo(server_repo_location) 
        
            1000
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1001
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1002
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            1003
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            1004
                if not user: 
        
            1005
                    flask.abort(401) 
        
            1006
                old_relationship = RepoFavourite.query.filter_by(user_username=user.username, 
        
            1008
                                                                 repo_route=repo_data.route).first() 
        
            1009
                if old_relationship: 
        
            1010
                    db.session.delete(old_relationship) 
        
            1011
                else: 
        
            1012
                    relationship = RepoFavourite(user, repo_data) 
        
            1013
                    db.session.add(relationship) 
        
            1014
                db.session.commit() 
        
            1016
                return flask.redirect(flask.url_for("favourites"), code=303) 
        
            1018
            @repositories.route("/<username>/<repository>/users/", methods=["GET", "POST"]) 
        
            1021
            def repository_users(username, repository): 
        
            1022
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1023
                if not os.path.exists(server_repo_location): 
        
            1024
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1025
                    flask.abort(404) 
        
            1026
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1027
                        flask.session.get("username"), username, 
        
            1028
                        repository) is not None): 
        
            1029
                    flask.abort(403) 
        
            1030
                app.logger.info(f"Loading {server_repo_location}") 
        
            1032
                if not os.path.exists(server_repo_location): 
        
            1034
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1035
                    return flask.render_template("not-found.html"), 404 
        
            1036
                repo = git.Repo(server_repo_location) 
        
            1038
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1039
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1040
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            1041
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            1042
                if flask.request.method == "GET": 
        
            1044
                    return flask.render_template( 
        
            1045
                            "repo-users.html", 
        
            1046
                            username=username, 
        
            1047
                            repository=repository, 
        
            1048
                            repo_data=repo_data, 
        
            1049
                            relationships=relationships, 
        
            1050
                            repo=repo, 
        
            1051
                            user_relationship=user_relationship, 
        
            1052
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1053
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1054
                    ) 
        
            1055
                else: 
        
            1056
                    if get_permission_level(flask.session.get("username"), username, repository) != 2: 
        
            1057
                        flask.abort(401) 
        
            1058
                    if flask.request.form.get("new-username"): 
        
            1060
                        # Create new relationship 
        
            1061
                        new_user = User.query.filter_by( 
        
            1062
                            username=flask.request.form.get("new-username")).first() 
        
            1063
                        relationship = RepoAccess(new_user, repo_data, flask.request.form.get("new-level")) 
        
            1064
                        db.session.add(relationship) 
        
            1065
                        db.session.commit() 
        
            1066
                    if flask.request.form.get("update-username"): 
        
            1067
                        # Create new relationship 
        
            1068
                        updated_user = User.query.filter_by( 
        
            1069
                            username=flask.request.form.get("update-username")).first() 
        
            1070
                        relationship = RepoAccess.query.filter_by(repo=repo_data, user=updated_user).first() 
        
            1071
                        if flask.request.form.get("update-level") == -1: 
        
            1072
                            relationship.delete() 
        
            1073
                        else: 
        
            1074
                            relationship.access_level = flask.request.form.get("update-level") 
        
            1075
                        db.session.commit() 
        
            1076
                    return flask.redirect( 
        
            1078
                        app.url_for(".repository_users", username=username, repository=repository)) 
        
            1079
            @repositories.route("/<username>/<repository>/branches/") 
        
            1082
            def repository_branches(username, repository): 
        
            1083
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1084
                if not os.path.exists(server_repo_location): 
        
            1085
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1086
                    flask.abort(404) 
        
            1087
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1088
                        flask.session.get("username"), username, 
        
            1089
                        repository) is not None): 
        
            1090
                    flask.abort(403) 
        
            1091
                app.logger.info(f"Loading {server_repo_location}") 
        
            1093
                if not os.path.exists(server_repo_location): 
        
            1095
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1096
                    return flask.render_template("not-found.html"), 404 
        
            1097
                repo = git.Repo(server_repo_location) 
        
            1099
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1100
                return flask.render_template( 
        
            1102
                        "repo-branches.html", 
        
            1103
                        username=username, 
        
            1104
                        repository=repository, 
        
            1105
                        repo_data=repo_data, 
        
            1106
                        repo=repo, 
        
            1107
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1108
                        is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1109
                ) 
        
            1110
            @repositories.route("/<username>/<repository>/log/", defaults={"branch": None}) 
        
            1113
            @repositories.route("/<username>/<repository>/log/<branch>/") 
        
            1114
            def repository_log(username, repository, branch): 
        
            1115
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1116
                if not os.path.exists(server_repo_location): 
        
            1117
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1118
                    flask.abort(404) 
        
            1119
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1120
                        flask.session.get("username"), username, 
        
            1121
                        repository) is not None): 
        
            1122
                    flask.abort(403) 
        
            1123
                app.logger.info(f"Loading {server_repo_location}") 
        
            1125
                if not os.path.exists(server_repo_location): 
        
            1127
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1128
                    return flask.render_template("not-found.html"), 404 
        
            1129
                repo = git.Repo(server_repo_location) 
        
            1131
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1132
                if not repo_data.default_branch: 
        
            1133
                    if repo.heads: 
        
            1134
                        repo_data.default_branch = repo.heads[0].name 
        
            1135
                    else: 
        
            1136
                        return flask.render_template("empty.html", 
        
            1137
                                                     remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            1138
                if not branch: 
        
            1139
                    branch = repo_data.default_branch 
        
            1140
                    return flask.redirect(f"./{branch}", code=302) 
        
            1141
                if branch.startswith("tag:"): 
        
            1143
                    ref = f"tags/{branch[4:]}" 
        
            1144
                elif branch.startswith("~"): 
        
            1145
                    ref = branch[1:] 
        
            1146
                else: 
        
            1147
                    ref = f"heads/{branch}" 
        
            1148
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            1150
                try: 
        
            1152
                    repo.git.checkout("-f", ref) 
        
            1153
                except git.exc.GitCommandError: 
        
            1154
                    return flask.render_template("not-found.html"), 404 
        
            1155
                branches = repo.heads 
        
            1157
                all_refs = [] 
        
            1159
                for ref in repo.heads: 
        
            1160
                    all_refs.append((ref, "head")) 
        
            1161
                for ref in repo.tags: 
        
            1162
                    all_refs.append((ref, "tag")) 
        
            1163
                commit_list = [f"/{username}/{repository}/{sha}" for sha in 
        
            1165
                               git_command(server_repo_location, None, "log", 
        
            1166
                                           "--format='%H'").decode().split("\n")] 
        
            1167
                commits = Commit.query.filter(Commit.identifier.in_(commit_list)) 
        
            1169
                return flask.render_template( 
        
            1171
                        "repo-log.html", 
        
            1172
                        username=username, 
        
            1173
                        repository=repository, 
        
            1174
                        branches=all_refs, 
        
            1175
                        current=branch, 
        
            1176
                        repo_data=repo_data, 
        
            1177
                        repo=repo, 
        
            1178
                        commits=commits, 
        
            1179
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1180
                        is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1181
                ) 
        
            1182
            @repositories.route("/<username>/<repository>/prs/", methods=["GET", "POST"]) 
        
            1185
            def repository_prs(username, repository): 
        
            1186
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1187
                if not os.path.exists(server_repo_location): 
        
            1188
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1189
                    flask.abort(404) 
        
            1190
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1191
                        flask.session.get("username"), username, 
        
            1192
                        repository) is not None): 
        
            1193
                    flask.abort(403) 
        
            1194
                app.logger.info(f"Loading {server_repo_location}") 
        
            1196
                if not os.path.exists(server_repo_location): 
        
            1198
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1199
                    return flask.render_template("not-found.html"), 404 
        
            1200
                if flask.request.method == "GET": 
        
            1202
                    repo = git.Repo(server_repo_location) 
        
            1203
                    repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1204
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1205
                    return flask.render_template( 
        
            1207
                            "repo-prs.html", 
        
            1208
                            username=username, 
        
            1209
                            repository=repository, 
        
            1210
                            repo_data=repo_data, 
        
            1211
                            repo=repo, 
        
            1212
                            PullRequest=PullRequest, 
        
            1213
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1214
                            is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            1215
                            default_branch=repo_data.default_branch, 
        
            1216
                            branches=repo.branches 
        
            1217
                    ) 
        
            1218
                else: 
        
            1220
                    repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1221
                    head = flask.request.form.get("head") 
        
            1222
                    head_route = flask.request.form.get("headroute") 
        
            1223
                    base = flask.request.form.get("base") 
        
            1224
                    if not head and base and head_route: 
        
            1226
                        return flask.redirect(".", 400) 
        
            1227
                    head_repo = git.Repo(os.path.join(config.REPOS_PATH, head_route.lstrip("/"))) 
        
            1229
                    base_repo = git.Repo(server_repo_location) 
        
            1230
                    print(head_repo) 
        
            1231
                    if head not in head_repo.branches or base not in base_repo.branches: 
        
            1233
                        flask.flash(Markup( 
        
            1234
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _("Bad branch name")), 
        
            1235
                                category="error") 
        
            1236
                        return flask.redirect(".", 303) 
        
            1237
                    head_data = db.session.get(Repo, head_route) 
        
            1239
                    if not head_data.visibility: 
        
            1240
                        flask.flash(Markup( 
        
            1241
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _( 
        
            1242
                                    "Head can't be restricted")), 
        
            1243
                                category="error") 
        
            1244
                        return flask.redirect(".", 303) 
        
            1245
                    pull_request = PullRequest(repo_data, head, head_data, base, 
        
            1247
                                               db.session.get(User, flask.session["username"])) 
        
            1248
                    db.session.add(pull_request) 
        
            1250
                    db.session.commit() 
        
            1251
                    return flask.redirect(".", 303) 
        
            1253
            @repositories.route("/<username>/<repository>/prs/merge", methods=["POST"]) 
        
            1256
            def repository_prs_merge(username, repository): 
        
            1257
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1258
                if not os.path.exists(server_repo_location): 
        
            1259
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1260
                    flask.abort(404) 
        
            1261
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1262
                        flask.session.get("username"), username, 
        
            1263
                        repository) is not None): 
        
            1264
                    flask.abort(403) 
        
            1265
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1267
                    flask.abort(401) 
        
            1268
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1270
                repo = git.Repo(server_repo_location) 
        
            1271
                id = flask.request.form.get("id") 
        
            1272
                pull_request = db.session.get(PullRequest, id) 
        
            1274
                if pull_request: 
        
            1276
                    result = celery_tasks.merge_heads.delay( 
        
            1277
                            pull_request.head_route, 
        
            1278
                            pull_request.head_branch, 
        
            1279
                            pull_request.base_route, 
        
            1280
                            pull_request.base_branch, 
        
            1281
                            simulate=True 
        
            1282
                    ) 
        
            1283
                    task_result = worker.AsyncResult(result.id) 
        
            1284
                    return flask.redirect(f"/task/{result.id}?pr-id={id}", 303) 
        
            1286
                    # db.session.delete(pull_request) 
        
            1287
                    # db.session.commit() 
        
            1288
                else: 
        
            1289
                    flask.abort(400) 
        
            1290
            @repositories.route("/<username>/<repository>/prs/<int:id>/merge") 
        
            1293
            def repository_prs_merge_stage_two(username, repository, id): 
        
            1294
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1295
                if not os.path.exists(server_repo_location): 
        
            1296
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1297
                    flask.abort(404) 
        
            1298
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1299
                        flask.session.get("username"), username, 
        
            1300
                        repository) is not None): 
        
            1301
                    flask.abort(403) 
        
            1302
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1304
                    flask.abort(401) 
        
            1305
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1307
                repo = git.Repo(server_repo_location) 
        
            1308
                pull_request = db.session.get(PullRequest, id) 
        
            1310
                if pull_request: 
        
            1312
                    result = celery_tasks.merge_heads.delay( 
        
            1313
                            pull_request.head_route, 
        
            1314
                            pull_request.head_branch, 
        
            1315
                            pull_request.base_route, 
        
            1316
                            pull_request.base_branch, 
        
            1317
                            simulate=False 
        
            1318
                    ) 
        
            1319
                    task_result = worker.AsyncResult(result.id) 
        
            1320
                    pull_request.state = 1 
        
            1322
                    db.session.commit() 
        
            1323
                    return flask.redirect(f"/task/{result.id}?pr-id={id}", 303) 
        
            1325
                    # db.session.delete(pull_request) 
        
            1326
                else: 
        
            1327
                    flask.abort(400) 
        
            1328
            @app.route("/task/<task_id>") 
        
            1331
            def task_monitor(task_id): 
        
            1332
                task_result = worker.AsyncResult(task_id) 
        
            1333
                print(task_result.status) 
        
            1334
                return flask.render_template("task-monitor.html", result=task_result) 
        
            1336
            @repositories.route("/<username>/<repository>/prs/delete", methods=["POST"]) 
        
            1339
            def repository_prs_delete(username, repository): 
        
            1340
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1341
                if not os.path.exists(server_repo_location): 
        
            1342
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1343
                    flask.abort(404) 
        
            1344
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1345
                        flask.session.get("username"), username, 
        
            1346
                        repository) is not None): 
        
            1347
                    flask.abort(403) 
        
            1348
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1350
                    flask.abort(401) 
        
            1351
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1353
                repo = git.Repo(server_repo_location) 
        
            1354
                id = flask.request.form.get("id") 
        
            1355
                pull_request = db.session.get(PullRequest, id) 
        
            1357
                if pull_request: 
        
            1359
                    pull_request.state = 2 
        
            1360
                    db.session.commit() 
        
            1361
                return flask.redirect(".", 303) 
        
            1363
            @repositories.route("/<username>/<repository>/settings/") 
        
            1366
            def repository_settings(username, repository): 
        
            1367
                if get_permission_level(flask.session.get("username"), username, repository) != 2: 
        
            1368
                    flask.abort(401) 
        
            1369
                return flask.render_template("repo-settings.html", username=username, repository=repository) 
        
            1371
            @app.errorhandler(404) 
        
            1374
            def e404(error): 
        
            1375
                return flask.render_template("not-found.html"), 404 
        
            1376
            @app.errorhandler(401) 
        
            1379
            def e401(error): 
        
            1380
                return flask.render_template("unauthorised.html"), 401 
        
            1381
            @app.errorhandler(403) 
        
            1384
            def e403(error): 
        
            1385
                return flask.render_template("forbidden.html"), 403 
        
            1386
            @app.errorhandler(418) 
        
            1389
            def e418(error): 
        
            1390
                return flask.render_template("teapot.html"), 418 
        
            1391
            @app.errorhandler(405) 
        
            1394
            def e405(error): 
        
            1395
                return flask.render_template("method-not-allowed.html"), 405 
        
            1396
            if __name__ == "__main__": 
        
            1399
                app.run(debug=True, port=8080, host="0.0.0.0") 
        
            1400
            app.register_blueprint(repositories)