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