mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-17 05:54:19 +01:00
Add code to support integration with a Vite build system for JS/CSS (#2051)
* Adds the `Assets` constant to access front end assets from Jinja templates * Adds a `views.themes_beta` route to avoid the `.dev`/`.min` extension being added automatically to frontend asset urls * Add `count` meta field to `/api/v1/users/me/solves`, `/api/v1/users/me/fails`, `/api/v1/users/me/awards`, `/api/v1/users/[user_id]/solves`, `/api/v1/users/[user_id]/fails`, `/api/v1/users/[user_id]/awards` * Works on #2049
This commit is contained in:
@@ -339,7 +339,8 @@ class UserPrivateSolves(Resource):
|
||||
if response.errors:
|
||||
return {"success": False, "errors": response.errors}, 400
|
||||
|
||||
return {"success": True, "data": response.data}
|
||||
count = len(response.data)
|
||||
return {"success": True, "data": response.data, "meta": {"count": count}}
|
||||
|
||||
|
||||
@users_namespace.route("/me/fails")
|
||||
@@ -358,8 +359,8 @@ class UserPrivateFails(Resource):
|
||||
data = response.data
|
||||
else:
|
||||
data = []
|
||||
count = len(response.data)
|
||||
|
||||
count = len(response.data)
|
||||
return {"success": True, "data": data, "meta": {"count": count}}
|
||||
|
||||
|
||||
@@ -377,7 +378,8 @@ class UserPrivateAwards(Resource):
|
||||
if response.errors:
|
||||
return {"success": False, "errors": response.errors}, 400
|
||||
|
||||
return {"success": True, "data": response.data}
|
||||
count = len(response.data)
|
||||
return {"success": True, "data": response.data, "meta": {"count": count}}
|
||||
|
||||
|
||||
@users_namespace.route("/<user_id>/solves")
|
||||
@@ -399,7 +401,8 @@ class UserPublicSolves(Resource):
|
||||
if response.errors:
|
||||
return {"success": False, "errors": response.errors}, 400
|
||||
|
||||
return {"success": True, "data": response.data}
|
||||
count = len(response.data)
|
||||
return {"success": True, "data": response.data, "meta": {"count": count}}
|
||||
|
||||
|
||||
@users_namespace.route("/<user_id>/fails")
|
||||
@@ -423,8 +426,8 @@ class UserPublicFails(Resource):
|
||||
data = response.data
|
||||
else:
|
||||
data = []
|
||||
count = len(response.data)
|
||||
|
||||
count = len(response.data)
|
||||
return {"success": True, "data": data, "meta": {"count": count}}
|
||||
|
||||
|
||||
@@ -446,7 +449,8 @@ class UserPublicAwards(Resource):
|
||||
if response.errors:
|
||||
return {"success": False, "errors": response.errors}, 400
|
||||
|
||||
return {"success": True, "data": response.data}
|
||||
count = len(response.data)
|
||||
return {"success": True, "data": response.data, "meta": {"count": count}}
|
||||
|
||||
|
||||
@users_namespace.route("/<int:user_id>/email")
|
||||
|
||||
51
CTFd/constants/assets.py
Normal file
51
CTFd/constants/assets.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import os
|
||||
|
||||
from flask import current_app, url_for
|
||||
|
||||
from CTFd.utils import _get_asset_json
|
||||
from CTFd.utils.config import ctf_theme
|
||||
from CTFd.utils.helpers import markup
|
||||
|
||||
|
||||
class _AssetsWrapper:
|
||||
def manifest(self):
|
||||
theme = ctf_theme()
|
||||
manifest = os.path.join(
|
||||
current_app.root_path, "themes", theme, "static", "manifest.json"
|
||||
)
|
||||
return _get_asset_json(path=manifest)
|
||||
|
||||
def manifest_css(self):
|
||||
theme = ctf_theme()
|
||||
manifest = os.path.join(
|
||||
current_app.root_path, "themes", theme, "static", "manifest-css.json"
|
||||
)
|
||||
return _get_asset_json(path=manifest)
|
||||
|
||||
def js(self, asset_key):
|
||||
asset = self.manifest()[asset_key]
|
||||
entry = asset["file"]
|
||||
imports = asset.get("imports", [])
|
||||
html = ""
|
||||
for i in imports:
|
||||
# TODO: Needs a better recursive solution
|
||||
i = self.manifest()[i]["file"]
|
||||
url = url_for("views.themes_beta", path=i)
|
||||
html += f'<script defer type="module" src="{url}"></script>'
|
||||
url = url_for("views.themes_beta", path=entry)
|
||||
html += f'<script defer type="module" src="{url}"></script>'
|
||||
return markup(html)
|
||||
|
||||
def css(self, asset_key):
|
||||
asset = self.manifest_css()[asset_key]
|
||||
entry = asset["file"]
|
||||
url = url_for("views.themes_beta", path=entry)
|
||||
return markup(f'<link rel="stylesheet" href="{url}">')
|
||||
|
||||
def file(self, asset_key):
|
||||
asset = self.manifest()[asset_key]
|
||||
entry = asset["file"]
|
||||
return url_for("views.themes_beta", path=entry)
|
||||
|
||||
|
||||
Assets = _AssetsWrapper()
|
||||
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
from enum import Enum
|
||||
|
||||
import cmarkgfm
|
||||
@@ -26,6 +27,12 @@ def get_app_config(key, default=None):
|
||||
return value
|
||||
|
||||
|
||||
@cache.memoize()
|
||||
def _get_asset_json(path):
|
||||
with open(path) as f:
|
||||
return json.loads(f.read())
|
||||
|
||||
|
||||
@cache.memoize()
|
||||
def _get_config(key):
|
||||
config = db.session.execute(
|
||||
|
||||
@@ -29,7 +29,12 @@ def get_errors():
|
||||
|
||||
@current_app.url_defaults
|
||||
def env_asset_url_default(endpoint, values):
|
||||
"""Create asset URLs dependent on the current env"""
|
||||
"""
|
||||
Create asset URLs dependent on the current env
|
||||
|
||||
In CTFd 4.0 this url_for behavior and the themes_beta
|
||||
route will be removed in favor of an improved theme system
|
||||
"""
|
||||
if endpoint == "views.themes":
|
||||
path = values.get("path", "")
|
||||
static_asset = path.endswith(".js") or path.endswith(".css")
|
||||
|
||||
@@ -53,6 +53,7 @@ def init_template_filters(app):
|
||||
|
||||
def init_template_globals(app):
|
||||
from CTFd.constants import JINJA_ENUMS
|
||||
from CTFd.constants.assets import Assets
|
||||
from CTFd.constants.config import Configs
|
||||
from CTFd.constants.plugins import Plugins
|
||||
from CTFd.constants.sessions import Session
|
||||
@@ -101,6 +102,7 @@ def init_template_globals(app):
|
||||
app.jinja_env.globals.update(get_current_user_attrs=get_current_user_attrs)
|
||||
app.jinja_env.globals.update(get_current_team_attrs=get_current_team_attrs)
|
||||
app.jinja_env.globals.update(get_ip=get_ip)
|
||||
app.jinja_env.globals.update(Assets=Assets)
|
||||
app.jinja_env.globals.update(Configs=Configs)
|
||||
app.jinja_env.globals.update(Plugins=Plugins)
|
||||
app.jinja_env.globals.update(Session=Session)
|
||||
|
||||
@@ -484,3 +484,23 @@ def themes(theme, path):
|
||||
if os.path.isfile(cand_path):
|
||||
return send_file(cand_path)
|
||||
abort(404)
|
||||
|
||||
|
||||
@views.route("/themes/<theme>/static/<path:path>")
|
||||
def themes_beta(theme, path):
|
||||
"""
|
||||
This is a copy of the above themes route used to avoid
|
||||
the current appending of .dev and .min for theme assets.
|
||||
|
||||
In CTFd 4.0 this url_for behavior and this themes_beta
|
||||
route will be removed.
|
||||
"""
|
||||
for cand_path in (
|
||||
safe_join(app.root_path, "themes", cand_theme, "static", path)
|
||||
# The `theme` value passed in may not be the configured one, e.g. for
|
||||
# admin pages, so we check that first
|
||||
for cand_theme in (theme, *config.ctf_theme_candidates())
|
||||
):
|
||||
if os.path.isfile(cand_path):
|
||||
return send_file(cand_path)
|
||||
abort(404)
|
||||
|
||||
Reference in New Issue
Block a user