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 • 33.89 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
return {"loggedInUser": username}
131
132
133
@app.route("/")
134
def main():
135
return flask.render_template("home.html")
136
137
138
@app.route("/about/")
139
def about():
140
return flask.render_template("about.html", platform=platform)
141
142
143
@app.route("/settings/", methods=["GET", "POST"])
144
def settings():
145
if not flask.session.get("username"):
146
flask.abort(401)
147
if flask.request.method == "GET":
148
user = User.query.filter_by(username=flask.session.get("username")).first()
149
150
return flask.render_template("user-settings.html", user=user)
151
else:
152
user = User.query.filter_by(username=flask.session.get("username")).first()
153
154
user.displayName = flask.request.form["displayname"]
155
user.URL = flask.request.form["url"]
156
user.company = flask.request.form["company"]
157
user.companyURL = flask.request.form["companyurl"]
158
user.location = flask.request.form["location"]
159
user.showMail = flask.request.form.get("showmail", user.showMail)
160
161
db.session.commit()
162
163
flask.flash(Markup("<iconify-icon icon='mdi:check'></iconify-icon>Settings saved"), category="success")
164
return flask.redirect(f"/{flask.session.get('username')}", code=303)
165
166
167
@app.route("/favourites/", methods=["GET", "POST"])
168
def favourites():
169
if not flask.session.get("username"):
170
flask.abort(401)
171
if flask.request.method == "GET":
172
relationships = RepoFavourite.query.filter_by(userUsername=flask.session.get("username"))
173
174
return flask.render_template("favourites.html", favourites=relationships)
175
176
177
@app.route("/accounts/", methods=["GET", "POST"])
178
def login():
179
if flask.request.method == "GET":
180
return flask.render_template("login.html")
181
else:
182
if "login" in flask.request.form:
183
username = flask.request.form["username"]
184
password = flask.request.form["password"]
185
186
user = User.query.filter_by(username=username).first()
187
188
if user and bcrypt.check_password_hash(user.passwordHashed, password):
189
flask.session["username"] = user.username
190
flask.flash(
191
Markup(f"<iconify-icon icon='mdi:account'></iconify-icon>Successfully logged in as {username}"),
192
category="success")
193
return flask.redirect("/", code=303)
194
elif not user:
195
flask.flash(Markup("<iconify-icon icon='mdi:account-question'></iconify-icon>User not found"),
196
category="alert")
197
return flask.render_template("login.html")
198
else:
199
flask.flash(Markup("<iconify-icon icon='mdi:account-question'></iconify-icon>Invalid password"),
200
category="error")
201
return flask.render_template("login.html")
202
if "signup" in flask.request.form:
203
username = flask.request.form["username"]
204
password = flask.request.form["password"]
205
password2 = flask.request.form["password2"]
206
email = flask.request.form.get("email")
207
email2 = flask.request.form.get("email2") # repeat email is a honeypot
208
name = flask.request.form.get("name")
209
210
if not onlyChars(username, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"):
211
flask.flash(Markup(
212
"<iconify-icon icon='mdi:account-error'></iconify-icon>Usernames may only contain Latin alphabet, numbers, '-' and '_'"),
213
category="error")
214
return flask.render_template("login.html")
215
216
if username in config.RESERVED_NAMES:
217
flask.flash(
218
Markup(f"<iconify-icon icon='mdi:account-error'></iconify-icon>Sorry, {username} is a system path"),
219
category="error")
220
return flask.render_template("login.html")
221
222
userCheck = User.query.filter_by(username=username).first()
223
if userCheck:
224
flask.flash(
225
Markup(f"<iconify-icon icon='mdi:account-error'></iconify-icon>The username {username} is taken"),
226
category="error")
227
return flask.render_template("login.html")
228
229
if password2 != password:
230
flask.flash(Markup("<iconify-icon icon='mdi:key-alert'></iconify-icon>Make sure the passwords match"),
231
category="error")
232
return flask.render_template("login.html")
233
234
user = User(username, password, email, name)
235
db.session.add(user)
236
db.session.commit()
237
flask.session["username"] = user.username
238
flask.flash(Markup(
239
f"<iconify-icon icon='mdi:account'></iconify-icon>Successfully created and logged in as {username}"),
240
category="success")
241
return flask.redirect("/", code=303)
242
243
244
@app.route("/newrepo/", methods=["GET", "POST"])
245
def newRepo():
246
if not flask.session.get("username"):
247
flask.abort(401)
248
if flask.request.method == "GET":
249
return flask.render_template("new-repo.html")
250
else:
251
name = flask.request.form["name"]
252
visibility = int(flask.request.form["visibility"])
253
254
if not onlyChars(name, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"):
255
flask.flash(Markup(
256
"<iconify-icon icon='mdi:error'></iconify-icon>Repository names may only contain Latin alphabet, numbers, '-' and '_'"),
257
category="error")
258
return flask.render_template("new-repo.html")
259
260
user = User.query.filter_by(username=flask.session.get("username")).first()
261
262
repo = Repo(user, name, visibility)
263
db.session.add(repo)
264
db.session.commit()
265
266
if not os.path.exists(os.path.join(config.REPOS_PATH, repo.route)):
267
subprocess.run(["git", "init", repo.name],
268
cwd=os.path.join(config.REPOS_PATH, flask.session.get("username")))
269
270
flask.flash(Markup(f"<iconify-icon icon='mdi:folder'></iconify-icon>Successfully created repository {name}"),
271
category="success")
272
return flask.redirect(repo.route, code=303)
273
274
275
@app.route("/logout")
276
def logout():
277
flask.session.clear()
278
flask.flash(Markup(f"<iconify-icon icon='mdi:account'></iconify-icon>Successfully logged out"), category="info")
279
return flask.redirect("/", code=303)
280
281
282
@app.route("/<username>/")
283
def userProfile(username):
284
user = User.query.filter_by(username=username).first()
285
repos = Repo.query.filter_by(ownerName=username, visibility=2)
286
return flask.render_template("user-profile.html", user=user, repos=repos)
287
288
289
@app.route("/<username>/<repository>/")
290
def repositoryIndex(username, repository):
291
return flask.redirect("./tree", code=302)
292
293
294
@app.route("/info/<username>/avatar")
295
def userAvatar(username):
296
serverUserdataLocation = os.path.join(config.USERDATA_PATH, username)
297
298
if not os.path.exists(serverUserdataLocation):
299
return flask.render_template("not-found.html"), 404
300
301
return flask.send_from_directory(serverUserdataLocation, "avatar.png")
302
303
304
@app.route("/<username>/<repository>/raw/<branch>/<path:subpath>")
305
def repositoryRaw(username, repository, branch, subpath):
306
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
307
repository) is not None):
308
flask.abort(403)
309
310
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
311
312
app.logger.info(f"Loading {serverRepoLocation}")
313
314
if not os.path.exists(serverRepoLocation):
315
app.logger.error(f"Cannot load {serverRepoLocation}")
316
return flask.render_template("not-found.html"), 404
317
318
repo = git.Repo(serverRepoLocation)
319
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
320
if not repoData.defaultBranch:
321
if repo.heads:
322
repoData.defaultBranch = repo.heads[0].name
323
else:
324
return flask.render_template("empty.html",
325
remote=f"http://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
326
if not branch:
327
branch = repoData.defaultBranch
328
return flask.redirect(f"./{branch}", code=302)
329
330
if branch.startswith("tag:"):
331
ref = f"tags/{branch[4:]}"
332
elif branch.startswith("~"):
333
ref = branch[1:]
334
else:
335
ref = f"heads/{branch}"
336
337
ref = ref.replace("~", "/") # encode slashes for URL support
338
339
try:
340
repo.git.checkout("-f", ref)
341
except git.exc.GitCommandError:
342
return flask.render_template("not-found.html"), 404
343
344
return flask.send_from_directory(config.REPOS_PATH, os.path.join(username, repository, subpath))
345
346
347
@repositories.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""})
348
@repositories.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""})
349
@repositories.route("/<username>/<repository>/tree/<branch>/<path:subpath>")
350
def repositoryTree(username, repository, branch, subpath):
351
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
352
repository) is not None):
353
flask.abort(403)
354
355
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
356
357
app.logger.info(f"Loading {serverRepoLocation}")
358
359
if not os.path.exists(serverRepoLocation):
360
app.logger.error(f"Cannot load {serverRepoLocation}")
361
return flask.render_template("not-found.html"), 404
362
363
repo = git.Repo(serverRepoLocation)
364
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
365
if not repoData.defaultBranch:
366
if repo.heads:
367
repoData.defaultBranch = repo.heads[0].name
368
else:
369
return flask.render_template("empty.html",
370
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
371
if not branch:
372
branch = repoData.defaultBranch
373
return flask.redirect(f"./{branch}", code=302)
374
375
if branch.startswith("tag:"):
376
ref = f"tags/{branch[4:]}"
377
elif branch.startswith("~"):
378
ref = branch[1:]
379
else:
380
ref = f"heads/{branch}"
381
382
ref = ref.replace("~", "/") # encode slashes for URL support
383
384
try:
385
repo.git.checkout("-f", ref)
386
except git.exc.GitCommandError:
387
return flask.render_template("not-found.html"), 404
388
389
branches = repo.heads
390
391
allRefs = []
392
for ref in repo.heads:
393
allRefs.append((ref, "head"))
394
for ref in repo.tags:
395
allRefs.append((ref, "tag"))
396
397
if os.path.isdir(os.path.join(serverRepoLocation, subpath)):
398
files = []
399
blobs = []
400
401
for entry in os.listdir(os.path.join(serverRepoLocation, subpath)):
402
if not os.path.basename(entry) == ".git":
403
files.append(os.path.join(subpath, entry))
404
405
infos = []
406
407
for file in files:
408
path = os.path.join(serverRepoLocation, file)
409
mimetype = guessMIME(path)
410
411
text = gitCommand(serverRepoLocation, None, "log", "--format='%H\n'", file).decode()
412
413
sha = text.split("\n")[0]
414
identifier = f"/{username}/{repository}/{sha}"
415
lastCommit = Commit.query.filter_by(identifier=identifier).first()
416
417
info = {
418
"name": os.path.basename(file),
419
"serverPath": path,
420
"relativePath": file,
421
"link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file),
422
"size": humanSize(os.path.getsize(path)),
423
"mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}",
424
"commit": lastCommit,
425
"shaSize": 7,
426
}
427
428
specialIcon = config.matchIcon(os.path.basename(file))
429
if specialIcon:
430
info["icon"] = specialIcon
431
elif os.path.isdir(path):
432
info["icon"] = config.folderIcon
433
elif mimetypes.guess_type(path)[0] in config.fileIcons:
434
info["icon"] = config.fileIcons[mimetypes.guess_type(path)[0]]
435
else:
436
info["icon"] = config.unknownIcon
437
438
if os.path.isdir(path):
439
infos.insert(0, info)
440
else:
441
infos.append(info)
442
443
return flask.render_template(
444
"repo-tree.html",
445
username=username,
446
repository=repository,
447
files=infos,
448
subpath=os.path.join("/", subpath),
449
branches=allRefs,
450
current=branch,
451
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
452
isFavourite=getFavourite(flask.session.get("username"), username, repository)
453
)
454
else:
455
path = os.path.join(serverRepoLocation, subpath)
456
457
if not os.path.exists(path):
458
return flask.render_template("not-found.html"), 404
459
460
mimetype = guessMIME(path)
461
mode = mimetype.split("/", 1)[0]
462
size = humanSize(os.path.getsize(path))
463
464
specialIcon = config.matchIcon(os.path.basename(path))
465
if specialIcon:
466
icon = specialIcon
467
elif os.path.isdir(path):
468
icon = config.folderIcon
469
elif mimetypes.guess_type(path)[0] in config.fileIcons:
470
icon = config.fileIcons[mimetypes.guess_type(path)[0]]
471
else:
472
icon = config.unknownIcon
473
474
contents = None
475
if mode == "text":
476
contents = convertToHTML(path)
477
478
return flask.render_template(
479
"repo-file.html",
480
username=username,
481
repository=repository,
482
file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath),
483
branches=allRefs,
484
current=branch,
485
mode=mode,
486
mimetype=mimetype,
487
detailedtype=magic.from_file(path),
488
size=size,
489
icon=icon,
490
subpath=os.path.join("/", subpath),
491
basename=os.path.basename(path),
492
contents=contents,
493
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
494
isFavourite=getFavourite(flask.session.get("username"), username, repository)
495
)
496
497
498
@repositories.route("/<username>/<repository>/forum/")
499
def repositoryForum(username, repository):
500
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
501
repository) is not None):
502
flask.abort(403)
503
504
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
505
506
app.logger.info(f"Loading {serverRepoLocation}")
507
508
if not os.path.exists(serverRepoLocation):
509
app.logger.error(f"Cannot load {serverRepoLocation}")
510
return flask.render_template("not-found.html"), 404
511
512
repo = git.Repo(serverRepoLocation)
513
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
514
user = User.query.filter_by(username=flask.session.get("username")).first()
515
relationships = RepoAccess.query.filter_by(repo=repoData)
516
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
517
518
return flask.render_template(
519
"repo-forum.html",
520
username=username,
521
repository=repository,
522
repoData=repoData,
523
relationships=relationships,
524
repo=repo,
525
userRelationship=userRelationship,
526
Post=Post,
527
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
528
isFavourite=getFavourite(flask.session.get("username"), username, repository)
529
)
530
531
532
@repositories.route("/<username>/<repository>/forum/new", methods=["POST"])
533
def repositoryForumAdd(username, repository):
534
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
535
repository) is not None):
536
flask.abort(403)
537
538
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
539
540
app.logger.info(f"Loading {serverRepoLocation}")
541
542
if not os.path.exists(serverRepoLocation):
543
app.logger.error(f"Cannot load {serverRepoLocation}")
544
return flask.render_template("not-found.html"), 404
545
546
repo = git.Repo(serverRepoLocation)
547
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
548
user = User.query.filter_by(username=flask.session.get("username")).first()
549
relationships = RepoAccess.query.filter_by(repo=repoData)
550
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
551
552
post = Post(user, repoData, None, flask.request.form["subject"], flask.request.form["message"])
553
554
db.session.add(post)
555
db.session.commit()
556
557
return flask.redirect(flask.url_for(".repositoryForumThread", username=username, repository=repository, postID=post.number), code=303)
558
559
560
@repositories.route("/<username>/<repository>/forum/<int:postID>")
561
def repositoryForumThread(username, repository, postID):
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
return flask.render_template(
581
"repo-forum-thread.html",
582
username=username,
583
repository=repository,
584
repoData=repoData,
585
relationships=relationships,
586
repo=repo,
587
userRelationship=userRelationship,
588
Post=Post,
589
postID=postID,
590
maxPostNesting=4,
591
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
592
isFavourite=getFavourite(flask.session.get("username"), username, repository)
593
)
594
595
596
@repositories.route("/<username>/<repository>/forum/<int:postID>/reply", methods=["POST"])
597
def repositoryForumReply(username, repository, postID):
598
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
599
repository) is not None):
600
flask.abort(403)
601
602
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
603
604
app.logger.info(f"Loading {serverRepoLocation}")
605
606
if not os.path.exists(serverRepoLocation):
607
app.logger.error(f"Cannot load {serverRepoLocation}")
608
return flask.render_template("not-found.html"), 404
609
610
repo = git.Repo(serverRepoLocation)
611
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
612
user = User.query.filter_by(username=flask.session.get("username")).first()
613
relationships = RepoAccess.query.filter_by(repo=repoData)
614
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
615
if not user:
616
flask.abort(401)
617
618
parent = Post.query.filter_by(identifier=f"/{username}/{repository}/{postID}").first()
619
post = Post(user, repoData, parent, flask.request.form["subject"], flask.request.form["message"])
620
621
db.session.add(post)
622
post.updateDate()
623
db.session.commit()
624
625
return flask.redirect(flask.url_for(".repositoryForumThread", username=username, repository=repository, postID=postID), code=303)
626
627
628
@app.route("/<username>/<repository>/forum/<int:postID>/voteup", defaults={"score": 1})
629
@app.route("/<username>/<repository>/forum/<int:postID>/votedown", defaults={"score": -1})
630
@app.route("/<username>/<repository>/forum/<int:postID>/votes", defaults={"score": 0})
631
def repositoryForumVote(username, repository, postID, score):
632
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
633
repository) is not None):
634
flask.abort(403)
635
636
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
637
638
app.logger.info(f"Loading {serverRepoLocation}")
639
640
if not os.path.exists(serverRepoLocation):
641
app.logger.error(f"Cannot load {serverRepoLocation}")
642
return flask.render_template("not-found.html"), 404
643
644
repo = git.Repo(serverRepoLocation)
645
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
646
user = User.query.filter_by(username=flask.session.get("username")).first()
647
relationships = RepoAccess.query.filter_by(repo=repoData)
648
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
649
if not user:
650
flask.abort(401)
651
652
post = Post.query.filter_by(identifier=f"/{username}/{repository}/{postID}").first()
653
654
if score:
655
oldRelationship = PostVote.query.filter_by(userUsername=user.username, postIdentifier=post.identifier).first()
656
if oldRelationship:
657
if score == oldRelationship.voteScore:
658
db.session.delete(oldRelationship)
659
post.voteSum -= oldRelationship.voteScore
660
else:
661
post.voteSum -= oldRelationship.voteScore
662
post.voteSum += score
663
oldRelationship.voteScore = score
664
else:
665
relationship = PostVote(user, post, score)
666
post.voteSum += score
667
db.session.add(relationship)
668
669
db.session.commit()
670
671
userVote = PostVote.query.filter_by(userUsername=user.username, postIdentifier=post.identifier).first()
672
response = flask.make_response(str(post.voteSum) + " " + str(userVote.voteScore if userVote else 0))
673
response.content_type = "text/plain"
674
675
return response
676
677
678
@app.route("/<username>/<repository>/favourite")
679
def repositoryFavourite(username, repository):
680
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
681
repository) is not None):
682
flask.abort(403)
683
684
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
685
686
app.logger.info(f"Loading {serverRepoLocation}")
687
688
if not os.path.exists(serverRepoLocation):
689
app.logger.error(f"Cannot load {serverRepoLocation}")
690
return flask.render_template("not-found.html"), 404
691
692
repo = git.Repo(serverRepoLocation)
693
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
694
user = User.query.filter_by(username=flask.session.get("username")).first()
695
relationships = RepoAccess.query.filter_by(repo=repoData)
696
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
697
if not user:
698
flask.abort(401)
699
700
oldRelationship = RepoFavourite.query.filter_by(userUsername=user.username, repoRoute=repoData.route).first()
701
if oldRelationship:
702
db.session.delete(oldRelationship)
703
else:
704
relationship = RepoFavourite(user, repoData)
705
db.session.add(relationship)
706
707
db.session.commit()
708
709
return flask.redirect(flask.url_for("favourites"), code=303)
710
711
712
@repositories.route("/<username>/<repository>/users/", methods=["GET", "POST"])
713
def repositoryUsers(username, repository):
714
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
715
repository) is not None):
716
flask.abort(403)
717
718
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
719
720
app.logger.info(f"Loading {serverRepoLocation}")
721
722
if not os.path.exists(serverRepoLocation):
723
app.logger.error(f"Cannot load {serverRepoLocation}")
724
return flask.render_template("not-found.html"), 404
725
726
repo = git.Repo(serverRepoLocation)
727
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
728
user = User.query.filter_by(username=flask.session.get("username")).first()
729
relationships = RepoAccess.query.filter_by(repo=repoData)
730
userRelationship = RepoAccess.query.filter_by(repo=repoData, user=user).first()
731
732
if flask.request.method == "GET":
733
return flask.render_template(
734
"repo-users.html",
735
username=username,
736
repository=repository,
737
repoData=repoData,
738
relationships=relationships,
739
repo=repo,
740
userRelationship=userRelationship,
741
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
742
isFavourite=getFavourite(flask.session.get("username"), username, repository)
743
)
744
else:
745
if getPermissionLevel(flask.session.get("username"), username, repository) != 2:
746
flask.abort(401)
747
748
if flask.request.form.get("new-username"):
749
# Create new relationship
750
newUser = User.query.filter_by(username=flask.request.form.get("new-username")).first()
751
relationship = RepoAccess(newUser, repoData, flask.request.form.get("new-level"))
752
db.session.add(relationship)
753
db.session.commit()
754
if flask.request.form.get("update-username"):
755
# Create new relationship
756
updatedUser = User.query.filter_by(username=flask.request.form.get("update-username")).first()
757
relationship = RepoAccess.query.filter_by(repo=repoData, user=updatedUser).first()
758
if flask.request.form.get("update-level") == -1:
759
relationship.delete()
760
else:
761
relationship.accessLevel = flask.request.form.get("update-level")
762
db.session.commit()
763
764
return flask.redirect(app.url_for(".repositoryUsers", username=username, repository=repository))
765
766
767
@repositories.route("/<username>/<repository>/branches/")
768
def repositoryBranches(username, repository):
769
if not (getVisibility(username, repository) or getPermissionLevel(flask.session.get("username"), username,
770
repository) is not None):
771
flask.abort(403)
772
773
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
774
775
app.logger.info(f"Loading {serverRepoLocation}")
776
777
if not os.path.exists(serverRepoLocation):
778
app.logger.error(f"Cannot load {serverRepoLocation}")
779
return flask.render_template("not-found.html"), 404
780
781
repo = git.Repo(serverRepoLocation)
782
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
783
784
return flask.render_template(
785
"repo-branches.html",
786
username=username,
787
repository=repository,
788
repoData=repoData,
789
repo=repo,
790
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
791
isFavourite=getFavourite(flask.session.get("username"), username, repository)
792
)
793
794
795
@repositories.route("/<username>/<repository>/log/", defaults={"branch": None})
796
@repositories.route("/<username>/<repository>/log/<branch>/")
797
def repositoryLog(username, repository, branch):
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
if not repoData.defaultBranch:
813
if repo.heads:
814
repoData.defaultBranch = repo.heads[0].name
815
else:
816
return flask.render_template("empty.html",
817
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}"), 200
818
if not branch:
819
branch = repoData.defaultBranch
820
return flask.redirect(f"./{branch}", code=302)
821
822
if branch.startswith("tag:"):
823
ref = f"tags/{branch[4:]}"
824
elif branch.startswith("~"):
825
ref = branch[1:]
826
else:
827
ref = f"heads/{branch}"
828
829
ref = ref.replace("~", "/") # encode slashes for URL support
830
831
try:
832
repo.git.checkout("-f", ref)
833
except git.exc.GitCommandError:
834
return flask.render_template("not-found.html"), 404
835
836
branches = repo.heads
837
838
allRefs = []
839
for ref in repo.heads:
840
allRefs.append((ref, "head"))
841
for ref in repo.tags:
842
allRefs.append((ref, "tag"))
843
844
commitList = [f"/{username}/{repository}/{sha}" for sha in gitCommand(serverRepoLocation, None, "log", "--format='%H'").decode().split("\n")]
845
846
commits = Commit.query.filter(Commit.identifier.in_(commitList))
847
848
return flask.render_template(
849
"repo-log.html",
850
username=username,
851
repository=repository,
852
branches=allRefs,
853
current=branch,
854
repoData=repoData,
855
repo=repo,
856
commits=commits,
857
remote=f"http{'s' if config.suggestHTTPS else ''}://{config.BASE_DOMAIN}/git/{username}/{repository}",
858
isFavourite=getFavourite(flask.session.get("username"), username, repository)
859
)
860
861
862
@repositories.route("/<username>/<repository>/settings/")
863
def repositorySettings(username, repository):
864
if getPermissionLevel(flask.session.get("username"), username, repository) != 2:
865
flask.abort(401)
866
867
return flask.render_template("repo-settings.html", username=username, repository=repository)
868
869
870
@app.errorhandler(404)
871
def e404(error):
872
return flask.render_template("not-found.html"), 404
873
874
875
@app.errorhandler(401)
876
def e401(error):
877
return flask.render_template("unauthorised.html"), 401
878
879
880
@app.errorhandler(403)
881
def e403(error):
882
return flask.render_template("forbidden.html"), 403
883
884
885
@app.errorhandler(418)
886
def e418(error):
887
return flask.render_template("teapot.html"), 418
888
889
890
@app.errorhandler(405)
891
def e405(error):
892
return flask.render_template("method-not-allowed.html"), 405
893
894
895
if __name__ == "__main__":
896
app.run(debug=True, port=8080, host="0.0.0.0")
897
898
899
app.register_blueprint(repositories)
900