Python script, Unicode text, UTF-8 text executable
        
            1
            __version__ = "0.2.0" 
        
            2
            import os 
        
            4
            import shutil 
        
            5
            import random 
        
            6
            import subprocess 
        
            7
            import platform 
        
            8
            import PIL 
        
            10
            import git 
        
            11
            import mimetypes 
        
            12
            import magic 
        
            13
            import flask 
        
            14
            import cairosvg 
        
            15
            import celery 
        
            16
            import shlex 
        
            17
            from functools import wraps 
        
            18
            from datetime import datetime 
        
            19
            from enum import Enum 
        
            20
            from cairosvg import svg2png 
        
            21
            from flask_sqlalchemy import SQLAlchemy 
        
            22
            from flask_bcrypt import Bcrypt 
        
            23
            from markupsafe import escape, Markup 
        
            24
            from flask_migrate import Migrate 
        
            25
            from PIL import Image 
        
            26
            from flask_httpauth import HTTPBasicAuth 
        
            27
            import config 
        
            28
            from flask_babel import Babel, gettext, ngettext, force_locale 
        
            29
            _ = gettext 
        
            31
            n_ = gettext 
        
            32
            app = flask.Flask(__name__) 
        
            34
            app.config.from_mapping( 
        
            35
                    CELERY=dict( 
        
            36
                            broker_url=config.REDIS_URI, 
        
            37
                            result_backend=config.REDIS_URI, 
        
            38
                            task_ignore_result=True, 
        
            39
                    ), 
        
            40
            ) 
        
            41
            auth = HTTPBasicAuth() 
        
            43
            app.config["SQLALCHEMY_DATABASE_URI"] = config.DB_URI 
        
            45
            app.config["SECRET_KEY"] = config.DB_PASSWORD 
        
            46
            app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False 
        
            47
            app.config["BABEL_TRANSLATION_DIRECTORIES"] = "i18n" 
        
            48
            app.config["MAX_CONTENT_LENGTH"] = config.MAX_PAYLOAD_SIZE 
        
            49
            db = SQLAlchemy(app) 
        
            51
            bcrypt = Bcrypt(app) 
        
            52
            migrate = Migrate(app, db) 
        
            53
            from misc_utils import * 
        
            55
            import git_http 
        
            57
            import jinja_utils 
        
            58
            import celery_tasks 
        
            59
            from celery import Celery, Task 
        
            60
            import celery_integration 
        
            61
            import pathlib 
        
            62
            from models import * 
        
            64
            babel = Babel(app) 
        
            66
            def get_locale(): 
        
            69
                if flask.request.cookies.get("language"): 
        
            70
                    return flask.request.cookies.get("language") 
        
            71
                return flask.request.accept_languages.best_match(config.available_locales) 
        
            72
            babel.init_app(app, locale_selector=get_locale) 
        
            75
            with app.app_context(): 
        
            77
                locale_names = {} 
        
            78
                for language in config.available_locales: 
        
            79
                    with force_locale(language): 
        
            80
                        # NOTE: Translate this to the language's name in that language, for example in French you would use français 
        
            81
                        locale_names[language] = gettext("English") 
        
            82
            worker = celery_integration.init_celery_app(app) 
        
            84
            repositories = flask.Blueprint("repository", __name__, template_folder="templates/repository/") 
        
            86
            app.jinja_env.add_extension("jinja2.ext.do") 
        
            88
            app.jinja_env.add_extension("jinja2.ext.loopcontrols") 
        
            89
            app.jinja_env.add_extension("jinja2.ext.debug") 
        
            90
            @app.context_processor 
        
            93
            def default(): 
        
            94
                username = flask.session.get("username") 
        
            95
                user_object = User.query.filter_by(username=username).first() 
        
            97
                return { 
        
            99
                    "logged_in_user": username, 
        
            100
                    "user_object": user_object, 
        
            101
                    "Notification": Notification, 
        
            102
                    "unread": UserNotification.query.filter_by(user_username=username).filter( 
        
            103
                            UserNotification.attention_level > 0).count(), 
        
            104
                    "config": config, 
        
            105
                    "Markup": Markup, 
        
            106
                    "locale_names": locale_names, 
        
            107
                } 
        
            108
            @app.route("/") 
        
            111
            def main(): 
        
            112
                if flask.session.get("username"): 
        
            113
                    return flask.render_template("home.html") 
        
            114
                else: 
        
            115
                    return flask.render_template("no-home.html") 
        
            116
            @app.route("/userstyle") 
        
            119
            def userstyle(): 
        
            120
                if flask.session.get("username") and os.path.exists( 
        
            121
                        os.path.join(config.REPOS_PATH, flask.session.get("username"), ".config", 
        
            122
                                     "theme.css")): 
        
            123
                    return flask.send_from_directory( 
        
            124
                        os.path.join(config.REPOS_PATH, flask.session.get("username"), ".config"), 
        
            125
                        "theme.css") 
        
            126
                else: 
        
            127
                    return flask.Response("", mimetype="text/css") 
        
            128
            @app.route("/about/") 
        
            131
            def about(): 
        
            132
                return flask.render_template("about.html", platform=platform, version=__version__) 
        
            133
            @app.route("/language", methods=["POST"]) 
        
            136
            def set_locale(): 
        
            137
                response = flask.redirect(flask.request.referrer if flask.request.referrer else "/", 
        
            138
                                          code=303) 
        
            139
                if not flask.request.form.get("language"): 
        
            140
                    response.delete_cookie("language") 
        
            141
                else: 
        
            142
                    response.set_cookie("language", flask.request.form.get("language")) 
        
            143
                return response 
        
            145
            @app.route("/cookie-dismiss") 
        
            148
            def dismiss_banner(): 
        
            149
                response = flask.redirect(flask.request.referrer if flask.request.referrer else "/", 
        
            150
                                          code=303) 
        
            151
                response.set_cookie("cookie-banner", "1") 
        
            152
                return response 
        
            153
            @app.route("/help/") 
        
            156
            def help_index(): 
        
            157
                return flask.render_template("help.html", faqs=config.faqs) 
        
            158
            @app.route("/settings/", methods=["GET", "POST"]) 
        
            161
            def settings(): 
        
            162
                if not flask.session.get("username"): 
        
            163
                    flask.abort(401) 
        
            164
                if flask.request.method == "GET": 
        
            165
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            166
                    return flask.render_template("user-settings.html", user=user) 
        
            168
                else: 
        
            169
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            170
                    user.display_name = flask.request.form["displayname"] 
        
            172
                    user.URL = flask.request.form["url"] 
        
            173
                    user.company = flask.request.form["company"] 
        
            174
                    user.company_URL = flask.request.form["companyurl"] 
        
            175
                    user.email = flask.request.form.get("email") if flask.request.form.get( 
        
            176
                            "email") else None 
        
            177
                    user.location = flask.request.form["location"] 
        
            178
                    user.show_mail = True if flask.request.form.get("showmail") else False 
        
            179
                    user.bio = flask.request.form.get("bio") 
        
            180
                    db.session.commit() 
        
            182
                    flask.flash( 
        
            184
                            Markup("<iconify-icon icon='mdi:check'></iconify-icon>" + _("Settings saved")), 
        
            185
                            category="success") 
        
            186
                    return flask.redirect(f"/{flask.session.get('username')}", code=303) 
        
            187
            @app.route("/favourites/", methods=["GET", "POST"]) 
        
            190
            def favourites(): 
        
            191
                if not flask.session.get("username"): 
        
            192
                    flask.abort(401) 
        
            193
                if flask.request.method == "GET": 
        
            194
                    relationships = RepoFavourite.query.filter_by( 
        
            195
                            user_username=flask.session.get("username")) 
        
            196
                    return flask.render_template("favourites.html", favourites=relationships) 
        
            198
            @app.route("/favourites/<int:id>", methods=["POST"]) 
        
            201
            def favourite_edit(id): 
        
            202
                if not flask.session.get("username"): 
        
            203
                    flask.abort(401) 
        
            204
                favourite = db.session.get(RepoFavourite, id) 
        
            205
                if favourite.user_username != flask.session.get("username"): 
        
            206
                    flask.abort(403) 
        
            207
                data = flask.request.form 
        
            208
                print(data) 
        
            209
                favourite.notify_commit = js_to_bool(data.get("commit")) 
        
            210
                favourite.notify_forum = js_to_bool(data.get("forum")) 
        
            211
                favourite.notify_pr = js_to_bool(data.get("pull_request")) 
        
            212
                favourite.notify_admin = js_to_bool(data.get("administrative")) 
        
            213
                print(favourite.notify_commit, favourite.notify_forum, favourite.notify_pr, 
        
            214
                      favourite.notify_admin) 
        
            215
                db.session.commit() 
        
            216
                return flask.render_template_string( 
        
            217
                        """ 
        
            218
                    <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"> 
        
            219
                        <td><a href="{{ favourite.repo.route }}">{{ favourite.repo.owner.username }}/{{ favourite.repo.name }}</a></td> 
        
            220
                        <td style="text-align: center;"><input type="checkbox" name="commit" id="commit-{{ favourite.id }}" value="true" {% if favourite.notify_commit %}checked{% endif %}></td> 
        
            221
                        <td style="text-align: center;"><input type="checkbox" name="forum" id="forum-{{ favourite.id }}" value="true" {% if favourite.notify_forum %}checked{% endif %}></td> 
        
            222
                        <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> 
        
            223
                        <td style="text-align: center;"><input type="checkbox" name="administrative" id="administrative-{{ favourite.id }}" value="true" {% if favourite.notify_admin %}checked{% endif %}></td> 
        
            224
                    </tr> 
        
            225
                    """, 
        
            226
                        favourite=favourite 
        
            227
                ) 
        
            228
            @app.route("/notifications/", methods=["GET", "POST"]) 
        
            231
            def notifications(): 
        
            232
                if not flask.session.get("username"): 
        
            233
                    flask.abort(401) 
        
            234
                if flask.request.method == "GET": 
        
            235
                    return flask.render_template("notifications.html", 
        
            236
                                                 notifications=UserNotification.query.filter_by( 
        
            237
                                                         user_username=flask.session.get("username") 
        
            238
                                                    ).order_by(UserNotification.id.desc()), 
        
            239
                                                 db=db, Commit=Commit 
        
            240
                                                 ) 
        
            241
            @app.route("/notifications/<int:notification_id>/read", methods=["POST"]) 
        
            244
            def mark_read(notification_id): 
        
            245
                if not flask.session.get("username"): 
        
            246
                    flask.abort(401) 
        
            247
                notification = UserNotification.query.filter_by(id=notification_id).first() 
        
            248
                if notification.user_username != flask.session.get("username"): 
        
            249
                    flask.abort(403) 
        
            250
                notification.mark_read() 
        
            251
                db.session.commit() 
        
            252
                return flask.render_template_string( 
        
            253
                    "<button hx-post='/notifications/{{ notification.id }}/unread' hx-swap='outerHTML'>Mark as unread</button>", 
        
            254
                    notification=notification), 200 
        
            255
            @app.route("/notifications/<int:notification_id>/unread", methods=["POST"]) 
        
            258
            def mark_unread(notification_id): 
        
            259
                if not flask.session.get("username"): 
        
            260
                    flask.abort(401) 
        
            261
                notification = UserNotification.query.filter_by(id=notification_id).first() 
        
            262
                if notification.user_username != flask.session.get("username"): 
        
            263
                    flask.abort(403) 
        
            264
                notification.mark_unread() 
        
            265
                db.session.commit() 
        
            266
                return flask.render_template_string( 
        
            267
                    "<button hx-post='/notifications/{{ notification.id }}/read' hx-swap='outerHTML'>Mark as read</button>", 
        
            268
                    notification=notification), 200 
        
            269
            @app.route("/notifications/mark-all-read", methods=["POST"]) 
        
            272
            def mark_all_read(): 
        
            273
                if not flask.session.get("username"): 
        
            274
                    flask.abort(401) 
        
            275
                notifications = UserNotification.query.filter_by( 
        
            277
                        user_username=flask.session.get("username")) 
        
            278
                for notification in notifications: 
        
            279
                    notification.mark_read() 
        
            280
                db.session.commit() 
        
            281
                return flask.redirect("/notifications/", code=303) 
        
            282
            @app.route("/accounts/", methods=["GET", "POST"]) 
        
            285
            def login(): 
        
            286
                if flask.request.method == "GET": 
        
            287
                    return flask.render_template("login.html") 
        
            288
                else: 
        
            289
                    if "login" in flask.request.form: 
        
            290
                        username = flask.request.form["username"] 
        
            291
                        password = flask.request.form["password"] 
        
            292
                        user = User.query.filter_by(username=username).first() 
        
            294
                        if user and bcrypt.check_password_hash(user.password_hashed, password): 
        
            296
                            flask.session["username"] = user.username 
        
            297
                            flask.flash( 
        
            298
                                    Markup("<iconify-icon icon='mdi:account'></iconify-icon>" + _( 
        
            299
                                            "Successfully logged in as {username}").format( 
        
            300
                                        username=username)), 
        
            301
                                    category="success") 
        
            302
                            return flask.redirect("/", code=303) 
        
            303
                        elif not user: 
        
            304
                            flask.flash(Markup( 
        
            305
                                    "<iconify-icon icon='mdi:account-question'></iconify-icon>" + _( 
        
            306
                                            "User not found")), 
        
            307
                                    category="alert") 
        
            308
                            return flask.render_template("login.html") 
        
            309
                        else: 
        
            310
                            flask.flash(Markup( 
        
            311
                                    "<iconify-icon icon='mdi:account-question'></iconify-icon>" + _( 
        
            312
                                            "Invalid password")), 
        
            313
                                    category="error") 
        
            314
                            return flask.render_template("login.html") 
        
            315
                    if "signup" in flask.request.form: 
        
            316
                        username = flask.request.form["username"] 
        
            317
                        password = flask.request.form["password"] 
        
            318
                        password2 = flask.request.form["password2"] 
        
            319
                        email = flask.request.form.get("email") 
        
            320
                        email2 = flask.request.form.get("email2")  # repeat email is a honeypot 
        
            321
                        name = flask.request.form.get("name") 
        
            322
                        if not only_chars(username, 
        
            324
                                          "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"): 
        
            325
                            flask.flash(Markup( 
        
            326
                                    _("Usernames may only contain Latin alphabet, numbers, '-' and '_'")), 
        
            327
                                    category="error") 
        
            328
                            return flask.render_template("login.html") 
        
            329
                        if username in config.RESERVED_NAMES: 
        
            331
                            flask.flash( 
        
            332
                                    Markup( 
        
            333
                                            "<iconify-icon icon='mdi:account-error'></iconify-icon>" + _( 
        
            334
                                                    "Sorry, {username} is a system path").format( 
        
            335
                                                    username=username)), 
        
            336
                                    category="error") 
        
            337
                            return flask.render_template("login.html") 
        
            338
                        user_check = User.query.filter_by(username=username).first() 
        
            340
                        if user_check or email2:  # make the honeypot look like a normal error 
        
            341
                            flask.flash( 
        
            342
                                    Markup( 
        
            343
                                            "<iconify-icon icon='mdi:account-error'></iconify-icon>" + _( 
        
            344
                                                    "The username {username} is taken").format( 
        
            345
                                                    username=username)), 
        
            346
                                    category="error") 
        
            347
                            return flask.render_template("login.html") 
        
            348
                        if password2 != password: 
        
            350
                            flask.flash(Markup("<iconify-icon icon='mdi:key-alert'></iconify-icon>" + _( 
        
            351
                                    "Make sure the passwords match")), 
        
            352
                                        category="error") 
        
            353
                            return flask.render_template("login.html") 
        
            354
                        user = User(username, password, email, name) 
        
            356
                        db.session.add(user) 
        
            357
                        db.session.commit() 
        
            358
                        flask.session["username"] = user.username 
        
            359
                        flask.flash(Markup( 
        
            360
                                "<iconify-icon icon='mdi:account'></iconify-icon>" + _( 
        
            361
                                        "Successfully created and logged in as {username}").format( 
        
            362
                                        username=username)), 
        
            363
                                category="success") 
        
            364
                        notification = Notification({"type": "welcome"}) 
        
            366
                        db.session.add(notification) 
        
            367
                        db.session.commit() 
        
            368
                        return flask.redirect("/", code=303) 
        
            370
            @app.route("/newrepo/", methods=["GET", "POST"]) 
        
            373
            def new_repo(): 
        
            374
                if not flask.session.get("username"): 
        
            375
                    flask.abort(401) 
        
            376
                if flask.request.method == "GET": 
        
            377
                    return flask.render_template("new-repo.html") 
        
            378
                else: 
        
            379
                    name = flask.request.form["name"] 
        
            380
                    visibility = int(flask.request.form["visibility"]) 
        
            381
                    if not only_chars(name, 
        
            383
                                      "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"): 
        
            384
                        flask.flash(Markup( 
        
            385
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _( 
        
            386
                                        "Repository names may only contain Latin alphabet, numbers, '-' and '_'")), 
        
            387
                                category="error") 
        
            388
                        return flask.render_template("new-repo.html") 
        
            389
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            391
                    repo = Repo(user, name, visibility) 
        
            393
                    db.session.add(repo) 
        
            394
                    db.session.commit() 
        
            395
                    flask.flash(Markup(_("Successfully created repository {name}").format(name=name)), 
        
            397
                                category="success") 
        
            398
                    return flask.redirect(repo.route, code=303) 
        
            399
            @app.route("/logout") 
        
            402
            def logout(): 
        
            403
                flask.session.clear() 
        
            404
                flask.flash(Markup( 
        
            405
                        "<iconify-icon icon='mdi:account'></iconify-icon>" + _("Successfully logged out")), 
        
            406
                        category="info") 
        
            407
                return flask.redirect("/", code=303) 
        
            408
            @app.route("/<username>/", methods=["GET", "POST"]) 
        
            411
            def user_profile(username): 
        
            412
                old_relationship = UserFollow.query.filter_by( 
        
            413
                        follower_username=flask.session.get("username"), 
        
            414
                        followed_username=username).first() 
        
            415
                if flask.request.method == "GET": 
        
            416
                    user = User.query.filter_by(username=username).first() 
        
            417
                    match flask.request.args.get("action"): 
        
            418
                        case "repositories": 
        
            419
                            repos = Repo.query.filter_by(owner_name=username, visibility=2) 
        
            420
                            return flask.render_template("user-profile-repositories.html", user=user, 
        
            421
                                                         repos=repos, 
        
            422
                                                         relationship=old_relationship) 
        
            423
                        case "followers": 
        
            424
                            return flask.render_template("user-profile-followers.html", user=user, 
        
            425
                                                         relationship=old_relationship) 
        
            426
                        case "follows": 
        
            427
                            return flask.render_template("user-profile-follows.html", user=user, 
        
            428
                                                         relationship=old_relationship) 
        
            429
                        case _: 
        
            430
                            return flask.render_template("user-profile-overview.html", user=user, 
        
            431
                                                         relationship=old_relationship) 
        
            432
                elif flask.request.method == "POST": 
        
            434
                    match flask.request.args.get("action"): 
        
            435
                        case "follow": 
        
            436
                            if username == flask.session.get("username"): 
        
            437
                                flask.abort(403) 
        
            438
                            if old_relationship: 
        
            439
                                db.session.delete(old_relationship) 
        
            440
                            else: 
        
            441
                                relationship = UserFollow( 
        
            442
                                        flask.session.get("username"), 
        
            443
                                        username 
        
            444
                                ) 
        
            445
                                db.session.add(relationship) 
        
            446
                                db.session.commit() 
        
            447
                                user = db.session.get(User, username) 
        
            449
                                author = db.session.get(User, flask.session.get("username")) 
        
            450
                                notification = Notification({"type": "update", "version": "0.0.0"}) 
        
            451
                                db.session.add(notification) 
        
            452
                                db.session.commit() 
        
            453
                            db.session.commit() 
        
            455
                            return flask.redirect("?", code=303) 
        
            456
            @app.route("/<username>/<repository>/") 
        
            459
            def repository_index(username, repository): 
        
            460
                return flask.redirect("./tree", code=302) 
        
            461
            @app.route("/info/<username>/avatar") 
        
            464
            def user_avatar(username): 
        
            465
                server_userdata_location = os.path.join(config.USERDATA_PATH, username) 
        
            466
                if not os.path.exists(server_userdata_location): 
        
            467
                    return flask.render_template("not-found.html"), 404 
        
            468
                return flask.send_from_directory(server_userdata_location, "avatar.png") 
        
            470
            @app.route("/info/<username>/avatar", methods=["POST"]) 
        
            473
            def user_avatar_upload(username): 
        
            474
                server_userdata_location = os.path.join(config.USERDATA_PATH, username) 
        
            475
                if not os.path.exists(server_userdata_location): 
        
            477
                    flask.abort(404) 
        
            478
                if not flask.session.get("username") == username: 
        
            479
                    flask.abort(403) 
        
            480
                # Convert image to PNG 
        
            482
                try: 
        
            483
                    image = Image.open(flask.request.files["avatar"]) 
        
            484
                except PIL.UnidentifiedImageError: 
        
            485
                    flask.abort(400) 
        
            486
                image.save(os.path.join(server_userdata_location, "avatar.png")) 
        
            487
                return flask.redirect(f"/{username}", code=303) 
        
            489
            @app.route("/<username>/<repository>/raw/<branch>/<path:subpath>") 
        
            492
            def repository_raw(username, repository, branch, subpath): 
        
            493
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            494
                if not os.path.exists(server_repo_location): 
        
            495
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            496
                    flask.abort(404) 
        
            497
                if not (get_visibility(username, repository) or get_permission_level( 
        
            498
                        flask.session.get("username"), username, 
        
            499
                        repository) is not None): 
        
            500
                    flask.abort(403) 
        
            501
                app.logger.info(f"Loading {server_repo_location}") 
        
            503
                if not os.path.exists(server_repo_location): 
        
            505
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            506
                    return flask.render_template("not-found.html"), 404 
        
            507
                repo = git.Repo(server_repo_location) 
        
            509
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            510
                if not repo_data.default_branch: 
        
            511
                    if repo.heads: 
        
            512
                        repo_data.default_branch = repo.heads[0].name 
        
            513
                    else: 
        
            514
                        return flask.render_template("empty.html", 
        
            515
                                                     remote=f"http://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            516
                if not branch: 
        
            517
                    branch = repo_data.default_branch 
        
            518
                    return flask.redirect(f"./{branch}", code=302) 
        
            519
                if branch.startswith("tag:"): 
        
            521
                    ref = f"tags/{branch[4:]}" 
        
            522
                elif branch.startswith("~"): 
        
            523
                    ref = branch[1:] 
        
            524
                else: 
        
            525
                    ref = f"heads/{branch}" 
        
            526
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            528
                try: 
        
            530
                    repo.git.checkout("-f", ref) 
        
            531
                except git.exc.GitCommandError: 
        
            532
                    return flask.render_template("not-found.html"), 404 
        
            533
                return flask.send_from_directory(config.REPOS_PATH, 
        
            535
                                                 os.path.join(username, repository, subpath)) 
        
            536
            @repositories.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""}) 
        
            539
            @repositories.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""}) 
        
            540
            @repositories.route("/<username>/<repository>/tree/<branch>/<path:subpath>") 
        
            541
            def repository_tree(username, repository, branch, subpath): 
        
            542
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            543
                if not os.path.exists(server_repo_location): 
        
            544
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            545
                    flask.abort(404) 
        
            546
                if not (get_visibility(username, repository) or get_permission_level( 
        
            547
                        flask.session.get("username"), username, 
        
            548
                        repository) is not None): 
        
            549
                    flask.abort(403) 
        
            550
                app.logger.info(f"Loading {server_repo_location}") 
        
            552
                repo = git.Repo(server_repo_location) 
        
            554
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            555
                if not repo_data.default_branch: 
        
            556
                    if repo.heads: 
        
            557
                        repo_data.default_branch = repo.heads[0].name 
        
            558
                    else: 
        
            559
                        return flask.render_template("empty.html", 
        
            560
                                                     remote=f"{config.www_protocol}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            561
                if not branch: 
        
            562
                    branch = repo_data.default_branch 
        
            563
                    return flask.redirect(f"./{branch}", code=302) 
        
            564
                if branch.startswith("tag:"): 
        
            566
                    ref = f"tags/{branch[4:]}" 
        
            567
                elif branch.startswith("~"): 
        
            568
                    ref = branch[1:] 
        
            569
                else: 
        
            570
                    ref = f"heads/{branch}" 
        
            571
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            573
                try: 
        
            575
                    repo.git.checkout("-f", ref) 
        
            576
                except git.exc.GitCommandError: 
        
            577
                    return flask.render_template("not-found.html"), 404 
        
            578
                branches = repo.heads 
        
            580
                all_refs = [] 
        
            582
                for ref in repo.heads: 
        
            583
                    all_refs.append((ref, "head")) 
        
            584
                for ref in repo.tags: 
        
            585
                    all_refs.append((ref, "tag")) 
        
            586
                if os.path.isdir(os.path.join(server_repo_location, subpath)): 
        
            588
                    files = [] 
        
            589
                    blobs = [] 
        
            590
                    for entry in os.listdir(os.path.join(server_repo_location, subpath)): 
        
            592
                        if not os.path.basename(entry) == ".git": 
        
            593
                            files.append(os.path.join(subpath, entry)) 
        
            594
                    infos = [] 
        
            596
                    for file in files: 
        
            598
                        path = os.path.join(server_repo_location, file) 
        
            599
                        mimetype = guess_mime(path) 
        
            600
                        text = git_command(server_repo_location, None, "log", "--format='%H\n'", 
        
            602
                                           shlex.quote(file)).decode() 
        
            603
                        sha = text.split("\n")[0] 
        
            605
                        identifier = f"/{username}/{repository}/{sha}" 
        
            606
                        last_commit = db.session.get(Commit, identifier) 
        
            608
                        info = { 
        
            610
                            "name": os.path.basename(file), 
        
            611
                            "serverPath": path, 
        
            612
                            "relativePath": file, 
        
            613
                            "link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file), 
        
            614
                            "size": human_size(os.path.getsize(path)), 
        
            615
                            "mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}", 
        
            616
                            "commit": last_commit, 
        
            617
                            "shaSize": 7, 
        
            618
                        } 
        
            619
                        special_icon = config.match_icon(os.path.basename(file)) 
        
            621
                        if special_icon: 
        
            622
                            info["icon"] = special_icon 
        
            623
                        elif os.path.isdir(path): 
        
            624
                            info["icon"] = config.folder_icon 
        
            625
                        elif mimetypes.guess_type(path)[0] in config.file_icons: 
        
            626
                            info["icon"] = config.file_icons[mimetypes.guess_type(path)[0]] 
        
            627
                        else: 
        
            628
                            info["icon"] = config.unknown_icon 
        
            629
                        if os.path.isdir(path): 
        
            631
                            infos.insert(0, info) 
        
            632
                        else: 
        
            633
                            infos.append(info) 
        
            634
                    return flask.render_template( 
        
            636
                            "repo-tree.html", 
        
            637
                            username=username, 
        
            638
                            repository=repository, 
        
            639
                            files=infos, 
        
            640
                            subpath=os.path.join("/", subpath), 
        
            641
                            branches=all_refs, 
        
            642
                            current=branch, 
        
            643
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            644
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            645
                    ) 
        
            646
                else: 
        
            647
                    path = os.path.join(server_repo_location, subpath) 
        
            648
                    if not os.path.exists(path): 
        
            650
                        return flask.render_template("not-found.html"), 404 
        
            651
                    mimetype = guess_mime(path) 
        
            653
                    mode = mimetype.split("/", 1)[0] 
        
            654
                    size = human_size(os.path.getsize(path)) 
        
            655
                    special_icon = config.match_icon(os.path.basename(path)) 
        
            657
                    if special_icon: 
        
            658
                        icon = special_icon 
        
            659
                    elif os.path.isdir(path): 
        
            660
                        icon = config.folder_icon 
        
            661
                    elif mimetypes.guess_type(path)[0] in config.file_icons: 
        
            662
                        icon = config.file_icons[mimetypes.guess_type(path)[0]] 
        
            663
                    else: 
        
            664
                        icon = config.unknown_icon 
        
            665
                    contents = None 
        
            667
                    if mode == "text": 
        
            668
                        contents = convert_to_html(path) 
        
            669
                    return flask.render_template( 
        
            671
                            "repo-file.html", 
        
            672
                            username=username, 
        
            673
                            repository=repository, 
        
            674
                            file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath), 
        
            675
                            branches=all_refs, 
        
            676
                            current=branch, 
        
            677
                            mode=mode, 
        
            678
                            mimetype=mimetype, 
        
            679
                            detailedtype=magic.from_file(path), 
        
            680
                            size=size, 
        
            681
                            icon=icon, 
        
            682
                            subpath=os.path.join("/", subpath), 
        
            683
                            extension=pathlib.Path(path).suffix, 
        
            684
                            basename=os.path.basename(path), 
        
            685
                            contents=contents, 
        
            686
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            687
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            688
                    ) 
        
            689
            @repositories.route("/<username>/<repository>/commit/<sha>") 
        
            692
            def repository_commit(username, repository, sha): 
        
            693
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            694
                if not os.path.exists(server_repo_location): 
        
            695
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            696
                    flask.abort(404) 
        
            697
                if not (get_visibility(username, repository) or get_permission_level( 
        
            698
                        flask.session.get("username"), username, 
        
            699
                        repository) is not None): 
        
            700
                    flask.abort(403) 
        
            701
                app.logger.info(f"Loading {server_repo_location}") 
        
            703
                if not os.path.exists(server_repo_location): 
        
            705
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            706
                    return flask.render_template("not-found.html"), 404 
        
            707
                repo = git.Repo(server_repo_location) 
        
            709
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            710
                files = git_command(os.path.join(server_repo_location, ".git"), None, "diff-tree", "-r", 
        
            712
                                    "--name-only", "--no-commit-id", sha).decode().split("\n")[:-1] 
        
            713
                print(files) 
        
            715
                return flask.render_template( 
        
            717
                        "repo-commit.html", 
        
            718
                        username=username, 
        
            719
                        repository=repository, 
        
            720
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            721
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            722
                        diff={file: git_command(os.path.join(server_repo_location, ".git"), None, "diff", 
        
            723
                                                str(sha) + "^!", "--", file).decode().split("\n") for 
        
            724
                              file in files}, 
        
            725
                        data=db.session.get(Commit, f"/{username}/{repository}/{sha}"), 
        
            726
                ) 
        
            727
            @repositories.route("/<username>/<repository>/forum/") 
        
            730
            def repository_forum(username, repository): 
        
            731
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            732
                if not os.path.exists(server_repo_location): 
        
            733
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            734
                    flask.abort(404) 
        
            735
                if not (get_visibility(username, repository) or get_permission_level( 
        
            736
                        flask.session.get("username"), username, 
        
            737
                        repository) is not None): 
        
            738
                    flask.abort(403) 
        
            739
                app.logger.info(f"Loading {server_repo_location}") 
        
            741
                if not os.path.exists(server_repo_location): 
        
            743
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            744
                    return flask.render_template("not-found.html"), 404 
        
            745
                repo = git.Repo(server_repo_location) 
        
            747
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            748
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            749
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            750
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            751
                return flask.render_template( 
        
            753
                        "repo-forum.html", 
        
            754
                        username=username, 
        
            755
                        repository=repository, 
        
            756
                        repo_data=repo_data, 
        
            757
                        relationships=relationships, 
        
            758
                        repo=repo, 
        
            759
                        user_relationship=user_relationship, 
        
            760
                        Post=Post, 
        
            761
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            762
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            763
                        default_branch=repo_data.default_branch 
        
            764
                ) 
        
            765
            @repositories.route("/<username>/<repository>/forum/topic/<int:id>") 
        
            768
            def repository_forum_topic(username, repository, id): 
        
            769
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            770
                if not os.path.exists(server_repo_location): 
        
            771
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            772
                    flask.abort(404) 
        
            773
                if not (get_visibility(username, repository) or get_permission_level( 
        
            774
                        flask.session.get("username"), username, 
        
            775
                        repository) is not None): 
        
            776
                    flask.abort(403) 
        
            777
                app.logger.info(f"Loading {server_repo_location}") 
        
            779
                if not os.path.exists(server_repo_location): 
        
            781
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            782
                    return flask.render_template("not-found.html"), 404 
        
            783
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            785
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            786
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            787
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            788
                post = Post.query.filter_by(id=id).first() 
        
            790
                return flask.render_template( 
        
            792
                        "repo-topic.html", 
        
            793
                        username=username, 
        
            794
                        repository=repository, 
        
            795
                        repo_data=repo_data, 
        
            796
                        relationships=relationships, 
        
            797
                        user_relationship=user_relationship, 
        
            798
                        post=post, 
        
            799
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            800
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            801
                        default_branch=repo_data.default_branch 
        
            802
                ) 
        
            803
            @repositories.route("/<username>/<repository>/forum/new", methods=["POST", "GET"]) 
        
            806
            def repository_forum_new(username, repository): 
        
            807
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            808
                if not os.path.exists(server_repo_location): 
        
            809
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            810
                    flask.abort(404) 
        
            811
                if not (get_visibility(username, repository) or get_permission_level( 
        
            812
                        flask.session.get("username"), username, 
        
            813
                        repository) is not None): 
        
            814
                    flask.abort(403) 
        
            815
                app.logger.info(f"Loading {server_repo_location}") 
        
            817
                if not os.path.exists(server_repo_location): 
        
            819
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            820
                    return flask.render_template("not-found.html"), 404 
        
            821
                repo = git.Repo(server_repo_location) 
        
            823
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            824
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            825
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            826
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            827
                post = Post(user, repo_data, None, flask.request.form["subject"], 
        
            829
                            flask.request.form["message"]) 
        
            830
                db.session.add(post) 
        
            832
                db.session.commit() 
        
            833
                return flask.redirect( 
        
            835
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            836
                                      post_id=post.number), 
        
            837
                        code=303) 
        
            838
            @repositories.route("/<username>/<repository>/forum/<int:post_id>") 
        
            841
            def repository_forum_thread(username, repository, post_id): 
        
            842
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            843
                if not os.path.exists(server_repo_location): 
        
            844
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            845
                    flask.abort(404) 
        
            846
                if not (get_visibility(username, repository) or get_permission_level( 
        
            847
                        flask.session.get("username"), username, 
        
            848
                        repository) is not None): 
        
            849
                    flask.abort(403) 
        
            850
                app.logger.info(f"Loading {server_repo_location}") 
        
            852
                if not os.path.exists(server_repo_location): 
        
            854
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            855
                    return flask.render_template("not-found.html"), 404 
        
            856
                repo = git.Repo(server_repo_location) 
        
            858
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            859
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            860
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            861
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            862
                return flask.render_template( 
        
            864
                        "repo-forum-thread.html", 
        
            865
                        username=username, 
        
            866
                        repository=repository, 
        
            867
                        repo_data=repo_data, 
        
            868
                        relationships=relationships, 
        
            869
                        repo=repo, 
        
            870
                        Post=Post, 
        
            871
                        user_relationship=user_relationship, 
        
            872
                        post_id=post_id, 
        
            873
                        max_post_nesting=4, 
        
            874
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            875
                        is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            876
                        parent=Post.query.filter_by(repo=repo_data, number=post_id).first(), 
        
            877
                        has_permission=not ((not get_permission_level(flask.session.get("username"), username, 
        
            878
                                                                 repository)) and db.session.get(Post, 
        
            879
                                                                                                 f"/{username}/{repository}/{post_id}").owner.username != flask.session.get("username")), 
        
            880
                ) 
        
            881
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/change-state", 
        
            884
                                methods=["POST"]) 
        
            885
            def repository_forum_change_state(username, repository, post_id): 
        
            886
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            887
                if not os.path.exists(server_repo_location): 
        
            888
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            889
                    flask.abort(404) 
        
            890
                if (not get_permission_level(flask.session.get("username"), username, repository)) and db.session.get(Post, f"/{username}/{repository}/{post_id}").owner.username != flask.session.get("username"): 
        
            891
                    flask.abort(403) 
        
            892
                app.logger.info(f"Loading {server_repo_location}") 
        
            894
                repo = git.Repo(server_repo_location) 
        
            896
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            897
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            898
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            899
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            900
                post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            902
                if not post: 
        
            904
                    flask.abort(404) 
        
            905
                post.state = int(flask.request.form["new-state"]) 
        
            907
                db.session.commit() 
        
            909
                return flask.redirect( 
        
            911
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            912
                                      post_id=post_id), 
        
            913
                        code=303) 
        
            914
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/reply", methods=["POST"]) 
        
            917
            def repository_forum_reply(username, repository, post_id): 
        
            918
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            919
                if not os.path.exists(server_repo_location): 
        
            920
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            921
                    flask.abort(404) 
        
            922
                if not (get_visibility(username, repository) or get_permission_level( 
        
            923
                        flask.session.get("username"), username, 
        
            924
                        repository) is not None): 
        
            925
                    flask.abort(403) 
        
            926
                app.logger.info(f"Loading {server_repo_location}") 
        
            928
                if not os.path.exists(server_repo_location): 
        
            930
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            931
                    return flask.render_template("not-found.html"), 404 
        
            932
                repo = git.Repo(server_repo_location) 
        
            934
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            935
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            936
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            937
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            938
                if not user: 
        
            939
                    flask.abort(401) 
        
            940
                parent = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            942
                post = Post(user, repo_data, parent, flask.request.form["subject"], 
        
            943
                            flask.request.form["message"]) 
        
            944
                db.session.add(post) 
        
            946
                post.update_date() 
        
            947
                db.session.commit() 
        
            948
                return flask.redirect( 
        
            950
                        flask.url_for(".repository_forum_thread", username=username, repository=repository, 
        
            951
                                      post_id=post_id), 
        
            952
                        code=303) 
        
            953
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/voteup", 
        
            956
                                defaults={"score": 1}) 
        
            957
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/votedown", 
        
            958
                                defaults={"score": -1}) 
        
            959
            @repositories.route("/<username>/<repository>/forum/<int:post_id>/votes", defaults={"score": 0}) 
        
            960
            def repository_forum_vote(username, repository, post_id, score): 
        
            961
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            962
                if not os.path.exists(server_repo_location): 
        
            963
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            964
                    flask.abort(404) 
        
            965
                if not (get_visibility(username, repository) or get_permission_level( 
        
            966
                        flask.session.get("username"), username, 
        
            967
                        repository) is not None): 
        
            968
                    flask.abort(403) 
        
            969
                app.logger.info(f"Loading {server_repo_location}") 
        
            971
                if not os.path.exists(server_repo_location): 
        
            973
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            974
                    return flask.render_template("not-found.html"), 404 
        
            975
                repo = git.Repo(server_repo_location) 
        
            977
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            978
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            979
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            980
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            981
                if not user: 
        
            982
                    flask.abort(401) 
        
            983
                post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first() 
        
            985
                if score: 
        
            987
                    old_relationship = PostVote.query.filter_by(user_username=user.username, 
        
            988
                                                                post_identifier=post.identifier).first() 
        
            989
                    if old_relationship: 
        
            990
                        if score == old_relationship.vote_score: 
        
            991
                            db.session.delete(old_relationship) 
        
            992
                            post.vote_sum -= old_relationship.vote_score 
        
            993
                        else: 
        
            994
                            post.vote_sum -= old_relationship.vote_score 
        
            995
                            post.vote_sum += score 
        
            996
                            old_relationship.vote_score = score 
        
            997
                    else: 
        
            998
                        relationship = PostVote(user, post, score) 
        
            999
                        post.vote_sum += score 
        
            1000
                        db.session.add(relationship) 
        
            1001
                    db.session.commit() 
        
            1003
                user_vote = PostVote.query.filter_by(user_username=user.username, 
        
            1005
                                                     post_identifier=post.identifier).first() 
        
            1006
                response = flask.make_response( 
        
            1007
                        str(post.vote_sum) + " " + str(user_vote.vote_score if user_vote else 0)) 
        
            1008
                response.content_type = "text/plain" 
        
            1009
                return response 
        
            1011
            @repositories.route("/<username>/<repository>/favourite") 
        
            1014
            def repository_favourite(username, repository): 
        
            1015
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1016
                if not os.path.exists(server_repo_location): 
        
            1017
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1018
                    flask.abort(404) 
        
            1019
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1020
                        flask.session.get("username"), username, 
        
            1021
                        repository) is not None): 
        
            1022
                    flask.abort(403) 
        
            1023
                app.logger.info(f"Loading {server_repo_location}") 
        
            1025
                if not os.path.exists(server_repo_location): 
        
            1027
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1028
                    return flask.render_template("not-found.html"), 404 
        
            1029
                repo = git.Repo(server_repo_location) 
        
            1031
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1032
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1033
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            1034
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            1035
                if not user: 
        
            1036
                    flask.abort(401) 
        
            1037
                old_relationship = RepoFavourite.query.filter_by(user_username=user.username, 
        
            1039
                                                                 repo_route=repo_data.route).first() 
        
            1040
                if old_relationship: 
        
            1041
                    db.session.delete(old_relationship) 
        
            1042
                else: 
        
            1043
                    relationship = RepoFavourite(user, repo_data) 
        
            1044
                    db.session.add(relationship) 
        
            1045
                db.session.commit() 
        
            1047
                return flask.redirect(flask.url_for("favourites"), code=303) 
        
            1049
            @repositories.route("/<username>/<repository>/users/", methods=["GET", "POST"]) 
        
            1052
            def repository_users(username, repository): 
        
            1053
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1054
                if not os.path.exists(server_repo_location): 
        
            1055
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1056
                    flask.abort(404) 
        
            1057
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1058
                        flask.session.get("username"), username, 
        
            1059
                        repository) is not None): 
        
            1060
                    flask.abort(403) 
        
            1061
                app.logger.info(f"Loading {server_repo_location}") 
        
            1063
                if not os.path.exists(server_repo_location): 
        
            1065
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1066
                    return flask.render_template("not-found.html"), 404 
        
            1067
                repo = git.Repo(server_repo_location) 
        
            1069
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1070
                user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1071
                relationships = RepoAccess.query.filter_by(repo=repo_data) 
        
            1072
                user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first() 
        
            1073
                if flask.request.method == "GET": 
        
            1075
                    return flask.render_template( 
        
            1076
                            "repo-users.html", 
        
            1077
                            username=username, 
        
            1078
                            repository=repository, 
        
            1079
                            repo_data=repo_data, 
        
            1080
                            relationships=relationships, 
        
            1081
                            repo=repo, 
        
            1082
                            user_relationship=user_relationship, 
        
            1083
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1084
                            is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1085
                    ) 
        
            1086
                else: 
        
            1087
                    if get_permission_level(flask.session.get("username"), username, repository) != 2: 
        
            1088
                        flask.abort(401) 
        
            1089
                    if flask.request.form.get("new-username"): 
        
            1091
                        # Create new relationship 
        
            1092
                        new_user = User.query.filter_by( 
        
            1093
                                username=flask.request.form.get("new-username")).first() 
        
            1094
                        relationship = RepoAccess(new_user, repo_data, flask.request.form.get("new-level")) 
        
            1095
                        db.session.add(relationship) 
        
            1096
                        db.session.commit() 
        
            1097
                    if flask.request.form.get("update-username"): 
        
            1098
                        # Create new relationship 
        
            1099
                        updated_user = User.query.filter_by( 
        
            1100
                                username=flask.request.form.get("update-username")).first() 
        
            1101
                        relationship = RepoAccess.query.filter_by(repo=repo_data, user=updated_user).first() 
        
            1102
                        if flask.request.form.get("update-level") == -1: 
        
            1103
                            relationship.delete() 
        
            1104
                        else: 
        
            1105
                            relationship.access_level = flask.request.form.get("update-level") 
        
            1106
                        db.session.commit() 
        
            1107
                    return flask.redirect( 
        
            1109
                            app.url_for(".repository_users", username=username, repository=repository)) 
        
            1110
            @repositories.route("/<username>/<repository>/branches/") 
        
            1113
            def repository_branches(username, repository): 
        
            1114
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1115
                if not os.path.exists(server_repo_location): 
        
            1116
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1117
                    flask.abort(404) 
        
            1118
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1119
                        flask.session.get("username"), username, 
        
            1120
                        repository) is not None): 
        
            1121
                    flask.abort(403) 
        
            1122
                app.logger.info(f"Loading {server_repo_location}") 
        
            1124
                if not os.path.exists(server_repo_location): 
        
            1126
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1127
                    return flask.render_template("not-found.html"), 404 
        
            1128
                repo = git.Repo(server_repo_location) 
        
            1130
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1131
                return flask.render_template( 
        
            1133
                        "repo-branches.html", 
        
            1134
                        username=username, 
        
            1135
                        repository=repository, 
        
            1136
                        repo_data=repo_data, 
        
            1137
                        repo=repo, 
        
            1138
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1139
                        is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1140
                ) 
        
            1141
            @repositories.route("/<username>/<repository>/log/", defaults={"branch": None}) 
        
            1144
            @repositories.route("/<username>/<repository>/log/<branch>/") 
        
            1145
            def repository_log(username, repository, branch): 
        
            1146
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1147
                if not os.path.exists(server_repo_location): 
        
            1148
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1149
                    flask.abort(404) 
        
            1150
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1151
                        flask.session.get("username"), username, 
        
            1152
                        repository) is not None): 
        
            1153
                    flask.abort(403) 
        
            1154
                app.logger.info(f"Loading {server_repo_location}") 
        
            1156
                if not os.path.exists(server_repo_location): 
        
            1158
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1159
                    return flask.render_template("not-found.html"), 404 
        
            1160
                repo = git.Repo(server_repo_location) 
        
            1162
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1163
                if not repo_data.default_branch: 
        
            1164
                    if repo.heads: 
        
            1165
                        repo_data.default_branch = repo.heads[0].name 
        
            1166
                    else: 
        
            1167
                        return flask.render_template("empty.html", 
        
            1168
                                                     remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200 
        
            1169
                if not branch: 
        
            1170
                    branch = repo_data.default_branch 
        
            1171
                    return flask.redirect(f"./{branch}", code=302) 
        
            1172
                if branch.startswith("tag:"): 
        
            1174
                    ref = f"tags/{branch[4:]}" 
        
            1175
                elif branch.startswith("~"): 
        
            1176
                    ref = branch[1:] 
        
            1177
                else: 
        
            1178
                    ref = f"heads/{branch}" 
        
            1179
                ref = ref.replace("~", "/")  # encode slashes for URL support 
        
            1181
                try: 
        
            1183
                    repo.git.checkout("-f", ref) 
        
            1184
                except git.exc.GitCommandError: 
        
            1185
                    return flask.render_template("not-found.html"), 404 
        
            1186
                branches = repo.heads 
        
            1188
                all_refs = [] 
        
            1190
                for ref in repo.heads: 
        
            1191
                    all_refs.append((ref, "head")) 
        
            1192
                for ref in repo.tags: 
        
            1193
                    all_refs.append((ref, "tag")) 
        
            1194
                commit_list = [f"/{username}/{repository}/{sha}" for sha in 
        
            1196
                               git_command(server_repo_location, None, "log", 
        
            1197
                                           "--format='%H'").decode().split("\n")] 
        
            1198
                commits = Commit.query.filter(Commit.identifier.in_(commit_list)) 
        
            1200
                return flask.render_template( 
        
            1202
                        "repo-log.html", 
        
            1203
                        username=username, 
        
            1204
                        repository=repository, 
        
            1205
                        branches=all_refs, 
        
            1206
                        current=branch, 
        
            1207
                        repo_data=repo_data, 
        
            1208
                        repo=repo, 
        
            1209
                        commits=commits, 
        
            1210
                        remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1211
                        is_favourite=get_favourite(flask.session.get("username"), username, repository) 
        
            1212
                ) 
        
            1213
            @repositories.route("/<username>/<repository>/prs/", methods=["GET", "POST"]) 
        
            1216
            def repository_prs(username, repository): 
        
            1217
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1218
                if not os.path.exists(server_repo_location): 
        
            1219
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1220
                    flask.abort(404) 
        
            1221
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1222
                        flask.session.get("username"), username, 
        
            1223
                        repository) is not None): 
        
            1224
                    flask.abort(403) 
        
            1225
                app.logger.info(f"Loading {server_repo_location}") 
        
            1227
                if not os.path.exists(server_repo_location): 
        
            1229
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1230
                    return flask.render_template("not-found.html"), 404 
        
            1231
                if flask.request.method == "GET": 
        
            1233
                    repo = git.Repo(server_repo_location) 
        
            1234
                    repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1235
                    user = User.query.filter_by(username=flask.session.get("username")).first() 
        
            1236
                    return flask.render_template( 
        
            1238
                            "repo-prs.html", 
        
            1239
                            username=username, 
        
            1240
                            repository=repository, 
        
            1241
                            repo_data=repo_data, 
        
            1242
                            repo=repo, 
        
            1243
                            PullRequest=PullRequest, 
        
            1244
                            remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}", 
        
            1245
                            is_favourite=get_favourite(flask.session.get("username"), username, repository), 
        
            1246
                            default_branch=repo_data.default_branch, 
        
            1247
                            branches=repo.branches 
        
            1248
                    ) 
        
            1249
                else: 
        
            1251
                    repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1252
                    head = flask.request.form.get("head") 
        
            1253
                    head_route = flask.request.form.get("headroute") 
        
            1254
                    base = flask.request.form.get("base") 
        
            1255
                    if not head and base and head_route: 
        
            1257
                        return flask.redirect(".", 400) 
        
            1258
                    head_repo = git.Repo(os.path.join(config.REPOS_PATH, head_route.lstrip("/"))) 
        
            1260
                    base_repo = git.Repo(server_repo_location) 
        
            1261
                    print(head_repo) 
        
            1262
                    if head not in head_repo.branches or base not in base_repo.branches: 
        
            1264
                        flask.flash(Markup( 
        
            1265
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _("Bad branch name")), 
        
            1266
                                category="error") 
        
            1267
                        return flask.redirect(".", 303) 
        
            1268
                    head_data = db.session.get(Repo, head_route) 
        
            1270
                    if not head_data.visibility: 
        
            1271
                        flask.flash(Markup( 
        
            1272
                                "<iconify-icon icon='mdi:error'></iconify-icon>" + _( 
        
            1273
                                        "Head can't be restricted")), 
        
            1274
                                category="error") 
        
            1275
                        return flask.redirect(".", 303) 
        
            1276
                    pull_request = PullRequest(repo_data, head, head_data, base, 
        
            1278
                                               db.session.get(User, flask.session["username"])) 
        
            1279
                    db.session.add(pull_request) 
        
            1281
                    db.session.commit() 
        
            1282
                    return flask.redirect(".", 303) 
        
            1284
            @repositories.route("/<username>/<repository>/prs/merge", methods=["POST"]) 
        
            1287
            def repository_prs_merge(username, repository): 
        
            1288
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1289
                if not os.path.exists(server_repo_location): 
        
            1290
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1291
                    flask.abort(404) 
        
            1292
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1293
                        flask.session.get("username"), username, 
        
            1294
                        repository) is not None): 
        
            1295
                    flask.abort(403) 
        
            1296
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1298
                    flask.abort(401) 
        
            1299
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1301
                repo = git.Repo(server_repo_location) 
        
            1302
                id = flask.request.form.get("id") 
        
            1303
                pull_request = db.session.get(PullRequest, id) 
        
            1305
                if pull_request: 
        
            1307
                    result = celery_tasks.merge_heads.delay( 
        
            1308
                            pull_request.head_route, 
        
            1309
                            pull_request.head_branch, 
        
            1310
                            pull_request.base_route, 
        
            1311
                            pull_request.base_branch, 
        
            1312
                            simulate=True 
        
            1313
                    ) 
        
            1314
                    task_result = worker.AsyncResult(result.id) 
        
            1315
                    return flask.redirect(f"/task/{result.id}?pr-id={id}", 303) 
        
            1317
                    # db.session.delete(pull_request) 
        
            1318
                    # db.session.commit() 
        
            1319
                else: 
        
            1320
                    flask.abort(400) 
        
            1321
            @repositories.route("/<username>/<repository>/prs/<int:id>/merge") 
        
            1324
            def repository_prs_merge_stage_two(username, repository, id): 
        
            1325
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1326
                if not os.path.exists(server_repo_location): 
        
            1327
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1328
                    flask.abort(404) 
        
            1329
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1330
                        flask.session.get("username"), username, 
        
            1331
                        repository) is not None): 
        
            1332
                    flask.abort(403) 
        
            1333
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1335
                    flask.abort(401) 
        
            1336
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1338
                repo = git.Repo(server_repo_location) 
        
            1339
                pull_request = db.session.get(PullRequest, id) 
        
            1341
                if pull_request: 
        
            1343
                    result = celery_tasks.merge_heads.delay( 
        
            1344
                            pull_request.head_route, 
        
            1345
                            pull_request.head_branch, 
        
            1346
                            pull_request.base_route, 
        
            1347
                            pull_request.base_branch, 
        
            1348
                            simulate=False 
        
            1349
                    ) 
        
            1350
                    task_result = worker.AsyncResult(result.id) 
        
            1351
                    pull_request.state = 1 
        
            1353
                    db.session.commit() 
        
            1354
                    return flask.redirect(f"/task/{result.id}?pr-id={id}", 303) 
        
            1356
                    # db.session.delete(pull_request) 
        
            1357
                else: 
        
            1358
                    flask.abort(400) 
        
            1359
            @app.route("/task/<task_id>") 
        
            1362
            def task_monitor(task_id): 
        
            1363
                task_result = worker.AsyncResult(task_id) 
        
            1364
                print(task_result.status) 
        
            1365
                return flask.render_template("task-monitor.html", result=task_result) 
        
            1367
            @repositories.route("/<username>/<repository>/prs/delete", methods=["POST"]) 
        
            1370
            def repository_prs_delete(username, repository): 
        
            1371
                server_repo_location = os.path.join(config.REPOS_PATH, username, repository) 
        
            1372
                if not os.path.exists(server_repo_location): 
        
            1373
                    app.logger.error(f"Cannot load {server_repo_location}") 
        
            1374
                    flask.abort(404) 
        
            1375
                if not (get_visibility(username, repository) or get_permission_level( 
        
            1376
                        flask.session.get("username"), username, 
        
            1377
                        repository) is not None): 
        
            1378
                    flask.abort(403) 
        
            1379
                if not get_permission_level(flask.session.get("username"), username, repository): 
        
            1381
                    flask.abort(401) 
        
            1382
                repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first() 
        
            1384
                repo = git.Repo(server_repo_location) 
        
            1385
                id = flask.request.form.get("id") 
        
            1386
                pull_request = db.session.get(PullRequest, id) 
        
            1388
                if pull_request: 
        
            1390
                    pull_request.state = 2 
        
            1391
                    db.session.commit() 
        
            1392
                return flask.redirect(".", 303) 
        
            1394
            @repositories.route("/<username>/<repository>/settings/") 
        
            1397
            def repository_settings(username, repository): 
        
            1398
                if get_permission_level(flask.session.get("username"), username, repository) != 2: 
        
            1399
                    flask.abort(401) 
        
            1400
                return flask.render_template("repo-settings.html", username=username, repository=repository) 
        
            1402
            @app.errorhandler(404) 
        
            1405
            def e404(error): 
        
            1406
                return flask.render_template("not-found.html"), 404 
        
            1407
            @app.errorhandler(401) 
        
            1410
            def e401(error): 
        
            1411
                return flask.render_template("unauthorised.html"), 401 
        
            1412
            @app.errorhandler(403) 
        
            1415
            def e403(error): 
        
            1416
                return flask.render_template("forbidden.html"), 403 
        
            1417
            @app.errorhandler(418) 
        
            1420
            def e418(error): 
        
            1421
                return flask.render_template("teapot.html"), 418 
        
            1422
            @app.errorhandler(405) 
        
            1425
            def e405(error): 
        
            1426
                return flask.render_template("method-not-allowed.html"), 405 
        
            1427
            if __name__ == "__main__": 
        
            1430
                app.run(debug=True, port=8080, host="0.0.0.0") 
        
            1431
            app.register_blueprint(repositories) 
        
            1433