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 • 35.11 kiB
Python script, ASCII text executable
        
            
1
import os
2
import random
3
import subprocess
4
from functools import wraps
5
6
import cairosvg
7
import flask
8
from flask_sqlalchemy import SQLAlchemy
9
import git
10
import mimetypes
11
import magic
12
from flask_bcrypt import Bcrypt
13
from markupsafe import escape, Markup
14
from flask_migrate import Migrate
15
from datetime import datetime
16
from enum import Enum
17
import shutil
18
from PIL import Image
19
from cairosvg import svg2png
20
import platform
21
22
import config
23
24
app = flask.Flask(__name__)
25
26
from flask_httpauth import HTTPBasicAuth
27
28
auth = HTTPBasicAuth()
29
30
app.config["SQLALCHEMY_DATABASE_URI"] = config.DB_URI
31
app.config["SECRET_KEY"] = config.DB_PASSWORD
32
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
33
db = SQLAlchemy(app)
34
bcrypt = Bcrypt(app)
35
migrate = Migrate(app, db)
36
from models import *
37
38
39
def gitCommand(repo, data, *args):
40
if not os.path.isdir(repo):
41
raise FileNotFoundError("Repo not found")
42
env = os.environ.copy()
43
44
command = ["git", *args]
45
46
proc = subprocess.Popen(" ".join(command), cwd=repo, env=env, shell=True, stdout=subprocess.PIPE,
47
stdin=subprocess.PIPE)
48
print(command)
49
50
if data:
51
proc.stdin.write(data)
52
53
out, err = proc.communicate()
54
return out
55
56
57
def onlyChars(string, chars):
58
for i in string:
59
if i not in chars:
60
return False
61
return True
62
63
64
def getPermissionLevel(loggedIn, username, repository):
65
user = User.query.filter_by(username=loggedIn).first()
66
repo = Repo.query.filter_by(route=f"/{username}/{repository}").first()
67
68
if user and repo:
69
permission = RepoAccess.query.filter_by(user=user, repo=repo).first()
70
if permission:
71
return permission.accessLevel
72
73
return None
74
75
76
def getVisibility(username, repository):
77
repo = Repo.query.filter_by(route=f"/{username}/{repository}").first()
78
79
if repo:
80
return repo.visibility
81
82
return None
83
84
85
def getFavourite(loggedIn, username, repository):
86
print(loggedIn, username, repository)
87
relationship = RepoFavourite.query.filter_by(userUsername=loggedIn, repoRoute=f"/{username}/{repository}").first()
88
return relationship
89
90
91
import gitHTTP
92
import jinjaUtils
93
94
95
def humanSize(value, decimals=2, scale=1024,
96
units=("B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB")):
97
for unit in units:
98
if value < scale:
99
break
100
value /= scale
101
if int(value) == value:
102
# do not return decimals, if the value is already round
103
return int(value), unit
104
return round(value * 10 ** decimals) / 10 ** decimals, unit
105
106
107
def guessMIME(path):
108
if os.path.isdir(path):
109
mimetype = "inode/directory"
110
elif magic.from_file(path, mime=True):
111
mimetype = magic.from_file(path, mime=True)
112
else:
113
mimetype = "application/octet-stream"
114
return mimetype
115
116
117
def convertToHTML(path):
118
with open(path, "r") as f:
119
contents = f.read()
120
return contents
121
122
123
repositories = flask.Blueprint("repository", __name__, template_folder="templates/repository/")
124
125
126
@app.context_processor
127
def default():
128
username = flask.session.get("username")
129
130
userObject = User.query.filter_by(username=username).first()
131
132
return {
133
"loggedInUser": username,
134
"userObject": userObject,
135
"Notification": Notification,
136
"unread": UserNotification.query.filter_by(userUsername=username).filter(UserNotification.attentionLevel > 0).count()
137
}
138
139
140
@app.route("/")
141
def main():
142
return flask.render_template("home.html")
143
144
145
@app.route("/about/")
146
def about():
147
return flask.render_template("about.html", platform=platform)
148
149
150
@app.route("/settings/", methods=["GET", "POST"])
151
def settings():
152
if not flask.session.get("username"):
153
flask.abort(401)
154
if flask.request.method == "GET":
155
user = User.query.filter_by(username=flask.session.get("username")).first()
156
157
return flask.render_template("user-settings.html", user=user)
158
else:
159
user = User.query.filter_by(username=flask.session.get("username")).first()
160
161
user.displayName = flask.request.form["displayname"]
162
user.URL = flask.request.form["url"]
163
user.company = flask.request.form["company"]
164
user.companyURL = flask.request.form["companyurl"]
165
user.location = flask.request.form["location"]
166
user.showMail = flask.request.form.get("showmail", user.showMail)
167
168
db.session.commit()
169
170
flask.flash(Markup("<iconify-icon icon='mdi:check'></iconify-icon>Settings saved"), category="success")
171
return flask.redirect(f"/{flask.session.get('username')}", code=303)
172
173
174
@app.route("/favourites/", methods=["GET", "POST"])
175
def favourites():
176
if not flask.session.get("username"):
177
flask.abort(401)
178
if flask.request.method == "GET":
179
relationships = RepoFavourite.query.filter_by(userUsername=flask.session.get("username"))
180
181
return flask.render_template("favourites.html", favourites=relationships)
182
183
184
@app.route("/notifications/", methods=["GET", "POST"])
185
def notifications():
186
if not flask.session.get("username"):
187
flask.abort(401)
188
if flask.request.method == "GET":
189
return flask.render_template("notifications.html", notifications=UserNotification.query.filter_by(userUsername=flask.session.get("username")))
190
191
192
@app.route("/accounts/", methods=["GET", "POST"])
193
def login():
194
if flask.request.method == "GET":
195
return flask.render_template("login.html")
196
else:
197
if "login" in flask.request.form:
198
username = flask.request.form["username"]
199
password = flask.request.form["password"]
200
201
user = User.query.filter_by(username=username).first()
202
203
if user and bcrypt.check_password_hash(user.passwordHashed, password):
204
flask.session["username"] = user.username
205
flask.flash(
206
Markup(f"<iconify-icon icon='mdi:account'></iconify-icon>Successfully logged in as {username}"),
207
category="success")
208
return flask.redirect("/", code=303)
209
elif not user:
210
flask.flash(Markup("<iconify-icon icon='mdi:account-question'></iconify-icon>User not found"),
211
category="alert")
212
return flask.render_template("login.html")
213
else:
214
flask.flash(Markup("<iconify-icon icon='mdi:account-question'></iconify-icon>Invalid password"),
215
category="error")
216
return flask.render_template("login.html")
217
if "signup" in flask.request.form:
218
username = flask.request.form["username"]
219
password = flask.request.form["password"]
220
password2 = flask.request.form["password2"]
221
email = flask.request.form.get("email")
222
email2 = flask.request.form.get("email2") # repeat email is a honeypot
223
name = flask.request.form.get("name")
224
225
if not onlyChars(username, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"):
226
flask.flash(Markup(
227
"<iconify-icon icon='mdi:account-error'></iconify-icon>Usernames may only contain Latin alphabet, numbers, '-' and '_'"),
228
category="error")
229
return flask.render_template("login.html")
230
231
if username in config.RESERVED_NAMES:
232
flask.flash(
233
Markup(f"<iconify-icon icon='mdi:account-error'></iconify-icon>Sorry, {username} is a system path"),
234
category="error")
235
return flask.render_template("login.html")
236
237
userCheck = User.query.filter_by(username=username).first()
238
if userCheck:
239
flask.flash(
240
Markup(f"<iconify-icon icon='mdi:account-error'></iconify-icon>The username {username} is taken"),
241
category="error")
242
return flask.render_template("login.html")
243
244
if password2 != password:
245
flask.flash(Markup("<iconify-icon icon='mdi:key-alert'></iconify-icon>Make sure the passwords match"),
246
category="error")
247
return flask.render_template("login.html")
248
249
user = User(username, password, email, name)
250
db.session.add(user)
251
db.session.commit()
252
flask.session["username"] = user.username
253
flask.flash(Markup(
254
f"<iconify-icon icon='mdi:account'></iconify-icon>Successfully created and logged in as {username}"),
255
category="success")
256
return flask.redirect("/", code=303)
257
258
259
@app.route("/newrepo/", methods=["GET", "POST"])
260
def newRepo():
261
if not flask.session.get("username"):
262
flask.abort(401)
263
if flask.request.method == "GET":
264
return flask.render_template("new-repo.html")
265
else:
266
name = flask.request.form["name"]
267
visibility = int(flask.request.form["visibility"])
268
269
if not onlyChars(name, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"):
270
flask.flash(Markup(
271
"<iconify-icon icon='mdi:error'></iconify-icon>Repository names may only contain Latin alphabet, numbers, '-' and '_'"),
272
category="error")
273
return flask.render_template("new-repo.html")
274
275
user = User.query.filter_by(username=flask.session.get("username")).first()
276
277
repo = Repo(user, name, visibility)
278
db.session.add(repo)
279
db.session.commit()
280
281
if not os.path.exists(os.path.join(config.REPOS_PATH, repo.route)):
282
subprocess.run(["git", "init", repo.name],
283
cwd=os.path.join(config.REPOS_PATH, flask.session.get("username")))
284
285
flask.flash(Markup(f"<iconify-icon icon='mdi:folder'></iconify-icon>Successfully created repository {name}"),
286
category="success")
287
return flask.redirect(repo.route, code=303)
288
289
290
@app.route("/logout")
291
def logout():
292
flask.session.clear()
293
flask.flash(Markup(f"<iconify-icon icon='mdi:account'></iconify-icon>Successfully logged out"), category="info")
294
return flask.redirect("/", code=303)
295
296
297
@app.route("/<username>/", methods=["GET", "POST"])
298
def userProfile(username):
299
oldRelationship = UserFollow.query.filter_by(followerUsername=flask.session.get("username"), followedUsername=username).first()
300
if flask.request.method == "GET":
301
user = User.query.filter_by(username=username).first()
302
repos = Repo.query.filter_by(ownerName=username, visibility=2)
303
return flask.render_template("user-profile.html", user=user, repos=repos, relationship=oldRelationship)
304
elif flask.request.args.get("action") == "follow":
305
if oldRelationship:
306
db.session.delete(oldRelationship)
307
else:
308
relationship = UserFollow(
309
flask.session.get("username"),
310
username
311
)
312
db.session.add(relationship)
313
314
db.session.commit()
315
return flask.redirect("?", code=303)
316
317
318
@app.route("/<username>/<repository>/")
319
def repositoryIndex(username, repository):
320
return flask.redirect("./tree", code=302)
321
322
323
@app.route("/info/<username>/avatar")
324
def userAvatar(username):
325
serverUserdataLocation = os.path.join(config.USERDATA_PATH, username)
326
327
if not os.path.exists(serverUserdataLocation):
328
return flask.render_template("not-found.html"), 404
329
330
return flask.send_from_directory(serverUserdataLocation, "avatar.png")
331
332
333
@app.route("/<username>/<repository>/raw/<branch>/<path:subpath>")
334
def repositoryRaw(username, repository, branch, subpath):
335
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
336
repository) is not None):
337
flask.abort(403)
338
339
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
340
341
app.logger.info(f"Loading {serverRepoLocation}")
342
343
if not os.path.exists(serverRepoLocation):
344
app.logger.error(f"Cannot load {serverRepoLocation}")
345
return flask.render_template("not-found.html"), 404
346
347
repo = git.Repo(serverRepoLocation)
348
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
349
if not repoData.defaultBranch:
350
if repo.heads:
351
repoData.defaultBranch = repo.heads[0].name
352
else:
353
return flask.render_template("empty.html",
354
remote=f"http://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
355
if not branch:
356
branch = repoData.defaultBranch
357
return flask.redirect(f"./{branch}", code=302)
358
359
if branch.startswith("tag:"):
360
ref = f"tags/{branch[4:]}"
361
elif branch.startswith("~"):
362
ref = branch[1:]
363
else:
364
ref = f"heads/{branch}"
365
366
ref = ref.replace("~", "/") # encode slashes for URL support
367
368
try:
369
repo.git.checkout("-f", ref)
370
except git.exc.GitCommandError:
371
return flask.render_template("not-found.html"), 404
372
373
return flask.send_from_directory(config.REPOS_PATH, os.path.join(username, repository, subpath))
374
375
376
@repositories.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""})
377
@repositories.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""})
378
@repositories.route("/<username>/<repository>/tree/<branch>/<path:subpath>")
379
def repositoryTree(username, repository, branch, subpath):
380
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
381
repository) is not None):
382
flask.abort(403)
383
384
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
385
386
app.logger.info(f"Loading {serverRepoLocation}")
387
388
if not os.path.exists(serverRepoLocation):
389
app.logger.error(f"Cannot load {serverRepoLocation}")
390
return flask.render_template("not-found.html"), 404
391
392
repo = git.Repo(serverRepoLocation)
393
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
394
if not repoData.defaultBranch:
395
if repo.heads:
396
repoData.defaultBranch = repo.heads[0].name
397
else:
398
return flask.render_template("empty.html",
399
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
400
if not branch:
401
branch = repoData.defaultBranch
402
return flask.redirect(f"./{branch}", code=302)
403
404
if branch.startswith("tag:"):
405
ref = f"tags/{branch[4:]}"
406
elif branch.startswith("~"):
407
ref = branch[1:]
408
else:
409
ref = f"heads/{branch}"
410
411
ref = ref.replace("~", "/") # encode slashes for URL support
412
413
try:
414
repo.git.checkout("-f", ref)
415
except git.exc.GitCommandError:
416
return flask.render_template("not-found.html"), 404
417
418
branches = repo.heads
419
420
allRefs = []
421
for ref in repo.heads:
422
allRefs.append((ref, "head"))
423
for ref in repo.tags:
424
allRefs.append((ref, "tag"))
425
426
if os.path.isdir(os.path.join(serverRepoLocation, subpath)):
427
files = []
428
blobs = []
429
430
for entry in os.listdir(os.path.join(serverRepoLocation, subpath)):
431
if not os.path.basename(entry) == ".git":
432
files.append(os.path.join(subpath, entry))
433
434
infos = []
435
436
for file in files:
437
path = os.path.join(serverRepoLocation, file)
438
mimetype = guessMIME(path)
439
440
text = gitCommand(serverRepoLocation, None, "log", "--format='%H\n'", file).decode()
441
442
sha = text.split("\n")[0]
443
identifier = f"/{username}/{repository}/{sha}"
444
lastCommit = Commit.query.filter_by(identifier=identifier).first()
445
446
info = {
447
"name": os.path.basename(file),
448
"serverPath": path,
449
"relativePath": file,
450
"link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file),
451
"size": humanSize(os.path.getsize(path)),
452
"mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}",
453
"commit": lastCommit,
454
"shaSize": 7,
455
}
456
457
specialIcon = config.matchIcon(os.path.basename(file))
458
if specialIcon:
459
info["icon"] = specialIcon
460
elif os.path.isdir(path):
461
info["icon"] = config.folderIcon
462
elif mimetypes.guess_type(path)[0] in config.fileIcons:
463
info["icon"] = config.fileIcons[mimetypes.guess_type(path)[0]]
464
else:
465
info["icon"] = config.unknownIcon
466
467
if os.path.isdir(path):
468
infos.insert(0, info)
469
else:
470
infos.append(info)
471
472
return flask.render_template(
473
"repo-tree.html",
474
username=username,
475
repository=repository,
476
files=infos,
477
subpath=os.path.join("/", subpath),
478
branches=allRefs,
479
current=branch,
480
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
481
isFavourite=getFavourite(flask.session.get("username"), username, repository)
482
)
483
else:
484
path = os.path.join(serverRepoLocation, subpath)
485
486
if not os.path.exists(path):
487
return flask.render_template("not-found.html"), 404
488
489
mimetype = guessMIME(path)
490
mode = mimetype.split("/", 1)[0]
491
size = humanSize(os.path.getsize(path))
492
493
specialIcon = config.matchIcon(os.path.basename(path))
494
if specialIcon:
495
icon = specialIcon
496
elif os.path.isdir(path):
497
icon = config.folderIcon
498
elif mimetypes.guess_type(path)[0] in config.fileIcons:
499
icon = config.fileIcons[mimetypes.guess_type(path)[0]]
500
else:
501
icon = config.unknownIcon
502
503
contents = None
504
if mode == "text":
505
contents = convertToHTML(path)
506
507
return flask.render_template(
508
"repo-file.html",
509
username=username,
510
repository=repository,
511
file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath),
512
branches=allRefs,
513
current=branch,
514
mode=mode,
515
mimetype=mimetype,
516
detailedtype=magic.from_file(path),
517
size=size,
518
icon=icon,
519
subpath=os.path.join("/", subpath),
520
basename=os.path.basename(path),
521
contents=contents,
522
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
523
isFavourite=getFavourite(flask.session.get("username"), username, repository)
524
)
525
526
527
@repositories.route("/<username>/<repository>/forum/")
528
def repositoryForum(username, repository):
529
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
530
repository) is not None):
531
flask.abort(403)
532
533
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
534
535
app.logger.info(f"Loading {serverRepoLocation}")
536
537
if not os.path.exists(serverRepoLocation):
538
app.logger.error(f"Cannot load {serverRepoLocation}")
539
return flask.render_template("not-found.html"), 404
540
541
repo = git.Repo(serverRepoLocation)
542
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
543
user = User.query.filter_by(username=flask.session.get("username")).first()
544
relationships = RepoAccess.query.filter_by(repo=repoData)
545
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
546
547
return flask.render_template(
548
"repo-forum.html",
549
username=username,
550
repository=repository,
551
repoData=repoData,
552
relationships=relationships,
553
repo=repo,
554
userRelationship=userRelationship,
555
Post=Post,
556
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
557
isFavourite=getFavourite(flask.session.get("username"), username, repository)
558
)
559
560
561
@repositories.route("/<username>/<repository>/forum/new", methods=["POST"])
562
def repositoryForumAdd(username, repository):
563
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
564
repository) is not None):
565
flask.abort(403)
566
567
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
568
569
app.logger.info(f"Loading {serverRepoLocation}")
570
571
if not os.path.exists(serverRepoLocation):
572
app.logger.error(f"Cannot load {serverRepoLocation}")
573
return flask.render_template("not-found.html"), 404
574
575
repo = git.Repo(serverRepoLocation)
576
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
577
user = User.query.filter_by(username=flask.session.get("username")).first()
578
relationships = RepoAccess.query.filter_by(repo=repoData)
579
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
580
581
post = Post(user, repoData, None, flask.request.form["subject"], flask.request.form["message"])
582
583
db.session.add(post)
584
db.session.commit()
585
586
return flask.redirect(flask.url_for(".repositoryForumThread", username=username, repository=repository, postID=post.number), code=303)
587
588
589
@repositories.route("/<username>/<repository>/forum/<int:postID>")
590
def repositoryForumThread(username, repository, postID):
591
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
592
repository) is not None):
593
flask.abort(403)
594
595
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
596
597
app.logger.info(f"Loading {serverRepoLocation}")
598
599
if not os.path.exists(serverRepoLocation):
600
app.logger.error(f"Cannot load {serverRepoLocation}")
601
return flask.render_template("not-found.html"), 404
602
603
repo = git.Repo(serverRepoLocation)
604
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
605
user = User.query.filter_by(username=flask.session.get("username")).first()
606
relationships = RepoAccess.query.filter_by(repo=repoData)
607
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
608
609
return flask.render_template(
610
"repo-forum-thread.html",
611
username=username,
612
repository=repository,
613
repoData=repoData,
614
relationships=relationships,
615
repo=repo,
616
userRelationship=userRelationship,
617
Post=Post,
618
postID=postID,
619
maxPostNesting=4,
620
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
621
isFavourite=getFavourite(flask.session.get("username"), username, repository)
622
)
623
624
625
@repositories.route("/<username>/<repository>/forum/<int:postID>/reply", methods=["POST"])
626
def repositoryForumReply(username, repository, postID):
627
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
628
repository) is not None):
629
flask.abort(403)
630
631
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
632
633
app.logger.info(f"Loading {serverRepoLocation}")
634
635
if not os.path.exists(serverRepoLocation):
636
app.logger.error(f"Cannot load {serverRepoLocation}")
637
return flask.render_template("not-found.html"), 404
638
639
repo = git.Repo(serverRepoLocation)
640
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
641
user = User.query.filter_by(username=flask.session.get("username")).first()
642
relationships = RepoAccess.query.filter_by(repo=repoData)
643
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
644
if not user:
645
flask.abort(401)
646
647
parent = Post.query.filter_by(identifier=f"/{username}/{repository}/{postID}").first()
648
post = Post(user, repoData, parent, flask.request.form["subject"], flask.request.form["message"])
649
650
db.session.add(post)
651
post.updateDate()
652
db.session.commit()
653
654
return flask.redirect(flask.url_for(".repositoryForumThread", username=username, repository=repository, postID=postID), code=303)
655
656
657
@app.route("/<username>/<repository>/forum/<int:postID>/voteup", defaults={"score": 1})
658
@app.route("/<username>/<repository>/forum/<int:postID>/votedown", defaults={"score": -1})
659
@app.route("/<username>/<repository>/forum/<int:postID>/votes", defaults={"score": 0})
660
def repositoryForumVote(username, repository, postID, score):
661
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
662
repository) is not None):
663
flask.abort(403)
664
665
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
666
667
app.logger.info(f"Loading {serverRepoLocation}")
668
669
if not os.path.exists(serverRepoLocation):
670
app.logger.error(f"Cannot load {serverRepoLocation}")
671
return flask.render_template("not-found.html"), 404
672
673
repo = git.Repo(serverRepoLocation)
674
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
675
user = User.query.filter_by(username=flask.session.get("username")).first()
676
relationships = RepoAccess.query.filter_by(repo=repoData)
677
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
678
if not user:
679
flask.abort(401)
680
681
post = Post.query.filter_by(identifier=f"/{username}/{repository}/{postID}").first()
682
683
if score:
684
oldRelationship = PostVote.query.filter_by(userUsername=user.username, postIdentifier=post.identifier).first()
685
if oldRelationship:
686
if score == oldRelationship.voteScore:
687
db.session.delete(oldRelationship)
688
post.voteSum -= oldRelationship.voteScore
689
else:
690
post.voteSum -= oldRelationship.voteScore
691
post.voteSum += score
692
oldRelationship.voteScore = score
693
else:
694
relationship = PostVote(user, post, score)
695
post.voteSum += score
696
db.session.add(relationship)
697
698
db.session.commit()
699
700
userVote = PostVote.query.filter_by(userUsername=user.username, postIdentifier=post.identifier).first()
701
response = flask.make_response(str(post.voteSum) + " " + str(userVote.voteScore if userVote else 0))
702
response.content_type = "text/plain"
703
704
return response
705
706
707
@app.route("/<username>/<repository>/favourite")
708
def repositoryFavourite(username, repository):
709
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
710
repository) is not None):
711
flask.abort(403)
712
713
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
714
715
app.logger.info(f"Loading {serverRepoLocation}")
716
717
if not os.path.exists(serverRepoLocation):
718
app.logger.error(f"Cannot load {serverRepoLocation}")
719
return flask.render_template("not-found.html"), 404
720
721
repo = git.Repo(serverRepoLocation)
722
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
723
user = User.query.filter_by(username=flask.session.get("username")).first()
724
relationships = RepoAccess.query.filter_by(repo=repoData)
725
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
726
if not user:
727
flask.abort(401)
728
729
oldRelationship = RepoFavourite.query.filter_by(userUsername=user.username, repoRoute=repoData.route).first()
730
if oldRelationship:
731
db.session.delete(oldRelationship)
732
else:
733
relationship = RepoFavourite(user, repoData)
734
db.session.add(relationship)
735
736
db.session.commit()
737
738
return flask.redirect(flask.url_for("favourites"), code=303)
739
740
741
@repositories.route("/<username>/<repository>/users/", methods=["GET", "POST"])
742
def repositoryUsers(username, repository):
743
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
744
repository) is not None):
745
flask.abort(403)
746
747
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
748
749
app.logger.info(f"Loading {serverRepoLocation}")
750
751
if not os.path.exists(serverRepoLocation):
752
app.logger.error(f"Cannot load {serverRepoLocation}")
753
return flask.render_template("not-found.html"), 404
754
755
repo = git.Repo(serverRepoLocation)
756
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
757
user = User.query.filter_by(username=flask.session.get("username")).first()
758
relationships = RepoAccess.query.filter_by(repo=repoData)
759
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
760
761
if flask.request.method == "GET":
762
return flask.render_template(
763
"repo-users.html",
764
username=username,
765
repository=repository,
766
repoData=repoData,
767
relationships=relationships,
768
repo=repo,
769
userRelationship=userRelationship,
770
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
771
isFavourite=getFavourite(flask.session.get("username"), username, repository)
772
)
773
else:
774
if getPermissionLevel(flask.session.get("username"), username, repository) != 2:
775
flask.abort(401)
776
777
if flask.request.form.get("new-username"):
778
# Create new relationship
779
newUser = User.query.filter_by(username=flask.request.form.get("new-username")).first()
780
relationship = RepoAccess(newUser, repoData, flask.request.form.get("new-level"))
781
db.session.add(relationship)
782
db.session.commit()
783
if flask.request.form.get("update-username"):
784
# Create new relationship
785
updatedUser = User.query.filter_by(username=flask.request.form.get("update-username")).first()
786
relationship = RepoAccess.query.filter_by(repo=repoData, user=updatedUser).first()
787
if flask.request.form.get("update-level") == -1:
788
relationship.delete()
789
else:
790
relationship.accessLevel = flask.request.form.get("update-level")
791
db.session.commit()
792
793
return flask.redirect(app.url_for(".repositoryUsers", username=username, repository=repository))
794
795
796
@repositories.route("/<username>/<repository>/branches/")
797
def repositoryBranches(username, repository):
798
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
799
repository) is not None):
800
flask.abort(403)
801
802
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
803
804
app.logger.info(f"Loading {serverRepoLocation}")
805
806
if not os.path.exists(serverRepoLocation):
807
app.logger.error(f"Cannot load {serverRepoLocation}")
808
return flask.render_template("not-found.html"), 404
809
810
repo = git.Repo(serverRepoLocation)
811
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
812
813
return flask.render_template(
814
"repo-branches.html",
815
username=username,
816
repository=repository,
817
repoData=repoData,
818
repo=repo,
819
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
820
isFavourite=getFavourite(flask.session.get("username"), username, repository)
821
)
822
823
824
@repositories.route("/<username>/<repository>/log/", defaults={"branch": None})
825
@repositories.route("/<username>/<repository>/log/<branch>/")
826
def repositoryLog(username, repository, branch):
827
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
828
repository) is not None):
829
flask.abort(403)
830
831
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
832
833
app.logger.info(f"Loading {serverRepoLocation}")
834
835
if not os.path.exists(serverRepoLocation):
836
app.logger.error(f"Cannot load {serverRepoLocation}")
837
return flask.render_template("not-found.html"), 404
838
839
repo = git.Repo(serverRepoLocation)
840
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
841
if not repoData.defaultBranch:
842
if repo.heads:
843
repoData.defaultBranch = repo.heads[0].name
844
else:
845
return flask.render_template("empty.html",
846
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
847
if not branch:
848
branch = repoData.defaultBranch
849
return flask.redirect(f"./{branch}", code=302)
850
851
if branch.startswith("tag:"):
852
ref = f"tags/{branch[4:]}"
853
elif branch.startswith("~"):
854
ref = branch[1:]
855
else:
856
ref = f"heads/{branch}"
857
858
ref = ref.replace("~", "/") # encode slashes for URL support
859
860
try:
861
repo.git.checkout("-f", ref)
862
except git.exc.GitCommandError:
863
return flask.render_template("not-found.html"), 404
864
865
branches = repo.heads
866
867
allRefs = []
868
for ref in repo.heads:
869
allRefs.append((ref, "head"))
870
for ref in repo.tags:
871
allRefs.append((ref, "tag"))
872
873
commitList = [f"/{username}/{repository}/{sha}" for sha in gitCommand(serverRepoLocation, None, "log", "--format='%H'").decode().split("\n")]
874
875
commits = Commit.query.filter(Commit.identifier.in_(commitList))
876
877
return flask.render_template(
878
"repo-log.html",
879
username=username,
880
repository=repository,
881
branches=allRefs,
882
current=branch,
883
repoData=repoData,
884
repo=repo,
885
commits=commits,
886
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
887
isFavourite=getFavourite(flask.session.get("username"), username, repository)
888
)
889
890
891
@repositories.route("/<username>/<repository>/settings/")
892
def repositorySettings(username, repository):
893
if getPermissionLevel(flask.session.get("username"), username, repository) != 2:
894
flask.abort(401)
895
896
return flask.render_template("repo-settings.html", username=username, repository=repository)
897
898
899
@app.errorhandler(404)
900
def e404(error):
901
return flask.render_template("not-found.html"), 404
902
903
904
@app.errorhandler(401)
905
def e401(error):
906
return flask.render_template("unauthorised.html"), 401
907
908
909
@app.errorhandler(403)
910
def e403(error):
911
return flask.render_template("forbidden.html"), 403
912
913
914
@app.errorhandler(418)
915
def e418(error):
916
return flask.render_template("teapot.html"), 418
917
918
919
@app.errorhandler(405)
920
def e405(error):
921
return flask.render_template("method-not-allowed.html"), 405
922
923
924
if __name__ == "__main__":
925
app.run(debug=True, port=8080, host="0.0.0.0")
926
927
928
app.register_blueprint(repositories)
929