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