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