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.41 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
serverUserdataLocation = os.path.join(config.USERDATA_PATH, username)
464
465
if not os.path.exists(serverUserdataLocation):
466
return flask.render_template("not-found.html"), 404
467
468
return flask.send_from_directory(serverUserdataLocation, "avatar.png")
469
470
471
@app.route("/<username>/<repository>/raw/<branch>/<path:subpath>")
472
def repository_raw(username, repository, branch, subpath):
473
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
474
if not os.path.exists(server_repo_location):
475
app.logger.error(f"Cannot load {server_repo_location}")
476
flask.abort(404)
477
if not (get_visibility(username, repository) or get_permission_level(
478
flask.session.get("username"), username,
479
repository) is not None):
480
flask.abort(403)
481
482
app.logger.info(f"Loading {server_repo_location}")
483
484
if not os.path.exists(server_repo_location):
485
app.logger.error(f"Cannot load {server_repo_location}")
486
return flask.render_template("not-found.html"), 404
487
488
repo = git.Repo(server_repo_location)
489
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
490
if not repo_data.default_branch:
491
if repo.heads:
492
repo_data.default_branch = repo.heads[0].name
493
else:
494
return flask.render_template("empty.html",
495
remote=f"http://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
496
if not branch:
497
branch = repo_data.default_branch
498
return flask.redirect(f"./{branch}", code=302)
499
500
if branch.startswith("tag:"):
501
ref = f"tags/{branch[4:]}"
502
elif branch.startswith("~"):
503
ref = branch[1:]
504
else:
505
ref = f"heads/{branch}"
506
507
ref = ref.replace("~", "/") # encode slashes for URL support
508
509
try:
510
repo.git.checkout("-f", ref)
511
except git.exc.GitCommandError:
512
return flask.render_template("not-found.html"), 404
513
514
return flask.send_from_directory(config.REPOS_PATH,
515
os.path.join(username, repository, subpath))
516
517
518
@repositories.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""})
519
@repositories.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""})
520
@repositories.route("/<username>/<repository>/tree/<branch>/<path:subpath>")
521
def repository_tree(username, repository, branch, subpath):
522
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
523
if not os.path.exists(server_repo_location):
524
app.logger.error(f"Cannot load {server_repo_location}")
525
flask.abort(404)
526
if not (get_visibility(username, repository) or get_permission_level(
527
flask.session.get("username"), username,
528
repository) is not None):
529
flask.abort(403)
530
531
app.logger.info(f"Loading {server_repo_location}")
532
533
repo = git.Repo(server_repo_location)
534
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
535
if not repo_data.default_branch:
536
if repo.heads:
537
repo_data.default_branch = repo.heads[0].name
538
else:
539
return flask.render_template("empty.html",
540
remote=f"{config.www_protocol}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
541
if not branch:
542
branch = repo_data.default_branch
543
return flask.redirect(f"./{branch}", code=302)
544
545
if branch.startswith("tag:"):
546
ref = f"tags/{branch[4:]}"
547
elif branch.startswith("~"):
548
ref = branch[1:]
549
else:
550
ref = f"heads/{branch}"
551
552
ref = ref.replace("~", "/") # encode slashes for URL support
553
554
try:
555
repo.git.checkout("-f", ref)
556
except git.exc.GitCommandError:
557
return flask.render_template("not-found.html"), 404
558
559
branches = repo.heads
560
561
all_refs = []
562
for ref in repo.heads:
563
all_refs.append((ref, "head"))
564
for ref in repo.tags:
565
all_refs.append((ref, "tag"))
566
567
if os.path.isdir(os.path.join(server_repo_location, subpath)):
568
files = []
569
blobs = []
570
571
for entry in os.listdir(os.path.join(server_repo_location, subpath)):
572
if not os.path.basename(entry) == ".git":
573
files.append(os.path.join(subpath, entry))
574
575
infos = []
576
577
for file in files:
578
path = os.path.join(server_repo_location, file)
579
mimetype = guess_mime(path)
580
581
text = git_command(server_repo_location, None, "log", "--format='%H\n'",
582
shlex.quote(file)).decode()
583
584
sha = text.split("\n")[0]
585
identifier = f"/{username}/{repository}/{sha}"
586
587
last_commit = db.session.get(Commit, identifier)
588
589
info = {
590
"name": os.path.basename(file),
591
"serverPath": path,
592
"relativePath": file,
593
"link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file),
594
"size": human_size(os.path.getsize(path)),
595
"mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}",
596
"commit": last_commit,
597
"shaSize": 7,
598
}
599
600
special_icon = config.match_icon(os.path.basename(file))
601
if special_icon:
602
info["icon"] = special_icon
603
elif os.path.isdir(path):
604
info["icon"] = config.folder_icon
605
elif mimetypes.guess_type(path)[0] in config.file_icons:
606
info["icon"] = config.file_icons[mimetypes.guess_type(path)[0]]
607
else:
608
info["icon"] = config.unknown_icon
609
610
if os.path.isdir(path):
611
infos.insert(0, info)
612
else:
613
infos.append(info)
614
615
return flask.render_template(
616
"repo-tree.html",
617
username=username,
618
repository=repository,
619
files=infos,
620
subpath=os.path.join("/", subpath),
621
branches=all_refs,
622
current=branch,
623
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
624
is_favourite=get_favourite(flask.session.get("username"), username, repository)
625
)
626
else:
627
path = os.path.join(server_repo_location, subpath)
628
629
if not os.path.exists(path):
630
return flask.render_template("not-found.html"), 404
631
632
mimetype = guess_mime(path)
633
mode = mimetype.split("/", 1)[0]
634
size = human_size(os.path.getsize(path))
635
636
special_icon = config.match_icon(os.path.basename(path))
637
if special_icon:
638
icon = special_icon
639
elif os.path.isdir(path):
640
icon = config.folder_icon
641
elif mimetypes.guess_type(path)[0] in config.file_icons:
642
icon = config.file_icons[mimetypes.guess_type(path)[0]]
643
else:
644
icon = config.unknown_icon
645
646
contents = None
647
if mode == "text":
648
contents = convert_to_html(path)
649
650
return flask.render_template(
651
"repo-file.html",
652
username=username,
653
repository=repository,
654
file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath),
655
branches=all_refs,
656
current=branch,
657
mode=mode,
658
mimetype=mimetype,
659
detailedtype=magic.from_file(path),
660
size=size,
661
icon=icon,
662
subpath=os.path.join("/", subpath),
663
extension=pathlib.Path(path).suffix,
664
basename=os.path.basename(path),
665
contents=contents,
666
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
667
is_favourite=get_favourite(flask.session.get("username"), username, repository)
668
)
669
670
671
@repositories.route("/<username>/<repository>/commit/<sha>")
672
def repository_commit(username, repository, sha):
673
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
674
if not os.path.exists(server_repo_location):
675
app.logger.error(f"Cannot load {server_repo_location}")
676
flask.abort(404)
677
if not (get_visibility(username, repository) or get_permission_level(
678
flask.session.get("username"), username,
679
repository) is not None):
680
flask.abort(403)
681
682
app.logger.info(f"Loading {server_repo_location}")
683
684
if not os.path.exists(server_repo_location):
685
app.logger.error(f"Cannot load {server_repo_location}")
686
return flask.render_template("not-found.html"), 404
687
688
repo = git.Repo(server_repo_location)
689
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
690
691
files = git_command(os.path.join(server_repo_location, ".git"), None, "diff-tree", "-r",
692
"--name-only", "--no-commit-id", sha).decode().split("\n")[:-1]
693
694
print(files)
695
696
return flask.render_template(
697
"repo-commit.html",
698
username=username,
699
repository=repository,
700
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
701
is_favourite=get_favourite(flask.session.get("username"), username, repository),
702
diff={file: git_command(os.path.join(server_repo_location, ".git"), None, "diff",
703
str(sha) + "^!", "--", file).decode().split("\n") for
704
file in files},
705
data=db.session.get(Commit, f"/{username}/{repository}/{sha}"),
706
)
707
708
709
@repositories.route("/<username>/<repository>/forum/")
710
def repository_forum(username, repository):
711
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
712
if not os.path.exists(server_repo_location):
713
app.logger.error(f"Cannot load {server_repo_location}")
714
flask.abort(404)
715
if not (get_visibility(username, repository) or get_permission_level(
716
flask.session.get("username"), username,
717
repository) is not None):
718
flask.abort(403)
719
720
app.logger.info(f"Loading {server_repo_location}")
721
722
if not os.path.exists(server_repo_location):
723
app.logger.error(f"Cannot load {server_repo_location}")
724
return flask.render_template("not-found.html"), 404
725
726
repo = git.Repo(server_repo_location)
727
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
728
user = User.query.filter_by(username=flask.session.get("username")).first()
729
relationships = RepoAccess.query.filter_by(repo=repo_data)
730
user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first()
731
732
return flask.render_template(
733
"repo-forum.html",
734
username=username,
735
repository=repository,
736
repo_data=repo_data,
737
relationships=relationships,
738
repo=repo,
739
user_relationship=user_relationship,
740
Post=Post,
741
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
742
is_favourite=get_favourite(flask.session.get("username"), username, repository),
743
default_branch=repo_data.default_branch
744
)
745
746
747
@repositories.route("/<username>/<repository>/forum/topic/<int:id>")
748
def repository_forum_topic(username, repository, id):
749
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
750
if not os.path.exists(server_repo_location):
751
app.logger.error(f"Cannot load {server_repo_location}")
752
flask.abort(404)
753
if not (get_visibility(username, repository) or get_permission_level(
754
flask.session.get("username"), username,
755
repository) is not None):
756
flask.abort(403)
757
758
app.logger.info(f"Loading {server_repo_location}")
759
760
if not os.path.exists(server_repo_location):
761
app.logger.error(f"Cannot load {server_repo_location}")
762
return flask.render_template("not-found.html"), 404
763
764
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
765
user = User.query.filter_by(username=flask.session.get("username")).first()
766
relationships = RepoAccess.query.filter_by(repo=repo_data)
767
user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first()
768
769
post = Post.query.filter_by(id=id).first()
770
771
return flask.render_template(
772
"repo-topic.html",
773
username=username,
774
repository=repository,
775
repo_data=repo_data,
776
relationships=relationships,
777
user_relationship=user_relationship,
778
post=post,
779
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
780
is_favourite=get_favourite(flask.session.get("username"), username, repository),
781
default_branch=repo_data.default_branch
782
)
783
784
785
@repositories.route("/<username>/<repository>/forum/new", methods=["POST", "GET"])
786
def repository_forum_new(username, repository):
787
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
788
if not os.path.exists(server_repo_location):
789
app.logger.error(f"Cannot load {server_repo_location}")
790
flask.abort(404)
791
if not (get_visibility(username, repository) or get_permission_level(
792
flask.session.get("username"), username,
793
repository) is not None):
794
flask.abort(403)
795
796
app.logger.info(f"Loading {server_repo_location}")
797
798
if not os.path.exists(server_repo_location):
799
app.logger.error(f"Cannot load {server_repo_location}")
800
return flask.render_template("not-found.html"), 404
801
802
repo = git.Repo(server_repo_location)
803
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
804
user = User.query.filter_by(username=flask.session.get("username")).first()
805
relationships = RepoAccess.query.filter_by(repo=repo_data)
806
user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first()
807
808
post = Post(user, repo_data, None, flask.request.form["subject"],
809
flask.request.form["message"])
810
811
db.session.add(post)
812
db.session.commit()
813
814
return flask.redirect(
815
flask.url_for(".repository_forum_thread", username=username, repository=repository,
816
post_id=post.number),
817
code=303)
818
819
820
@repositories.route("/<username>/<repository>/forum/<int:post_id>")
821
def repository_forum_thread(username, repository, post_id):
822
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
823
if not os.path.exists(server_repo_location):
824
app.logger.error(f"Cannot load {server_repo_location}")
825
flask.abort(404)
826
if not (get_visibility(username, repository) or get_permission_level(
827
flask.session.get("username"), username,
828
repository) is not None):
829
flask.abort(403)
830
831
app.logger.info(f"Loading {server_repo_location}")
832
833
if not os.path.exists(server_repo_location):
834
app.logger.error(f"Cannot load {server_repo_location}")
835
return flask.render_template("not-found.html"), 404
836
837
repo = git.Repo(server_repo_location)
838
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
839
user = User.query.filter_by(username=flask.session.get("username")).first()
840
relationships = RepoAccess.query.filter_by(repo=repo_data)
841
user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first()
842
843
return flask.render_template(
844
"repo-forum-thread.html",
845
username=username,
846
repository=repository,
847
repo_data=repo_data,
848
relationships=relationships,
849
repo=repo,
850
Post=Post,
851
user_relationship=user_relationship,
852
post_id=post_id,
853
max_post_nesting=4,
854
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
855
is_favourite=get_favourite(flask.session.get("username"), username, repository),
856
parent=Post.query.filter_by(repo=repo_data, number=post_id).first(),
857
has_permission=not ((not get_permission_level(flask.session.get("username"), username,
858
repository)) and db.session.get(Post,
859
f"/{username}/{repository}/{post_id}").owner.username != flask.session.get("username")),
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_permission_level(flask.session.get("username"), username, repository)) and db.session.get(Post, f"/{username}/{repository}/{post_id}").owner.username != flask.session.get("username"):
871
flask.abort(403)
872
873
app.logger.info(f"Loading {server_repo_location}")
874
875
repo = git.Repo(server_repo_location)
876
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
877
user = User.query.filter_by(username=flask.session.get("username")).first()
878
relationships = RepoAccess.query.filter_by(repo=repo_data)
879
user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first()
880
881
post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first()
882
883
if not post:
884
flask.abort(404)
885
886
post.state = int(flask.request.form["new-state"])
887
888
db.session.commit()
889
890
return flask.redirect(
891
flask.url_for(".repository_forum_thread", username=username, repository=repository,
892
post_id=post_id),
893
code=303)
894
895
896
@repositories.route("/<username>/<repository>/forum/<int:post_id>/reply", methods=["POST"])
897
def repository_forum_reply(username, repository, post_id):
898
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
899
if not os.path.exists(server_repo_location):
900
app.logger.error(f"Cannot load {server_repo_location}")
901
flask.abort(404)
902
if not (get_visibility(username, repository) or get_permission_level(
903
flask.session.get("username"), username,
904
repository) is not None):
905
flask.abort(403)
906
907
app.logger.info(f"Loading {server_repo_location}")
908
909
if not os.path.exists(server_repo_location):
910
app.logger.error(f"Cannot load {server_repo_location}")
911
return flask.render_template("not-found.html"), 404
912
913
repo = git.Repo(server_repo_location)
914
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
915
user = User.query.filter_by(username=flask.session.get("username")).first()
916
relationships = RepoAccess.query.filter_by(repo=repo_data)
917
user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first()
918
if not user:
919
flask.abort(401)
920
921
parent = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first()
922
post = Post(user, repo_data, parent, flask.request.form["subject"],
923
flask.request.form["message"])
924
925
db.session.add(post)
926
post.update_date()
927
db.session.commit()
928
929
return flask.redirect(
930
flask.url_for(".repository_forum_thread", username=username, repository=repository,
931
post_id=post_id),
932
code=303)
933
934
935
@repositories.route("/<username>/<repository>/forum/<int:post_id>/voteup",
936
defaults={"score": 1})
937
@repositories.route("/<username>/<repository>/forum/<int:post_id>/votedown",
938
defaults={"score": -1})
939
@repositories.route("/<username>/<repository>/forum/<int:post_id>/votes", defaults={"score": 0})
940
def repository_forum_vote(username, repository, post_id, score):
941
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
942
if not os.path.exists(server_repo_location):
943
app.logger.error(f"Cannot load {server_repo_location}")
944
flask.abort(404)
945
if not (get_visibility(username, repository) or get_permission_level(
946
flask.session.get("username"), username,
947
repository) is not None):
948
flask.abort(403)
949
950
app.logger.info(f"Loading {server_repo_location}")
951
952
if not os.path.exists(server_repo_location):
953
app.logger.error(f"Cannot load {server_repo_location}")
954
return flask.render_template("not-found.html"), 404
955
956
repo = git.Repo(server_repo_location)
957
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
958
user = User.query.filter_by(username=flask.session.get("username")).first()
959
relationships = RepoAccess.query.filter_by(repo=repo_data)
960
user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first()
961
if not user:
962
flask.abort(401)
963
964
post = Post.query.filter_by(identifier=f"/{username}/{repository}/{post_id}").first()
965
966
if score:
967
old_relationship = PostVote.query.filter_by(user_username=user.username,
968
post_identifier=post.identifier).first()
969
if old_relationship:
970
if score == old_relationship.vote_score:
971
db.session.delete(old_relationship)
972
post.vote_sum -= old_relationship.vote_score
973
else:
974
post.vote_sum -= old_relationship.vote_score
975
post.vote_sum += score
976
old_relationship.vote_score = score
977
else:
978
relationship = PostVote(user, post, score)
979
post.vote_sum += score
980
db.session.add(relationship)
981
982
db.session.commit()
983
984
user_vote = PostVote.query.filter_by(user_username=user.username,
985
post_identifier=post.identifier).first()
986
response = flask.make_response(
987
str(post.vote_sum) + " " + str(user_vote.vote_score if user_vote else 0))
988
response.content_type = "text/plain"
989
990
return response
991
992
993
@repositories.route("/<username>/<repository>/favourite")
994
def repository_favourite(username, repository):
995
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
996
if not os.path.exists(server_repo_location):
997
app.logger.error(f"Cannot load {server_repo_location}")
998
flask.abort(404)
999
if not (get_visibility(username, repository) or get_permission_level(
1000
flask.session.get("username"), username,
1001
repository) is not None):
1002
flask.abort(403)
1003
1004
app.logger.info(f"Loading {server_repo_location}")
1005
1006
if not os.path.exists(server_repo_location):
1007
app.logger.error(f"Cannot load {server_repo_location}")
1008
return flask.render_template("not-found.html"), 404
1009
1010
repo = git.Repo(server_repo_location)
1011
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1012
user = User.query.filter_by(username=flask.session.get("username")).first()
1013
relationships = RepoAccess.query.filter_by(repo=repo_data)
1014
user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first()
1015
if not user:
1016
flask.abort(401)
1017
1018
old_relationship = RepoFavourite.query.filter_by(user_username=user.username,
1019
repo_route=repo_data.route).first()
1020
if old_relationship:
1021
db.session.delete(old_relationship)
1022
else:
1023
relationship = RepoFavourite(user, repo_data)
1024
db.session.add(relationship)
1025
1026
db.session.commit()
1027
1028
return flask.redirect(flask.url_for("favourites"), code=303)
1029
1030
1031
@repositories.route("/<username>/<repository>/users/", methods=["GET", "POST"])
1032
def repository_users(username, repository):
1033
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1034
if not os.path.exists(server_repo_location):
1035
app.logger.error(f"Cannot load {server_repo_location}")
1036
flask.abort(404)
1037
if not (get_visibility(username, repository) or get_permission_level(
1038
flask.session.get("username"), username,
1039
repository) is not None):
1040
flask.abort(403)
1041
1042
app.logger.info(f"Loading {server_repo_location}")
1043
1044
if not os.path.exists(server_repo_location):
1045
app.logger.error(f"Cannot load {server_repo_location}")
1046
return flask.render_template("not-found.html"), 404
1047
1048
repo = git.Repo(server_repo_location)
1049
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1050
user = User.query.filter_by(username=flask.session.get("username")).first()
1051
relationships = RepoAccess.query.filter_by(repo=repo_data)
1052
user_relationship = RepoAccess.query.filter_by(repo=repo_data, user=user).first()
1053
1054
if flask.request.method == "GET":
1055
return flask.render_template(
1056
"repo-users.html",
1057
username=username,
1058
repository=repository,
1059
repo_data=repo_data,
1060
relationships=relationships,
1061
repo=repo,
1062
user_relationship=user_relationship,
1063
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
1064
is_favourite=get_favourite(flask.session.get("username"), username, repository)
1065
)
1066
else:
1067
if get_permission_level(flask.session.get("username"), username, repository) != 2:
1068
flask.abort(401)
1069
1070
if flask.request.form.get("new-username"):
1071
# Create new relationship
1072
new_user = User.query.filter_by(
1073
username=flask.request.form.get("new-username")).first()
1074
relationship = RepoAccess(new_user, repo_data, flask.request.form.get("new-level"))
1075
db.session.add(relationship)
1076
db.session.commit()
1077
if flask.request.form.get("update-username"):
1078
# Create new relationship
1079
updated_user = User.query.filter_by(
1080
username=flask.request.form.get("update-username")).first()
1081
relationship = RepoAccess.query.filter_by(repo=repo_data, user=updated_user).first()
1082
if flask.request.form.get("update-level") == -1:
1083
relationship.delete()
1084
else:
1085
relationship.access_level = flask.request.form.get("update-level")
1086
db.session.commit()
1087
1088
return flask.redirect(
1089
app.url_for(".repository_users", username=username, repository=repository))
1090
1091
1092
@repositories.route("/<username>/<repository>/branches/")
1093
def repository_branches(username, repository):
1094
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1095
if not os.path.exists(server_repo_location):
1096
app.logger.error(f"Cannot load {server_repo_location}")
1097
flask.abort(404)
1098
if not (get_visibility(username, repository) or get_permission_level(
1099
flask.session.get("username"), username,
1100
repository) is not None):
1101
flask.abort(403)
1102
1103
app.logger.info(f"Loading {server_repo_location}")
1104
1105
if not os.path.exists(server_repo_location):
1106
app.logger.error(f"Cannot load {server_repo_location}")
1107
return flask.render_template("not-found.html"), 404
1108
1109
repo = git.Repo(server_repo_location)
1110
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1111
1112
return flask.render_template(
1113
"repo-branches.html",
1114
username=username,
1115
repository=repository,
1116
repo_data=repo_data,
1117
repo=repo,
1118
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
1119
is_favourite=get_favourite(flask.session.get("username"), username, repository)
1120
)
1121
1122
1123
@repositories.route("/<username>/<repository>/log/", defaults={"branch": None})
1124
@repositories.route("/<username>/<repository>/log/<branch>/")
1125
def repository_log(username, repository, branch):
1126
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1127
if not os.path.exists(server_repo_location):
1128
app.logger.error(f"Cannot load {server_repo_location}")
1129
flask.abort(404)
1130
if not (get_visibility(username, repository) or get_permission_level(
1131
flask.session.get("username"), username,
1132
repository) is not None):
1133
flask.abort(403)
1134
1135
app.logger.info(f"Loading {server_repo_location}")
1136
1137
if not os.path.exists(server_repo_location):
1138
app.logger.error(f"Cannot load {server_repo_location}")
1139
return flask.render_template("not-found.html"), 404
1140
1141
repo = git.Repo(server_repo_location)
1142
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1143
if not repo_data.default_branch:
1144
if repo.heads:
1145
repo_data.default_branch = repo.heads[0].name
1146
else:
1147
return flask.render_template("empty.html",
1148
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
1149
if not branch:
1150
branch = repo_data.default_branch
1151
return flask.redirect(f"./{branch}", code=302)
1152
1153
if branch.startswith("tag:"):
1154
ref = f"tags/{branch[4:]}"
1155
elif branch.startswith("~"):
1156
ref = branch[1:]
1157
else:
1158
ref = f"heads/{branch}"
1159
1160
ref = ref.replace("~", "/") # encode slashes for URL support
1161
1162
try:
1163
repo.git.checkout("-f", ref)
1164
except git.exc.GitCommandError:
1165
return flask.render_template("not-found.html"), 404
1166
1167
branches = repo.heads
1168
1169
all_refs = []
1170
for ref in repo.heads:
1171
all_refs.append((ref, "head"))
1172
for ref in repo.tags:
1173
all_refs.append((ref, "tag"))
1174
1175
commit_list = [f"/{username}/{repository}/{sha}" for sha in
1176
git_command(server_repo_location, None, "log",
1177
"--format='%H'").decode().split("\n")]
1178
1179
commits = Commit.query.filter(Commit.identifier.in_(commit_list))
1180
1181
return flask.render_template(
1182
"repo-log.html",
1183
username=username,
1184
repository=repository,
1185
branches=all_refs,
1186
current=branch,
1187
repo_data=repo_data,
1188
repo=repo,
1189
commits=commits,
1190
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
1191
is_favourite=get_favourite(flask.session.get("username"), username, repository)
1192
)
1193
1194
1195
@repositories.route("/<username>/<repository>/prs/", methods=["GET", "POST"])
1196
def repository_prs(username, repository):
1197
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1198
if not os.path.exists(server_repo_location):
1199
app.logger.error(f"Cannot load {server_repo_location}")
1200
flask.abort(404)
1201
if not (get_visibility(username, repository) or get_permission_level(
1202
flask.session.get("username"), username,
1203
repository) is not None):
1204
flask.abort(403)
1205
1206
app.logger.info(f"Loading {server_repo_location}")
1207
1208
if not os.path.exists(server_repo_location):
1209
app.logger.error(f"Cannot load {server_repo_location}")
1210
return flask.render_template("not-found.html"), 404
1211
1212
if flask.request.method == "GET":
1213
repo = git.Repo(server_repo_location)
1214
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1215
user = User.query.filter_by(username=flask.session.get("username")).first()
1216
1217
return flask.render_template(
1218
"repo-prs.html",
1219
username=username,
1220
repository=repository,
1221
repo_data=repo_data,
1222
repo=repo,
1223
PullRequest=PullRequest,
1224
remote=f"http{'s' if config.suggest_https else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
1225
is_favourite=get_favourite(flask.session.get("username"), username, repository),
1226
default_branch=repo_data.default_branch,
1227
branches=repo.branches
1228
)
1229
1230
else:
1231
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1232
head = flask.request.form.get("head")
1233
head_route = flask.request.form.get("headroute")
1234
base = flask.request.form.get("base")
1235
1236
if not head and base and head_route:
1237
return flask.redirect(".", 400)
1238
1239
head_repo = git.Repo(os.path.join(config.REPOS_PATH, head_route.lstrip("/")))
1240
base_repo = git.Repo(server_repo_location)
1241
print(head_repo)
1242
1243
if head not in head_repo.branches or base not in base_repo.branches:
1244
flask.flash(Markup(
1245
"<iconify-icon icon='mdi:error'></iconify-icon>" + _("Bad branch name")),
1246
category="error")
1247
return flask.redirect(".", 303)
1248
1249
head_data = db.session.get(Repo, head_route)
1250
if not head_data.visibility:
1251
flask.flash(Markup(
1252
"<iconify-icon icon='mdi:error'></iconify-icon>" + _(
1253
"Head can't be restricted")),
1254
category="error")
1255
return flask.redirect(".", 303)
1256
1257
pull_request = PullRequest(repo_data, head, head_data, base,
1258
db.session.get(User, flask.session["username"]))
1259
1260
db.session.add(pull_request)
1261
db.session.commit()
1262
1263
return flask.redirect(".", 303)
1264
1265
1266
@repositories.route("/<username>/<repository>/prs/merge", methods=["POST"])
1267
def repository_prs_merge(username, repository):
1268
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1269
if not os.path.exists(server_repo_location):
1270
app.logger.error(f"Cannot load {server_repo_location}")
1271
flask.abort(404)
1272
if not (get_visibility(username, repository) or get_permission_level(
1273
flask.session.get("username"), username,
1274
repository) is not None):
1275
flask.abort(403)
1276
1277
if not get_permission_level(flask.session.get("username"), username, repository):
1278
flask.abort(401)
1279
1280
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1281
repo = git.Repo(server_repo_location)
1282
id = flask.request.form.get("id")
1283
1284
pull_request = db.session.get(PullRequest, id)
1285
1286
if pull_request:
1287
result = celery_tasks.merge_heads.delay(
1288
pull_request.head_route,
1289
pull_request.head_branch,
1290
pull_request.base_route,
1291
pull_request.base_branch,
1292
simulate=True
1293
)
1294
task_result = worker.AsyncResult(result.id)
1295
1296
return flask.redirect(f"/task/{result.id}?pr-id={id}", 303)
1297
# db.session.delete(pull_request)
1298
# db.session.commit()
1299
else:
1300
flask.abort(400)
1301
1302
1303
@repositories.route("/<username>/<repository>/prs/<int:id>/merge")
1304
def repository_prs_merge_stage_two(username, repository, id):
1305
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1306
if not os.path.exists(server_repo_location):
1307
app.logger.error(f"Cannot load {server_repo_location}")
1308
flask.abort(404)
1309
if not (get_visibility(username, repository) or get_permission_level(
1310
flask.session.get("username"), username,
1311
repository) is not None):
1312
flask.abort(403)
1313
1314
if not get_permission_level(flask.session.get("username"), username, repository):
1315
flask.abort(401)
1316
1317
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1318
repo = git.Repo(server_repo_location)
1319
1320
pull_request = db.session.get(PullRequest, id)
1321
1322
if pull_request:
1323
result = celery_tasks.merge_heads.delay(
1324
pull_request.head_route,
1325
pull_request.head_branch,
1326
pull_request.base_route,
1327
pull_request.base_branch,
1328
simulate=False
1329
)
1330
task_result = worker.AsyncResult(result.id)
1331
1332
pull_request.state = 1
1333
db.session.commit()
1334
1335
return flask.redirect(f"/task/{result.id}?pr-id={id}", 303)
1336
# db.session.delete(pull_request)
1337
else:
1338
flask.abort(400)
1339
1340
1341
@app.route("/task/<task_id>")
1342
def task_monitor(task_id):
1343
task_result = worker.AsyncResult(task_id)
1344
print(task_result.status)
1345
1346
return flask.render_template("task-monitor.html", result=task_result)
1347
1348
1349
@repositories.route("/<username>/<repository>/prs/delete", methods=["POST"])
1350
def repository_prs_delete(username, repository):
1351
server_repo_location = os.path.join(config.REPOS_PATH, username, repository)
1352
if not os.path.exists(server_repo_location):
1353
app.logger.error(f"Cannot load {server_repo_location}")
1354
flask.abort(404)
1355
if not (get_visibility(username, repository) or get_permission_level(
1356
flask.session.get("username"), username,
1357
repository) is not None):
1358
flask.abort(403)
1359
1360
if not get_permission_level(flask.session.get("username"), username, repository):
1361
flask.abort(401)
1362
1363
repo_data = Repo.query.filter_by(route=f"/{username}/{repository}").first()
1364
repo = git.Repo(server_repo_location)
1365
id = flask.request.form.get("id")
1366
1367
pull_request = db.session.get(PullRequest, id)
1368
1369
if pull_request:
1370
pull_request.state = 2
1371
db.session.commit()
1372
1373
return flask.redirect(".", 303)
1374
1375
1376
@repositories.route("/<username>/<repository>/settings/")
1377
def repository_settings(username, repository):
1378
if get_permission_level(flask.session.get("username"), username, repository) != 2:
1379
flask.abort(401)
1380
1381
return flask.render_template("repo-settings.html", username=username, repository=repository)
1382
1383
1384
@app.errorhandler(404)
1385
def e404(error):
1386
return flask.render_template("not-found.html"), 404
1387
1388
1389
@app.errorhandler(401)
1390
def e401(error):
1391
return flask.render_template("unauthorised.html"), 401
1392
1393
1394
@app.errorhandler(403)
1395
def e403(error):
1396
return flask.render_template("forbidden.html"), 403
1397
1398
1399
@app.errorhandler(418)
1400
def e418(error):
1401
return flask.render_template("teapot.html"), 418
1402
1403
1404
@app.errorhandler(405)
1405
def e405(error):
1406
return flask.render_template("method-not-allowed.html"), 405
1407
1408
1409
if __name__ == "__main__":
1410
app.run(debug=True, port=8080, host="0.0.0.0")
1411
1412
app.register_blueprint(repositories)
1413