By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 app.py

View raw Download
text/x-script.python • 56.02 kiB
Python script, Unicode text, UTF-8 text executable
        
            
1
__version__ = "0.2.0"
2
3
import os
4
import shutil
5
import random
6
import subprocess
7
import platform
8
9
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
30
_ = gettext
31
n_ = gettext
32
33
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
42
auth = HTTPBasicAuth()
43
44
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
50
db = SQLAlchemy(app)
51
bcrypt = Bcrypt(app)
52
migrate = Migrate(app, db)
53
54
from misc_utils import *
55
56
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
63
from models import *
64
65
babel = Babel(app)
66
67
68
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
73
74
babel.init_app(app, locale_selector=get_locale)
75
76
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
83
worker = celery_integration.init_celery_app(app)
84
85
repositories = flask.Blueprint("repository", __name__, template_folder="templates/repository/")
86
87
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
91
92
@app.context_processor
93
def default():
94
username = flask.session.get("username")
95
96
user_object = User.query.filter_by(username=username).first()
97
98
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
109
110
@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
117
118
@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
129
130
@app.route("/about/")
131
def about():
132
return flask.render_template("about.html", platform=platform, version=__version__)
133
134
135
@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
144
return response
145
146
147
@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
154
155
@app.route("/help/")
156
def help_index():
157
return flask.render_template("help.html", faqs=config.faqs)
158
159
160
@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
167
return flask.render_template("user-settings.html", user=user)
168
else:
169
user = User.query.filter_by(username=flask.session.get("username")).first()
170
171
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
181
db.session.commit()
182
183
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
188
189
@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
197
return flask.render_template("favourites.html", favourites=relationships)
198
199
200
@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
229
230
@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
242
243
@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
256
257
@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
270
271
@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
276
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
283
284
@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
293
user = User.query.filter_by(username=username).first()
294
295
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
323
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
330
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
339
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
349
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
355
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
365
notification = Notification({"type": "welcome"})
366
db.session.add(notification)
367
db.session.commit()
368
369
return flask.redirect("/", code=303)
370
371
372
@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
382
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
390
user = User.query.filter_by(username=flask.session.get("username")).first()
391
392
repo = Repo(user, name, visibility)
393
db.session.add(repo)
394
db.session.commit()
395
396
flask.flash(Markup(_("Successfully created repository {name}").format(name=name)),
397
category="success")
398
return flask.redirect(repo.route, code=303)
399
400
401
@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
409
410
@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
433
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
448
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
454
db.session.commit()
455
return flask.redirect("?", code=303)
456
457
458
@app.route("/<username>/<repository>/")
459
def repository_index(username, repository):
460
return flask.redirect("./tree", code=302)
461
462
463
@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
469
return flask.send_from_directory(server_userdata_location, "avatar.png")
470
471
472
@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
476
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
481
# 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
488
return flask.redirect(f"/{username}", code=303)
489
490
491
@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
502
app.logger.info(f"Loading {server_repo_location}")
503
504
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
508
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
520
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
527
ref = ref.replace("~", "/") # encode slashes for URL support
528
529
try:
530
repo.git.checkout("-f", ref)
531
except git.exc.GitCommandError:
532
return flask.render_template("not-found.html"), 404
533
534
return flask.send_from_directory(config.REPOS_PATH,
535
os.path.join(username, repository, subpath))
536
537
538
@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
551
app.logger.info(f"Loading {server_repo_location}")
552
553
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
565
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
572
ref = ref.replace("~", "/") # encode slashes for URL support
573
574
try:
575
repo.git.checkout("-f", ref)
576
except git.exc.GitCommandError:
577
return flask.render_template("not-found.html"), 404
578
579
branches = repo.heads
580
581
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
587
if os.path.isdir(os.path.join(server_repo_location, subpath)):
588
files = []
589
blobs = []
590
591
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
595
infos = []
596
597
for file in files:
598
path = os.path.join(server_repo_location, file)
599
mimetype = guess_mime(path)
600
601
text = git_command(server_repo_location, None, "log", "--format='%H\n'",
602
shlex.quote(file)).decode()
603
604
sha = text.split("\n")[0]
605
identifier = f"/{username}/{repository}/{sha}"
606
607
last_commit = db.session.get(Commit, identifier)
608
609
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
620
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
630
if os.path.isdir(path):
631
infos.insert(0, info)
632
else:
633
infos.append(info)
634
635
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
649
if not os.path.exists(path):
650
return flask.render_template("not-found.html"), 404
651
652
mimetype = guess_mime(path)
653
mode = mimetype.split("/", 1)[0]
654
size = human_size(os.path.getsize(path))
655
656
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
666
contents = None
667
if mode == "text":
668
contents = convert_to_html(path)
669
670
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
690
691
@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
702
app.logger.info(f"Loading {server_repo_location}")
703
704
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
708
repo = git.Repo(server_repo_location)
709
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
710
711
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
714
print(files)
715
716
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
728
729
@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
740
app.logger.info(f"Loading {server_repo_location}")
741
742
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
746
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
752
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
766
767
@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
778
app.logger.info(f"Loading {server_repo_location}")
779
780
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
784
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
789
post = Post.query.filter_by(id=id).first()
790
791
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
804
805
@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
816
app.logger.info(f"Loading {server_repo_location}")
817
818
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
822
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
828
post = Post(user, repo_data, None, flask.request.form["subject"],
829
flask.request.form["message"])
830
831
db.session.add(post)
832
db.session.commit()
833
834
return flask.redirect(
835
flask.url_for(".repository_forum_thread", username=username, repository=repository,
836
post_id=post.number),
837
code=303)
838
839
840
@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
851
app.logger.info(f"Loading {server_repo_location}")
852
853
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
857
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
863
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
882
883
@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
893
app.logger.info(f"Loading {server_repo_location}")
894
895
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
901
post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first()
902
903
if not post:
904
flask.abort(404)
905
906
post.state = int(flask.request.form["new-state"])
907
908
db.session.commit()
909
910
return flask.redirect(
911
flask.url_for(".repository_forum_thread", username=username, repository=repository,
912
post_id=post_id),
913
code=303)
914
915
916
@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
927
app.logger.info(f"Loading {server_repo_location}")
928
929
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
933
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
941
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
945
db.session.add(post)
946
post.update_date()
947
db.session.commit()
948
949
return flask.redirect(
950
flask.url_for(".repository_forum_thread", username=username, repository=repository,
951
post_id=post_id),
952
code=303)
953
954
955
@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
970
app.logger.info(f"Loading {server_repo_location}")
971
972
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
976
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
984
post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first()
985
986
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
1002
db.session.commit()
1003
1004
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
1010
return response
1011
1012
1013
@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
1024
app.logger.info(f"Loading {server_repo_location}")
1025
1026
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
1030
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
1038
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
1046
db.session.commit()
1047
1048
return flask.redirect(flask.url_for("favourites"), code=303)
1049
1050
1051
@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
1062
app.logger.info(f"Loading {server_repo_location}")
1063
1064
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
1068
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
1074
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
1090
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
1108
return flask.redirect(
1109
app.url_for(".repository_users", username=username, repository=repository))
1110
1111
1112
@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
1123
app.logger.info(f"Loading {server_repo_location}")
1124
1125
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
1129
repo = git.Repo(server_repo_location)
1130
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1131
1132
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
1142
1143
@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
1155
app.logger.info(f"Loading {server_repo_location}")
1156
1157
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
1161
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
1173
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
1180
ref = ref.replace("~", "/") # encode slashes for URL support
1181
1182
try:
1183
repo.git.checkout("-f", ref)
1184
except git.exc.GitCommandError:
1185
return flask.render_template("not-found.html"), 404
1186
1187
branches = repo.heads
1188
1189
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
1195
commit_list = [f"/{username}/{repository}/{sha}" for sha in
1196
git_command(server_repo_location, None, "log",
1197
"--format='%H'").decode().split("\n")]
1198
1199
commits = Commit.query.filter(Commit.identifier.in_(commit_list))
1200
1201
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
1214
1215
@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
1226
app.logger.info(f"Loading {server_repo_location}")
1227
1228
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
1232
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
1237
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
1250
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
1256
if not head and base and head_route:
1257
return flask.redirect(".", 400)
1258
1259
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
1263
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
1269
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
1277
pull_request = PullRequest(repo_data, head, head_data, base,
1278
db.session.get(User, flask.session["username"]))
1279
1280
db.session.add(pull_request)
1281
db.session.commit()
1282
1283
return flask.redirect(".", 303)
1284
1285
1286
@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
1297
if not get_permission_level(flask.session.get("username"), username, repository):
1298
flask.abort(401)
1299
1300
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
1304
pull_request = db.session.get(PullRequest, id)
1305
1306
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
1316
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
1322
1323
@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
1334
if not get_permission_level(flask.session.get("username"), username, repository):
1335
flask.abort(401)
1336
1337
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1338
repo = git.Repo(server_repo_location)
1339
1340
pull_request = db.session.get(PullRequest, id)
1341
1342
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
1352
pull_request.state = 1
1353
db.session.commit()
1354
1355
return flask.redirect(f"/task/{result.id}?pr-id={id}", 303)
1356
# db.session.delete(pull_request)
1357
else:
1358
flask.abort(400)
1359
1360
1361
@app.route("/task/<task_id>")
1362
def task_monitor(task_id):
1363
task_result = worker.AsyncResult(task_id)
1364
print(task_result.status)
1365
1366
return flask.render_template("task-monitor.html", result=task_result)
1367
1368
1369
@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
1380
if not get_permission_level(flask.session.get("username"), username, repository):
1381
flask.abort(401)
1382
1383
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
1387
pull_request = db.session.get(PullRequest, id)
1388
1389
if pull_request:
1390
pull_request.state = 2
1391
db.session.commit()
1392
1393
return flask.redirect(".", 303)
1394
1395
1396
@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
1401
return flask.render_template("repo-settings.html", username=username, repository=repository)
1402
1403
1404
@app.errorhandler(404)
1405
def e404(error):
1406
return flask.render_template("not-found.html"), 404
1407
1408
1409
@app.errorhandler(401)
1410
def e401(error):
1411
return flask.render_template("unauthorised.html"), 401
1412
1413
1414
@app.errorhandler(403)
1415
def e403(error):
1416
return flask.render_template("forbidden.html"), 403
1417
1418
1419
@app.errorhandler(418)
1420
def e418(error):
1421
return flask.render_template("teapot.html"), 418
1422
1423
1424
@app.errorhandler(405)
1425
def e405(error):
1426
return flask.render_template("method-not-allowed.html"), 405
1427
1428
1429
if __name__ == "__main__":
1430
app.run(debug=True, port=8080, host="0.0.0.0")
1431
1432
app.register_blueprint(repositories)
1433