By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 app.py

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