app.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #!/usr/bin/python3
  2. # SPDX-License-Identifier: AGPL-3.0-or-later
  3. #
  4. # Copyright (C) 2023 Ferass El Hafidi <vitali64pmemail@protonmail.com>
  5. # Copyright (C) 2023 Leo Gavilieau <xmoo@vern.cc>
  6. from flask.helpers import redirect, url_for
  7. from markupsafe import escape
  8. from sys import argv
  9. from flask import Flask
  10. from flask import request
  11. from flask.templating import render_template
  12. from markupsafe import Markup
  13. from api.base import errcheck, get_projects_list
  14. from api.groups import get_group_data, get_subgroups_list
  15. from api.users import get_user_data, is_user
  16. from api.wiki import get_project_wiki_page, get_project_wiki_sitemap
  17. from api.repos import get_repo_cloneurls, get_repo_commits, \
  18. get_repo_description, get_repo_brancheslist, \
  19. get_repo_idle, get_repo_issue, get_repo_issueparticipants, \
  20. get_repo_issues, get_repo_primary_branch, get_repo_readme, \
  21. get_repo_tagslist, get_repo_avatar, get_repo_tree, get_repo_blob
  22. app = Flask(__name__)
  23. def abort(code):
  24. return render_template('common.html', err=code), code
  25. @app.route('/')
  26. def index():
  27. return render_template('common.html', about='active', title='about')
  28. @app.route('/favicon.ico')
  29. def favicon():
  30. return "Not found", 404
  31. @app.route('/<name>', methods = ['GET', 'POST'])
  32. @app.route('/projects', methods = ['GET', 'POST'])
  33. def projects(name = None):
  34. if name is None:
  35. name = 'gitlab.freedesktop.org'
  36. if request.args.get('page') is None:
  37. page = 1
  38. else:
  39. page = int(request.args.get('page'))
  40. if request.method == 'GET':
  41. if errcheck(name) != 200:
  42. return abort(404)
  43. return render_template('common.html', title='projects',
  44. instance=name, \
  45. projects='active', projects_list=Markup(get_projects_list(\
  46. name, None, None, page)))
  47. elif request.method == 'POST':
  48. return render_template('common.html', title='search',
  49. instance=name, \
  50. projects='active', projects_list=Markup(get_projects_list(name, \
  51. request.form['laboratory_searchquery'], None, page)))
  52. @app.route('/<instance>/<user>/-/about')
  53. def userinfo(instance = None, user = None):
  54. if errcheck(instance, username=user) != 200:
  55. return abort(404)
  56. user_data = get_user_data(instance, user)
  57. return render_template('common.html', title='~%s' % (user),\
  58. user=1, about='active', \
  59. instance=instance, user_name=user_data[1], user_state=user_data[2], \
  60. user_avatar=user_data[0], user_url="/%s/%s" % (instance, user))
  61. @app.route('/<instance>/<path:group>', methods = ['GET', 'POST'])
  62. def group_projects(instance = None, group = None):
  63. if request.args.get('page') is None:
  64. page = 1
  65. else:
  66. page = int(request.args.get('page'))
  67. group_escaped = group.replace('/', '%2F')
  68. if is_user(instance, group_escaped):
  69. return redirect('/%s/%s/-/about' % (instance, group))
  70. if errcheck(instance, group=group_escaped) != 200:
  71. return abort(404)
  72. group_data = get_group_data(instance, group_escaped)
  73. if request.method == 'POST':
  74. search_query = request.form['laboratory_searchquery']
  75. else: search_query = None
  76. return render_template('common.html', title='%s' % (group),\
  77. group=1, projects='active', group_url="/%s/%s" % (instance, group), \
  78. instance=instance, group_name=group_data[1], group_desc=group_data[2], \
  79. projects_list=Markup(get_projects_list(\
  80. instance, search_query, group_escaped, page)), \
  81. group_avatar=group_data[0])
  82. @app.route('/<instance>/<path:group>/-/subgroups', methods = ['GET', 'POST'])
  83. def group_subgroups(instance = None, group = None):
  84. if request.args.get('page') is None:
  85. page = 1
  86. else:
  87. page = int(request.args.get('page'))
  88. group_escaped = group.replace('/', '%2F')
  89. if errcheck(instance, group=group_escaped) != 200:
  90. return abort(404)
  91. if is_user(instance, group_escaped):
  92. return redirect('/%s/%s/-/about' % (instance, group))
  93. group_data = get_group_data(instance, group_escaped)
  94. if request.method == 'POST':
  95. search_query = request.form['laboratory_searchquery']
  96. else: search_query = None
  97. return render_template('common.html', title='%s' % (group),\
  98. group=1, subgroups='active', group_url="/%s/%s" % (instance, group), \
  99. instance=instance, group_name=group_data[1], group_desc=group_data[2], \
  100. subgroups_list=Markup(get_subgroups_list(\
  101. instance, group_escaped, search_query, page)), \
  102. group_avatar=group_data[0])
  103. @app.route('/<instance>/<path:user_group>/<repo>/')
  104. def repository(instance = None, user_group = None, repo = None):
  105. full_reponame = '%s%%2F%s' % (user_group.replace('/', '%2F'), repo)
  106. if errcheck(instance, repo=full_reponame) != 200:
  107. return abort(404)
  108. return render_template('common.html', title=Markup(\
  109. '<a href="/%s/%s">%s</a>/%s' % (instance, user_group, user_group, repo)),\
  110. repository=1, summary='active', \
  111. repo_description=get_repo_description(instance, full_reponame), \
  112. repo_idle=get_repo_idle(instance, full_reponame), \
  113. repo_cloneurl=get_repo_cloneurls(instance, full_reponame)[0], \
  114. repo_clonessh=get_repo_cloneurls(instance, full_reponame)[1], \
  115. repo_branches_list=Markup(get_repo_brancheslist(instance, full_reponame)), \
  116. repo_tags_list=Markup(get_repo_tagslist(instance, full_reponame)), \
  117. repo_avatar=get_repo_avatar(instance, full_reponame), \
  118. repo_readme=Markup(get_repo_readme(instance, full_reponame)), \
  119. instance=instance, \
  120. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  121. @app.route('/<instance>/<path:user_group>/<repo>/-/branches')
  122. def repo_refs(instance = None, user_group = None, repo = None):
  123. full_reponame = '%s%%2F%s' % (user_group.replace('/', '%2F'), repo)
  124. if errcheck(instance, repo=full_reponame) != 200:
  125. return abort(404)
  126. return render_template('common.html', title=Markup(\
  127. '<a href="/%s/%s">%s</a>/%s' % (instance, user_group, user_group, repo)),\
  128. repository=1, refs='active', \
  129. repo_description=get_repo_description(instance, full_reponame), \
  130. repo_branches_list=Markup(get_repo_brancheslist(instance, full_reponame, 1)), \
  131. repo_avatar=get_repo_avatar(instance, full_reponame), \
  132. instance=instance, \
  133. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  134. @app.route('/<instance>/<path:user_group>/<repo>/-/commits/')
  135. @app.route('/<instance>/<path:user_group>/<repo>/-/commits/<branch>')
  136. def repo_log(instance = None, user_group = None, repo = None, branch = None):
  137. if request.args.get('page') is None:
  138. page = 1
  139. else:
  140. page = int(request.args.get('page'))
  141. full_reponame = '%s%%2F%s' % (user_group.replace('/', '%2F'), repo)
  142. if errcheck(instance, repo=full_reponame) != 200:
  143. return abort(404)
  144. return render_template('common.html', title=Markup(\
  145. '<a href="/%s/%s">%s</a>/%s' % (instance, user_group, user_group, repo)),\
  146. repository=1, log='active', \
  147. repo_description=get_repo_description(instance, full_reponame), \
  148. repo_commits_list=Markup(get_repo_commits(\
  149. instance, full_reponame, page, branch)), \
  150. repo_avatar=get_repo_avatar(instance, full_reponame), \
  151. instance=instance, \
  152. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  153. @app.route('/<instance>/<path:user_group>/<repo>/-/commit/<commit_id>')
  154. def repo_showlog(instance = None, user_group = None, repo = None, commit_id = None):
  155. full_reponame = '%s%%2F%s' % (user_group.replace('/', '%2F'), repo)
  156. if errcheck(instance, repo=full_reponame) != 200:
  157. return abort(404)
  158. commit_info = get_repo_commits(instance, full_reponame, 1, None, commit_id)
  159. return render_template('common.html', title=Markup(\
  160. '<a href="/%s/%s">%s</a>/%s' % (instance, user_group, user_group, repo)),\
  161. repository=1, log='active', \
  162. repo_description=get_repo_description(instance, full_reponame), \
  163. repo_commit_author=commit_info[0], \
  164. repo_commit_committer=commit_info[1], \
  165. repo_commit_id=commit_info[2], \
  166. repo_commit_parent=commit_info[3], \
  167. repo_commit_message=commit_info[4], \
  168. repo_commit_diff=commit_info[5], \
  169. repo_avatar=get_repo_avatar(instance, full_reponame), \
  170. instance=instance, \
  171. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  172. @app.route('/<instance>/<path:user_group>/<repo>/-/about')
  173. def repo_about(instance = None, user_group = None, repo = None):
  174. full_reponame = '%s%%2F%s' % (user_group.replace('/', '%2F'), repo)
  175. if errcheck(instance, repo=full_reponame) != 200:
  176. return abort(404)
  177. return render_template('common.html', title=Markup(\
  178. '<a href="/%s/%s">%s</a>/%s' % (instance, user_group, user_group, repo)),\
  179. repository=1, about='active', \
  180. repo_avatar=get_repo_avatar(instance, full_reponame), \
  181. repo_readme=Markup(get_repo_readme(instance, full_reponame)), \
  182. repo_description=get_repo_description(instance, full_reponame), \
  183. instance=instance, \
  184. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  185. @app.route('/<instance>/<path:user_group>/<repo>/-/tree/')
  186. @app.route('/<instance>/<path:user_group>/<repo>/-/tree/<branch>')
  187. @app.route('/<instance>/<path:user_group>/<repo>/-/tree/<branch>/<path:tree>')
  188. def repo_tree(instance = None, user_group = None, repo = None, \
  189. branch = None, tree = None):
  190. full_reponame = '%s%%2F%s' % (user_group.replace('/', '%2F'), repo)
  191. if errcheck(instance, repo=full_reponame) != 200:
  192. return abort(404)
  193. if branch == None: branch = get_repo_primary_branch(instance, full_reponame)
  194. if tree == None: tree = '/'
  195. return render_template('common.html', title=Markup(\
  196. '<a href="/%s/%s">%s</a>/%s' % (instance, user_group, user_group, repo)),\
  197. repository=1, tree='active', \
  198. repo_tree=Markup(get_repo_tree(instance, full_reponame, tree, branch)), \
  199. repo_description=get_repo_description(instance, full_reponame), \
  200. repo_avatar=get_repo_avatar(instance, full_reponame), \
  201. instance=instance, \
  202. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  203. @app.route('/<instance>/<path:user_group>/<repo>/-/blob/<branch>/<path:blob>')
  204. def repo_blob(instance = None, user_group = None, repo = None, \
  205. branch = None, blob = None):
  206. full_reponame = '%s%%2F%s' % (user_group.replace('/', '%2F'), repo)
  207. if errcheck(instance, repo=full_reponame) != 200:
  208. return abort(404)
  209. if branch == None: branch = get_repo_primary_branch(instance, full_reponame)
  210. return render_template('common.html', title=Markup(\
  211. '<a href="/%s/%s">%s</a>/%s' % (instance, user_group, user_group, repo)),\
  212. repository=1, tree='active', \
  213. repo_blob=get_repo_blob(instance, full_reponame, blob, branch)[0], \
  214. repo_bloblines=Markup(get_repo_blob(instance, full_reponame, blob, branch)[1]), \
  215. repo_description=get_repo_description(instance, full_reponame), \
  216. repo_avatar=get_repo_avatar(instance, full_reponame), \
  217. instance=instance, \
  218. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  219. @app.route('/<instance>/<path:user_group>/<repo>/-/issues')
  220. @app.route('/<instance>/<path:user_group>/<repo>/-/issues/')
  221. def repo_issues(instance = None, user_group = None, repo = None):
  222. if request.args.get('page') is None:
  223. page = 1
  224. else:
  225. page = int(request.args.get('page'))
  226. full_reponame = '%s%%2F%s' % (user_group.replace('/', '%2F'), repo)
  227. if errcheck(instance, repo=full_reponame) != 200:
  228. return abort(404)
  229. return render_template('common.html', title=Markup(\
  230. '<a href="/%s/%s">%s</a>/%s' % (instance, user_group, user_group, repo)),\
  231. repository=1, issues='active', \
  232. repo_issueslist=Markup(get_repo_issues(instance, full_reponame, \
  233. request.args.get('state'), page)), \
  234. repo_avatar=get_repo_avatar(instance, full_reponame), \
  235. repo_description=get_repo_description(instance, full_reponame), \
  236. repo_issuesstate=request.args.get('state') \
  237. if request.args.get('state') is not None else "all", \
  238. repo_issuesnextpage=str(page + 1), \
  239. instance=instance, \
  240. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  241. @app.route('/<instance>/<path:user_group>/<repo>/-/issues/<iid>')
  242. def repo_issue(instance = None, user_group = None, repo = None, iid = None):
  243. full_reponame = '%s%%2F%s' % (user_group.replace('/', '%2F'), repo)
  244. if errcheck(instance, repo=full_reponame) != 200:
  245. return abort(404)
  246. issue = get_repo_issue(instance, full_reponame, iid)
  247. return render_template('common.html', title=Markup(\
  248. '<a href="/%s/%s">%s</a>/%s' % (instance, user_group, user_group, repo)),\
  249. repository=1, issues='active', \
  250. repo_issueid=iid, \
  251. repo_issuetitle=issue[0], \
  252. repo_issuestate=issue[1], \
  253. repo_issueidle=issue[2], \
  254. repo_issueauthor=issue[3], \
  255. repo_issue=Markup(issue[4].replace('\n', '<br />')), \
  256. repo_description=get_repo_description(instance, full_reponame), \
  257. repo_issueparticipants=get_repo_issueparticipants(instance, \
  258. full_reponame, iid), \
  259. repo_avatar=get_repo_avatar(instance, full_reponame), \
  260. repo_issuesstate=request.args.get('state'), \
  261. instance=instance, \
  262. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  263. @app.route("/<instance>/<user_group>/<repo>/-/wikis/<path:wiki>",strict_slashes=False)
  264. @app.route("/<instance>/<user_group>/<repo>/-/wikis/<path:wiki>",strict_slashes=False)
  265. def project_wiki(instance = None, user_group = None, repo = None, wiki = None):
  266. full_reponame = "%s%%2F%s" % (user_group.replace('/',"%2F"), repo)
  267. if errcheck(instance, repo=full_reponame) != 200:
  268. return abort(404)
  269. wikicontent = ""
  270. if wiki == "pages":
  271. wikicontent = Markup(get_project_wiki_sitemap(instance, full_reponame))
  272. else:
  273. wikicontent = Markup(get_project_wiki_page(instance, full_reponame, wiki))
  274. return render_template("common.html", title=Markup(\
  275. '<a href="/%s/%s">%s</a>/<a href="/%s/%s/%s/-/wikis/home">%s</a>' \
  276. % (instance, user_group, user_group, instance, user_group, repo, repo)),\
  277. repository=1, wikis='active', \
  278. repo_avatar=get_repo_avatar(instance, full_reponame), \
  279. repo_wiki=wikicontent, instance=instance, \
  280. repo_description=get_repo_description(instance, full_reponame), \
  281. repo_url="/%s/%s/%s" % (instance, user_group, repo))
  282. if __name__ == "__main__":
  283. print("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=")
  284. print("=== Laboratory GitLab web frontend ===")
  285. print("------ Flask Development server ------")
  286. print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
  287. if len(argv) > 1 and argv[1] == '-h':
  288. print("Usage: ./app.py [-d]")
  289. print("\t-d\tEnable debug mode.")
  290. elif len(argv) > 1 and argv[1] == '-d':
  291. app.run(debug=True)
  292. else: app.run(debug=False)