mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-18 14:34:21 +01:00
Make scoreboard caching only cache the score table (#1586)
* Make scoreboard caching only cache the score table instead of the entire page * Closes #1584
This commit is contained in:
7
CTFd/cache/__init__.py
vendored
7
CTFd/cache/__init__.py
vendored
@@ -1,5 +1,5 @@
|
|||||||
from flask import request
|
from flask import request
|
||||||
from flask_caching import Cache
|
from flask_caching import Cache, make_template_fragment_key
|
||||||
|
|
||||||
cache = Cache()
|
cache = Cache()
|
||||||
|
|
||||||
@@ -27,6 +27,7 @@ def clear_config():
|
|||||||
|
|
||||||
def clear_standings():
|
def clear_standings():
|
||||||
from CTFd.models import Users, Teams
|
from CTFd.models import Users, Teams
|
||||||
|
from CTFd.constants.static import CacheKeys
|
||||||
from CTFd.utils.scores import get_standings, get_team_standings, get_user_standings
|
from CTFd.utils.scores import get_standings, get_team_standings, get_user_standings
|
||||||
from CTFd.api.v1.scoreboard import ScoreboardDetail, ScoreboardList
|
from CTFd.api.v1.scoreboard import ScoreboardDetail, ScoreboardList
|
||||||
from CTFd.api import api
|
from CTFd.api import api
|
||||||
@@ -55,11 +56,13 @@ def clear_standings():
|
|||||||
cache.delete_memoized(get_team_place)
|
cache.delete_memoized(get_team_place)
|
||||||
|
|
||||||
# Clear out HTTP request responses
|
# Clear out HTTP request responses
|
||||||
cache.delete(make_cache_key(path="scoreboard.listing"))
|
|
||||||
cache.delete(make_cache_key(path=api.name + "." + ScoreboardList.endpoint))
|
cache.delete(make_cache_key(path=api.name + "." + ScoreboardList.endpoint))
|
||||||
cache.delete(make_cache_key(path=api.name + "." + ScoreboardDetail.endpoint))
|
cache.delete(make_cache_key(path=api.name + "." + ScoreboardDetail.endpoint))
|
||||||
cache.delete_memoized(ScoreboardList.get)
|
cache.delete_memoized(ScoreboardList.get)
|
||||||
|
|
||||||
|
# Clear out scoreboard templates
|
||||||
|
cache.delete(make_template_fragment_key(CacheKeys.PUBLIC_SCOREBOARD_TABLE))
|
||||||
|
|
||||||
|
|
||||||
def clear_pages():
|
def clear_pages():
|
||||||
from CTFd.utils.config.pages import get_page, get_pages
|
from CTFd.utils.config.pages import get_page, get_pages
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from enum import Enum
|
|||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
JS_ENUMS = {}
|
JS_ENUMS = {}
|
||||||
|
JINJA_ENUMS = {}
|
||||||
|
|
||||||
|
|
||||||
class RawEnum(Enum):
|
class RawEnum(Enum):
|
||||||
@@ -59,6 +60,7 @@ def JinjaEnum(cls):
|
|||||||
"""
|
"""
|
||||||
if cls.__name__ not in current_app.jinja_env.globals:
|
if cls.__name__ not in current_app.jinja_env.globals:
|
||||||
current_app.jinja_env.globals[cls.__name__] = cls
|
current_app.jinja_env.globals[cls.__name__] = cls
|
||||||
|
JINJA_ENUMS[cls.__name__] = cls
|
||||||
else:
|
else:
|
||||||
raise KeyError("{} was already defined as a JinjaEnum".format(cls.__name__))
|
raise KeyError("{} was already defined as a JinjaEnum".format(cls.__name__))
|
||||||
return cls
|
return cls
|
||||||
|
|||||||
14
CTFd/constants/static.py
Normal file
14
CTFd/constants/static.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from CTFd.constants import JinjaEnum, RawEnum
|
||||||
|
|
||||||
|
|
||||||
|
@JinjaEnum
|
||||||
|
class CacheKeys(str, RawEnum):
|
||||||
|
PUBLIC_SCOREBOARD_TABLE = "public_scoreboard_table"
|
||||||
|
|
||||||
|
|
||||||
|
# Placeholder object. Not used, just imported to force initialization of any Enums here
|
||||||
|
class _StaticsWrapper:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
Static = _StaticsWrapper()
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
from flask import Blueprint, render_template
|
from flask import Blueprint, render_template
|
||||||
|
|
||||||
from CTFd.cache import cache, make_cache_key
|
|
||||||
from CTFd.utils import config
|
from CTFd.utils import config
|
||||||
from CTFd.utils.config.visibility import scores_visible
|
from CTFd.utils.config.visibility import scores_visible
|
||||||
from CTFd.utils.decorators.visibility import check_score_visibility
|
from CTFd.utils.decorators.visibility import check_score_visibility
|
||||||
@@ -13,7 +12,6 @@ scoreboard = Blueprint("scoreboard", __name__)
|
|||||||
|
|
||||||
@scoreboard.route("/scoreboard")
|
@scoreboard.route("/scoreboard")
|
||||||
@check_score_visibility
|
@check_score_visibility
|
||||||
@cache.cached(timeout=60, key_prefix=make_cache_key)
|
|
||||||
def listing():
|
def listing():
|
||||||
infos = get_infos()
|
infos = get_infos()
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% cache 60, CacheKeys.PUBLIC_SCOREBOARD_TABLE %}
|
||||||
{% if standings %}
|
{% if standings %}
|
||||||
<div id="scoreboard" class="row">
|
<div id="scoreboard" class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
@@ -55,6 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endcache %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -52,9 +52,11 @@ def init_template_filters(app):
|
|||||||
|
|
||||||
|
|
||||||
def init_template_globals(app):
|
def init_template_globals(app):
|
||||||
|
from CTFd.constants import JINJA_ENUMS
|
||||||
from CTFd.constants.config import Configs
|
from CTFd.constants.config import Configs
|
||||||
from CTFd.constants.plugins import Plugins
|
from CTFd.constants.plugins import Plugins
|
||||||
from CTFd.constants.sessions import Session
|
from CTFd.constants.sessions import Session
|
||||||
|
from CTFd.constants.static import Static
|
||||||
from CTFd.constants.users import User
|
from CTFd.constants.users import User
|
||||||
from CTFd.constants.teams import Team
|
from CTFd.constants.teams import Team
|
||||||
from CTFd.forms import Forms
|
from CTFd.forms import Forms
|
||||||
@@ -101,10 +103,20 @@ def init_template_globals(app):
|
|||||||
app.jinja_env.globals.update(Configs=Configs)
|
app.jinja_env.globals.update(Configs=Configs)
|
||||||
app.jinja_env.globals.update(Plugins=Plugins)
|
app.jinja_env.globals.update(Plugins=Plugins)
|
||||||
app.jinja_env.globals.update(Session=Session)
|
app.jinja_env.globals.update(Session=Session)
|
||||||
|
app.jinja_env.globals.update(Static=Static)
|
||||||
app.jinja_env.globals.update(Forms=Forms)
|
app.jinja_env.globals.update(Forms=Forms)
|
||||||
app.jinja_env.globals.update(User=User)
|
app.jinja_env.globals.update(User=User)
|
||||||
app.jinja_env.globals.update(Team=Team)
|
app.jinja_env.globals.update(Team=Team)
|
||||||
|
|
||||||
|
# Add in JinjaEnums
|
||||||
|
# The reason this exists is that on double import, JinjaEnums are not reinitialized
|
||||||
|
# Thus, if you try to create two jinja envs (e.g. during testing), sometimes
|
||||||
|
# an Enum will not be available to Jinja.
|
||||||
|
# Instead we can just directly grab them from the persisted global dictionary.
|
||||||
|
for k, v in JINJA_ENUMS.items():
|
||||||
|
# .update() can't be used here because it would use the literal value k
|
||||||
|
app.jinja_env.globals[k] = v
|
||||||
|
|
||||||
|
|
||||||
def init_logs(app):
|
def init_logs(app):
|
||||||
logger_submissions = logging.getLogger("submissions")
|
logger_submissions = logging.getLogger("submissions")
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from flask_caching import make_template_fragment_key
|
||||||
|
|
||||||
from CTFd.cache import clear_standings
|
from CTFd.cache import clear_standings
|
||||||
from tests.helpers import (
|
from tests.helpers import (
|
||||||
create_ctfd,
|
create_ctfd,
|
||||||
@@ -40,13 +42,19 @@ def test_scoreboard_is_cached():
|
|||||||
assert app.cache.get("view/api.scoreboard_scoreboard_detail")
|
assert app.cache.get("view/api.scoreboard_scoreboard_detail")
|
||||||
|
|
||||||
# Check scoreboard page
|
# Check scoreboard page
|
||||||
assert app.cache.get("view/scoreboard.listing") is None
|
assert (
|
||||||
|
app.cache.get(make_template_fragment_key("public_scoreboard_table"))
|
||||||
|
is None
|
||||||
|
)
|
||||||
client.get("/scoreboard")
|
client.get("/scoreboard")
|
||||||
assert app.cache.get("view/scoreboard.listing")
|
assert app.cache.get(make_template_fragment_key("public_scoreboard_table"))
|
||||||
|
|
||||||
# Empty standings and check that the cached data is gone
|
# Empty standings and check that the cached data is gone
|
||||||
clear_standings()
|
clear_standings()
|
||||||
assert app.cache.get("view/api.scoreboard_scoreboard_list") is None
|
assert app.cache.get("view/api.scoreboard_scoreboard_list") is None
|
||||||
assert app.cache.get("view/api.scoreboard_scoreboard_detail") is None
|
assert app.cache.get("view/api.scoreboard_scoreboard_detail") is None
|
||||||
assert app.cache.get("view/scoreboard.listing") is None
|
assert (
|
||||||
|
app.cache.get(make_template_fragment_key("public_scoreboard_table"))
|
||||||
|
is None
|
||||||
|
)
|
||||||
destroy_ctfd(app)
|
destroy_ctfd(app)
|
||||||
|
|||||||
Reference in New Issue
Block a user