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.61 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)).order_by(Commit.author_date.desc())
1200
page_number = flask.request.args.get("page", 1, type=int)
1201
page_length = flask.request.args.get("per_page", 2, type=int)
1202
page_listing = db.paginate(commits, page=page_number, per_page=page_length)
1203
1204
if page_listing.has_next:
1205
next_page = page_listing.next_num
1206
else:
1207
next_page = None
1208
1209
if page_listing.has_prev:
1210
prev_page = page_listing.prev_num
1211
else:
1212
prev_page = None
1213
1214
return flask.render_template(
1215
"repo-log.html",
1216
username=username,
1217
repository=repository,
1218
branches=all_refs,
1219
current=branch,
1220
repo_data=repo_data,
1221
repo=repo,
1222
commits=page_listing,
1223
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
1224
is_favourite=get_favourite(flask.session.get("username"), username, repository),
1225
page_number=page_number,
1226
page_length=page_length,
1227
next_page=next_page,
1228
prev_page=prev_page
1229
)
1230
1231
1232
@repositories.route("/<username>/<repository>/prs/", methods=["GET", "POST"])
1233
def repository_prs(username, repository):
1234
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1235
if not os.path.exists(server_repo_location):
1236
app.logger.error(f"Cannot load {server_repo_location}")
1237
flask.abort(404)
1238
if not (get_visibility(username, repository) or get_permission_level(
1239
flask.session.get("username"), username,
1240
repository) is not None):
1241
flask.abort(403)
1242
1243
app.logger.info(f"Loading {server_repo_location}")
1244
1245
if not os.path.exists(server_repo_location):
1246
app.logger.error(f"Cannot load {server_repo_location}")
1247
return flask.render_template("not-found.html"), 404
1248
1249
if flask.request.method == "GET":
1250
repo = git.Repo(server_repo_location)
1251
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1252
user = User.query.filter_by(username=flask.session.get("username")).first()
1253
1254
return flask.render_template(
1255
"repo-prs.html",
1256
username=username,
1257
repository=repository,
1258
repo_data=repo_data,
1259
repo=repo,
1260
PullRequest=PullRequest,
1261
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
1262
is_favourite=get_favourite(flask.session.get("username"), username, repository),
1263
default_branch=repo_data.default_branch,
1264
branches=repo.branches
1265
)
1266
1267
else:
1268
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1269
head = flask.request.form.get("head")
1270
head_route = flask.request.form.get("headroute")
1271
base = flask.request.form.get("base")
1272
1273
if not head and base and head_route:
1274
return flask.redirect(".", 400)
1275
1276
head_repo = git.Repo(os.path.join(config.REPOS_PATH, head_route.lstrip("/")))
1277
base_repo = git.Repo(server_repo_location)
1278
print(head_repo)
1279
1280
if head not in head_repo.branches or base not in base_repo.branches:
1281
flask.flash(Markup(
1282
"<iconify-icon icon='mdi:error'></iconify-icon>" + _("Bad branch name")),
1283
category="error")
1284
return flask.redirect(".", 303)
1285
1286
head_data = db.session.get(Repo, head_route)
1287
if not head_data.visibility:
1288
flask.flash(Markup(
1289
"<iconify-icon icon='mdi:error'></iconify-icon>" + _(
1290
"Head can't be restricted")),
1291
category="error")
1292
return flask.redirect(".", 303)
1293
1294
pull_request = PullRequest(repo_data, head, head_data, base,
1295
db.session.get(User, flask.session["username"]))
1296
1297
db.session.add(pull_request)
1298
db.session.commit()
1299
1300
return flask.redirect(".", 303)
1301
1302
1303
@repositories.route("/<username>/<repository>/prs/merge", methods=["POST"])
1304
def repository_prs_merge(username, repository):
1305
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1306
if not os.path.exists(server_repo_location):
1307
app.logger.error(f"Cannot load {server_repo_location}")
1308
flask.abort(404)
1309
if not (get_visibility(username, repository) or get_permission_level(
1310
flask.session.get("username"), username,
1311
repository) is not None):
1312
flask.abort(403)
1313
1314
if not get_permission_level(flask.session.get("username"), username, repository):
1315
flask.abort(401)
1316
1317
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1318
repo = git.Repo(server_repo_location)
1319
id = flask.request.form.get("id")
1320
1321
pull_request = db.session.get(PullRequest, id)
1322
1323
if pull_request:
1324
result = celery_tasks.merge_heads.delay(
1325
pull_request.head_route,
1326
pull_request.head_branch,
1327
pull_request.base_route,
1328
pull_request.base_branch,
1329
simulate=True
1330
)
1331
task_result = worker.AsyncResult(result.id)
1332
1333
return flask.redirect(f"/task/{result.id}?pr-id={id}", 303)
1334
# db.session.delete(pull_request)
1335
# db.session.commit()
1336
else:
1337
flask.abort(400)
1338
1339
1340
@repositories.route("/<username>/<repository>/prs/<int:id>/merge")
1341
def repository_prs_merge_stage_two(username, repository, id):
1342
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1343
if not os.path.exists(server_repo_location):
1344
app.logger.error(f"Cannot load {server_repo_location}")
1345
flask.abort(404)
1346
if not (get_visibility(username, repository) or get_permission_level(
1347
flask.session.get("username"), username,
1348
repository) is not None):
1349
flask.abort(403)
1350
1351
if not get_permission_level(flask.session.get("username"), username, repository):
1352
flask.abort(401)
1353
1354
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1355
repo = git.Repo(server_repo_location)
1356
1357
pull_request = db.session.get(PullRequest, id)
1358
1359
if pull_request:
1360
result = celery_tasks.merge_heads.delay(
1361
pull_request.head_route,
1362
pull_request.head_branch,
1363
pull_request.base_route,
1364
pull_request.base_branch,
1365
simulate=False
1366
)
1367
task_result = worker.AsyncResult(result.id)
1368
1369
pull_request.state = 1
1370
db.session.commit()
1371
1372
return flask.redirect(f"/task/{result.id}?pr-id={id}", 303)
1373
# db.session.delete(pull_request)
1374
else:
1375
flask.abort(400)
1376
1377
1378
@app.route("/task/<task_id>")
1379
def task_monitor(task_id):
1380
task_result = worker.AsyncResult(task_id)
1381
print(task_result.status)
1382
1383
return flask.render_template("task-monitor.html", result=task_result)
1384
1385
1386
@repositories.route("/<username>/<repository>/prs/delete", methods=["POST"])
1387
def repository_prs_delete(username, repository):
1388
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1389
if not os.path.exists(server_repo_location):
1390
app.logger.error(f"Cannot load {server_repo_location}")
1391
flask.abort(404)
1392
if not (get_visibility(username, repository) or get_permission_level(
1393
flask.session.get("username"), username,
1394
repository) is not None):
1395
flask.abort(403)
1396
1397
if not get_permission_level(flask.session.get("username"), username, repository):
1398
flask.abort(401)
1399
1400
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1401
repo = git.Repo(server_repo_location)
1402
id = flask.request.form.get("id")
1403
1404
pull_request = db.session.get(PullRequest, id)
1405
1406
if pull_request:
1407
pull_request.state = 2
1408
db.session.commit()
1409
1410
return flask.redirect(".", 303)
1411
1412
1413
@repositories.route("/<username>/<repository>/settings/")
1414
def repository_settings(username, repository):
1415
if get_permission_level(flask.session.get("username"), username, repository) != 2:
1416
flask.abort(401)
1417
1418
return flask.render_template("repo-settings.html", username=username, repository=repository)
1419
1420
1421
@app.errorhandler(404)
1422
def e404(error):
1423
return flask.render_template("not-found.html"), 404
1424
1425
1426
@app.errorhandler(401)
1427
def e401(error):
1428
return flask.render_template("unauthorised.html"), 401
1429
1430
1431
@app.errorhandler(403)
1432
def e403(error):
1433
return flask.render_template("forbidden.html"), 403
1434
1435
1436
@app.errorhandler(418)
1437
def e418(error):
1438
return flask.render_template("teapot.html"), 418
1439
1440
1441
@app.errorhandler(405)
1442
def e405(error):
1443
return flask.render_template("method-not-allowed.html"), 405
1444
1445
1446
if __name__ == "__main__":
1447
app.run(debug=True, port=8080, host="0.0.0.0")
1448
1449
app.register_blueprint(repositories)
1450