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