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)
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 request.query.get("action") == "follow":
305
if oldRelationship:
306
db.session.delete(oldRelationship)
307
else:
308
relationship = UserFollow(
309
User.query.filter_by(username=db.session.get("username")),
310
User.query.filter_by(username=username)
311
)
312
db.session.add(relationship)
313
314
db.session.commit()
315
316
317
@app.route("/<username>/<repository>/")
318
def repositoryIndex(username, repository):
319
return flask.redirect("./tree", code=302)
320
321
322
@app.route("/info/<username>/avatar")
323
def userAvatar(username):
324
serverUserdataLocation = os.path.join(config.USERDATA_PATH, username)
325
326
if not os.path.exists(serverUserdataLocation):
327
return flask.render_template("not-found.html"), 404
328
329
return flask.send_from_directory(serverUserdataLocation, "avatar.png")
330
331
332
@app.route("/<username>/<repository>/raw/<branch>/<path:subpath>")
333
def repositoryRaw(username, repository, branch, subpath):
334
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
335
repository) is not None):
336
flask.abort(403)
337
338
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
339
340
app.logger.info(f"Loading {serverRepoLocation}")
341
342
if not os.path.exists(serverRepoLocation):
343
app.logger.error(f"Cannot load {serverRepoLocation}")
344
return flask.render_template("not-found.html"), 404
345
346
repo = git.Repo(serverRepoLocation)
347
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
348
if not repoData.defaultBranch:
349
if repo.heads:
350
repoData.defaultBranch = repo.heads[0].name
351
else:
352
return flask.render_template("empty.html",
353
remote=f"http://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
354
if not branch:
355
branch = repoData.defaultBranch
356
return flask.redirect(f"./{branch}", code=302)
357
358
if branch.startswith("tag:"):
359
ref = f"tags/{branch[4:]}"
360
elif branch.startswith("~"):
361
ref = branch[1:]
362
else:
363
ref = f"heads/{branch}"
364
365
ref = ref.replace("~", "/") # encode slashes for URL support
366
367
try:
368
repo.git.checkout("-f", ref)
369
except git.exc.GitCommandError:
370
return flask.render_template("not-found.html"), 404
371
372
return flask.send_from_directory(config.REPOS_PATH, os.path.join(username, repository, subpath))
373
374
375
@repositories.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""})
376
@repositories.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""})
377
@repositories.route("/<username>/<repository>/tree/<branch>/<path:subpath>")
378
def repositoryTree(username, repository, branch, subpath):
379
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
380
repository) is not None):
381
flask.abort(403)
382
383
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
384
385
app.logger.info(f"Loading {serverRepoLocation}")
386
387
if not os.path.exists(serverRepoLocation):
388
app.logger.error(f"Cannot load {serverRepoLocation}")
389
return flask.render_template("not-found.html"), 404
390
391
repo = git.Repo(serverRepoLocation)
392
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
393
if not repoData.defaultBranch:
394
if repo.heads:
395
repoData.defaultBranch = repo.heads[0].name
396
else:
397
return flask.render_template("empty.html",
398
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
399
if not branch:
400
branch = repoData.defaultBranch
401
return flask.redirect(f"./{branch}", code=302)
402
403
if branch.startswith("tag:"):
404
ref = f"tags/{branch[4:]}"
405
elif branch.startswith("~"):
406
ref = branch[1:]
407
else:
408
ref = f"heads/{branch}"
409
410
ref = ref.replace("~", "/") # encode slashes for URL support
411
412
try:
413
repo.git.checkout("-f", ref)
414
except git.exc.GitCommandError:
415
return flask.render_template("not-found.html"), 404
416
417
branches = repo.heads
418
419
allRefs = []
420
for ref in repo.heads:
421
allRefs.append((ref, "head"))
422
for ref in repo.tags:
423
allRefs.append((ref, "tag"))
424
425
if os.path.isdir(os.path.join(serverRepoLocation, subpath)):
426
files = []
427
blobs = []
428
429
for entry in os.listdir(os.path.join(serverRepoLocation, subpath)):
430
if not os.path.basename(entry) == ".git":
431
files.append(os.path.join(subpath, entry))
432
433
infos = []
434
435
for file in files:
436
path = os.path.join(serverRepoLocation, file)
437
mimetype = guessMIME(path)
438
439
text = gitCommand(serverRepoLocation, None, "log", "--format='%H\n'", file).decode()
440
441
sha = text.split("\n")[0]
442
identifier = f"/{username}/{repository}/{sha}"
443
lastCommit = Commit.query.filter_by(identifier=identifier).first()
444
445
info = {
446
"name": os.path.basename(file),
447
"serverPath": path,
448
"relativePath": file,
449
"link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file),
450
"size": humanSize(os.path.getsize(path)),
451
"mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}",
452
"commit": lastCommit,
453
"shaSize": 7,
454
}
455
456
specialIcon = config.matchIcon(os.path.basename(file))
457
if specialIcon:
458
info["icon"] = specialIcon
459
elif os.path.isdir(path):
460
info["icon"] = config.folderIcon
461
elif mimetypes.guess_type(path)[0] in config.fileIcons:
462
info["icon"] = config.fileIcons[mimetypes.guess_type(path)[0]]
463
else:
464
info["icon"] = config.unknownIcon
465
466
if os.path.isdir(path):
467
infos.insert(0, info)
468
else:
469
infos.append(info)
470
471
return flask.render_template(
472
"repo-tree.html",
473
username=username,
474
repository=repository,
475
files=infos,
476
subpath=os.path.join("/", subpath),
477
branches=allRefs,
478
current=branch,
479
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
480
isFavourite=getFavourite(flask.session.get("username"), username, repository)
481
)
482
else:
483
path = os.path.join(serverRepoLocation, subpath)
484
485
if not os.path.exists(path):
486
return flask.render_template("not-found.html"), 404
487
488
mimetype = guessMIME(path)
489
mode = mimetype.split("/", 1)[0]
490
size = humanSize(os.path.getsize(path))
491
492
specialIcon = config.matchIcon(os.path.basename(path))
493
if specialIcon:
494
icon = specialIcon
495
elif os.path.isdir(path):
496
icon = config.folderIcon
497
elif mimetypes.guess_type(path)[0] in config.fileIcons:
498
icon = config.fileIcons[mimetypes.guess_type(path)[0]]
499
else:
500
icon = config.unknownIcon
501
502
contents = None
503
if mode == "text":
504
contents = convertToHTML(path)
505
506
return flask.render_template(
507
"repo-file.html",
508
username=username,
509
repository=repository,
510
file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath),
511
branches=allRefs,
512
current=branch,
513
mode=mode,
514
mimetype=mimetype,
515
detailedtype=magic.from_file(path),
516
size=size,
517
icon=icon,
518
subpath=os.path.join("/", subpath),
519
basename=os.path.basename(path),
520
contents=contents,
521
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
522
isFavourite=getFavourite(flask.session.get("username"), username, repository)
523
)
524
525
526
@repositories.route("/<username>/<repository>/forum/")
527
def repositoryForum(username, repository):
528
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
529
repository) is not None):
530
flask.abort(403)
531
532
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
533
534
app.logger.info(f"Loading {serverRepoLocation}")
535
536
if not os.path.exists(serverRepoLocation):
537
app.logger.error(f"Cannot load {serverRepoLocation}")
538
return flask.render_template("not-found.html"), 404
539
540
repo = git.Repo(serverRepoLocation)
541
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
542
user = User.query.filter_by(username=flask.session.get("username")).first()
543
relationships = RepoAccess.query.filter_by(repo=repoData)
544
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
545
546
return flask.render_template(
547
"repo-forum.html",
548
username=username,
549
repository=repository,
550
repoData=repoData,
551
relationships=relationships,
552
repo=repo,
553
userRelationship=userRelationship,
554
Post=Post,
555
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
556
isFavourite=getFavourite(flask.session.get("username"), username, repository)
557
)
558
559
560
@repositories.route("/<username>/<repository>/forum/new", methods=["POST"])
561
def repositoryForumAdd(username, repository):
562
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
563
repository) is not None):
564
flask.abort(403)
565
566
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
567
568
app.logger.info(f"Loading {serverRepoLocation}")
569
570
if not os.path.exists(serverRepoLocation):
571
app.logger.error(f"Cannot load {serverRepoLocation}")
572
return flask.render_template("not-found.html"), 404
573
574
repo = git.Repo(serverRepoLocation)
575
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
576
user = User.query.filter_by(username=flask.session.get("username")).first()
577
relationships = RepoAccess.query.filter_by(repo=repoData)
578
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
579
580
post = Post(user, repoData, None, flask.request.form["subject"], flask.request.form["message"])
581
582
db.session.add(post)
583
db.session.commit()
584
585
return flask.redirect(flask.url_for(".repositoryForumThread", username=username, repository=repository, postID=post.number), code=303)
586
587
588
@repositories.route("/<username>/<repository>/forum/<int:postID>")
589
def repositoryForumThread(username, repository, postID):
590
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
591
repository) is not None):
592
flask.abort(403)
593
594
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
595
596
app.logger.info(f"Loading {serverRepoLocation}")
597
598
if not os.path.exists(serverRepoLocation):
599
app.logger.error(f"Cannot load {serverRepoLocation}")
600
return flask.render_template("not-found.html"), 404
601
602
repo = git.Repo(serverRepoLocation)
603
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
604
user = User.query.filter_by(username=flask.session.get("username")).first()
605
relationships = RepoAccess.query.filter_by(repo=repoData)
606
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
607
608
return flask.render_template(
609
"repo-forum-thread.html",
610
username=username,
611
repository=repository,
612
repoData=repoData,
613
relationships=relationships,
614
repo=repo,
615
userRelationship=userRelationship,
616
Post=Post,
617
postID=postID,
618
maxPostNesting=4,
619
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
620
isFavourite=getFavourite(flask.session.get("username"), username, repository)
621
)
622
623
624
@repositories.route("/<username>/<repository>/forum/<int:postID>/reply", methods=["POST"])
625
def repositoryForumReply(username, repository, postID):
626
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
627
repository) is not None):
628
flask.abort(403)
629
630
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
631
632
app.logger.info(f"Loading {serverRepoLocation}")
633
634
if not os.path.exists(serverRepoLocation):
635
app.logger.error(f"Cannot load {serverRepoLocation}")
636
return flask.render_template("not-found.html"), 404
637
638
repo = git.Repo(serverRepoLocation)
639
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
640
user = User.query.filter_by(username=flask.session.get("username")).first()
641
relationships = RepoAccess.query.filter_by(repo=repoData)
642
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
643
if not user:
644
flask.abort(401)
645
646
parent = Post.query.filter_by(identifier=f"/{username}/{repository}/{postID}").first()
647
post = Post(user, repoData, parent, flask.request.form["subject"], flask.request.form["message"])
648
649
db.session.add(post)
650
post.updateDate()
651
db.session.commit()
652
653
return flask.redirect(flask.url_for(".repositoryForumThread", username=username, repository=repository, postID=postID), code=303)
654
655
656
@app.route("/<username>/<repository>/forum/<int:postID>/voteup", defaults={"score": 1})
657
@app.route("/<username>/<repository>/forum/<int:postID>/votedown", defaults={"score": -1})
658
@app.route("/<username>/<repository>/forum/<int:postID>/votes", defaults={"score": 0})
659
def repositoryForumVote(username, repository, postID, score):
660
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
661
repository) is not None):
662
flask.abort(403)
663
664
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
665
666
app.logger.info(f"Loading {serverRepoLocation}")
667
668
if not os.path.exists(serverRepoLocation):
669
app.logger.error(f"Cannot load {serverRepoLocation}")
670
return flask.render_template("not-found.html"), 404
671
672
repo = git.Repo(serverRepoLocation)
673
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
674
user = User.query.filter_by(username=flask.session.get("username")).first()
675
relationships = RepoAccess.query.filter_by(repo=repoData)
676
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
677
if not user:
678
flask.abort(401)
679
680
post = Post.query.filter_by(identifier=f"/{username}/{repository}/{postID}").first()
681
682
if score:
683
oldRelationship = PostVote.query.filter_by(userUsername=user.username, postIdentifier=post.identifier).first()
684
if oldRelationship:
685
if score == oldRelationship.voteScore:
686
db.session.delete(oldRelationship)
687
post.voteSum -= oldRelationship.voteScore
688
else:
689
post.voteSum -= oldRelationship.voteScore
690
post.voteSum += score
691
oldRelationship.voteScore = score
692
else:
693
relationship = PostVote(user, post, score)
694
post.voteSum += score
695
db.session.add(relationship)
696
697
db.session.commit()
698
699
userVote = PostVote.query.filter_by(userUsername=user.username, postIdentifier=post.identifier).first()
700
response = flask.make_response(str(post.voteSum) + " " + str(userVote.voteScore if userVote else 0))
701
response.content_type = "text/plain"
702
703
return response
704
705
706
@app.route("/<username>/<repository>/favourite")
707
def repositoryFavourite(username, repository):
708
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
709
repository) is not None):
710
flask.abort(403)
711
712
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
713
714
app.logger.info(f"Loading {serverRepoLocation}")
715
716
if not os.path.exists(serverRepoLocation):
717
app.logger.error(f"Cannot load {serverRepoLocation}")
718
return flask.render_template("not-found.html"), 404
719
720
repo = git.Repo(serverRepoLocation)
721
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
722
user = User.query.filter_by(username=flask.session.get("username")).first()
723
relationships = RepoAccess.query.filter_by(repo=repoData)
724
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
725
if not user:
726
flask.abort(401)
727
728
oldRelationship = RepoFavourite.query.filter_by(userUsername=user.username, repoRoute=repoData.route).first()
729
if oldRelationship:
730
db.session.delete(oldRelationship)
731
else:
732
relationship = RepoFavourite(user, repoData)
733
db.session.add(relationship)
734
735
db.session.commit()
736
737
return flask.redirect(flask.url_for("favourites"), code=303)
738
739
740
@repositories.route("/<username>/<repository>/users/", methods=["GET", "POST"])
741
def repositoryUsers(username, repository):
742
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
743
repository) is not None):
744
flask.abort(403)
745
746
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
747
748
app.logger.info(f"Loading {serverRepoLocation}")
749
750
if not os.path.exists(serverRepoLocation):
751
app.logger.error(f"Cannot load {serverRepoLocation}")
752
return flask.render_template("not-found.html"), 404
753
754
repo = git.Repo(serverRepoLocation)
755
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
756
user = User.query.filter_by(username=flask.session.get("username")).first()
757
relationships = RepoAccess.query.filter_by(repo=repoData)
758
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
759
760
if flask.request.method == "GET":
761
return flask.render_template(
762
"repo-users.html",
763
username=username,
764
repository=repository,
765
repoData=repoData,
766
relationships=relationships,
767
repo=repo,
768
userRelationship=userRelationship,
769
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
770
isFavourite=getFavourite(flask.session.get("username"), username, repository)
771
)
772
else:
773
if getPermissionLevel(flask.session.get("username"), username, repository) != 2:
774
flask.abort(401)
775
776
if flask.request.form.get("new-username"):
777
# Create new relationship
778
newUser = User.query.filter_by(username=flask.request.form.get("new-username")).first()
779
relationship = RepoAccess(newUser, repoData, flask.request.form.get("new-level"))
780
db.session.add(relationship)
781
db.session.commit()
782
if flask.request.form.get("update-username"):
783
# Create new relationship
784
updatedUser = User.query.filter_by(username=flask.request.form.get("update-username")).first()
785
relationship = RepoAccess.query.filter_by(repo=repoData, user=updatedUser).first()
786
if flask.request.form.get("update-level") == -1:
787
relationship.delete()
788
else:
789
relationship.accessLevel = flask.request.form.get("update-level")
790
db.session.commit()
791
792
return flask.redirect(app.url_for(".repositoryUsers", username=username, repository=repository))
793
794
795
@repositories.route("/<username>/<repository>/branches/")
796
def repositoryBranches(username, repository):
797
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
798
repository) is not None):
799
flask.abort(403)
800
801
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
802
803
app.logger.info(f"Loading {serverRepoLocation}")
804
805
if not os.path.exists(serverRepoLocation):
806
app.logger.error(f"Cannot load {serverRepoLocation}")
807
return flask.render_template("not-found.html"), 404
808
809
repo = git.Repo(serverRepoLocation)
810
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
811
812
return flask.render_template(
813
"repo-branches.html",
814
username=username,
815
repository=repository,
816
repoData=repoData,
817
repo=repo,
818
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
819
isFavourite=getFavourite(flask.session.get("username"), username, repository)
820
)
821
822
823
@repositories.route("/<username>/<repository>/log/", defaults={"branch": None})
824
@repositories.route("/<username>/<repository>/log/<branch>/")
825
def repositoryLog(username, repository, branch):
826
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
827
repository) is not None):
828
flask.abort(403)
829
830
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
831
832
app.logger.info(f"Loading {serverRepoLocation}")
833
834
if not os.path.exists(serverRepoLocation):
835
app.logger.error(f"Cannot load {serverRepoLocation}")
836
return flask.render_template("not-found.html"), 404
837
838
repo = git.Repo(serverRepoLocation)
839
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
840
if not repoData.defaultBranch:
841
if repo.heads:
842
repoData.defaultBranch = repo.heads[0].name
843
else:
844
return flask.render_template("empty.html",
845
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
846
if not branch:
847
branch = repoData.defaultBranch
848
return flask.redirect(f"./{branch}", code=302)
849
850
if branch.startswith("tag:"):
851
ref = f"tags/{branch[4:]}"
852
elif branch.startswith("~"):
853
ref = branch[1:]
854
else:
855
ref = f"heads/{branch}"
856
857
ref = ref.replace("~", "/") # encode slashes for URL support
858
859
try:
860
repo.git.checkout("-f", ref)
861
except git.exc.GitCommandError:
862
return flask.render_template("not-found.html"), 404
863
864
branches = repo.heads
865
866
allRefs = []
867
for ref in repo.heads:
868
allRefs.append((ref, "head"))
869
for ref in repo.tags:
870
allRefs.append((ref, "tag"))
871
872
commitList = [f"/{username}/{repository}/{sha}" for sha in gitCommand(serverRepoLocation, None, "log", "--format='%H'").decode().split("\n")]
873
874
commits = Commit.query.filter(Commit.identifier.in_(commitList))
875
876
return flask.render_template(
877
"repo-log.html",
878
username=username,
879
repository=repository,
880
branches=allRefs,
881
current=branch,
882
repoData=repoData,
883
repo=repo,
884
commits=commits,
885
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
886
isFavourite=getFavourite(flask.session.get("username"), username, repository)
887
)
888
889
890
@repositories.route("/<username>/<repository>/settings/")
891
def repositorySettings(username, repository):
892
if getPermissionLevel(flask.session.get("username"), username, repository) != 2:
893
flask.abort(401)
894
895
return flask.render_template("repo-settings.html", username=username, repository=repository)
896
897
898
@app.errorhandler(404)
899
def e404(error):
900
return flask.render_template("not-found.html"), 404
901
902
903
@app.errorhandler(401)
904
def e401(error):
905
return flask.render_template("unauthorised.html"), 401
906
907
908
@app.errorhandler(403)
909
def e403(error):
910
return flask.render_template("forbidden.html"), 403
911
912
913
@app.errorhandler(418)
914
def e418(error):
915
return flask.render_template("teapot.html"), 418
916
917
918
@app.errorhandler(405)
919
def e405(error):
920
return flask.render_template("method-not-allowed.html"), 405
921
922
923
if __name__ == "__main__":
924
app.run(debug=True, port=8080, host="0.0.0.0")
925
926
927
app.register_blueprint(repositories)
928