diff --git a/app/WYGIWYH/settings.py b/app/WYGIWYH/settings.py index 79d3e89..f05c8f3 100644 --- a/app/WYGIWYH/settings.py +++ b/app/WYGIWYH/settings.py @@ -487,6 +487,8 @@ else: CACHALOT_UNCACHABLE_TABLES = ("django_migrations", "procrastinate_jobs") +# Procrastinate +PROCRASTINATE_ON_APP_READY = "apps.common.procrastinate.on_app_ready" # PWA PWA_APP_NAME = SITE_TITLE diff --git a/app/apps/common/apps.py b/app/apps/common/apps.py index 76c08b2..213972d 100644 --- a/app/apps/common/apps.py +++ b/app/apps/common/apps.py @@ -1,4 +1,5 @@ from django.apps import AppConfig +from django.core.cache import cache class CommonConfig(AppConfig): @@ -18,3 +19,7 @@ class CommonConfig(AppConfig): admin.site.unregister(SocialAccount) admin.site.unregister(SocialApp) admin.site.unregister(SocialToken) + + # Delete the cache for update checks to prevent false-positives when the app is restarted + # this will be recreated by the check_for_updates task + cache.delete("update_check") diff --git a/app/apps/common/procrastinate.py b/app/apps/common/procrastinate.py new file mode 100644 index 0000000..c6f3c3d --- /dev/null +++ b/app/apps/common/procrastinate.py @@ -0,0 +1,6 @@ +import procrastinate + + +def on_app_ready(app: procrastinate.App): + """This function is ran upon procrastinate initialization.""" + ... diff --git a/app/apps/common/tasks.py b/app/apps/common/tasks.py index 2218d45..b462a38 100644 --- a/app/apps/common/tasks.py +++ b/app/apps/common/tasks.py @@ -1,13 +1,17 @@ import logging +from packaging.version import parse as parse_version, InvalidVersion from asgiref.sync import sync_to_async from django.conf import settings from django.core import management from django.db import DEFAULT_DB_ALIAS +from django.core.cache import cache from procrastinate import builtin_tasks from procrastinate.contrib.django import app +import requests + logger = logging.getLogger(__name__) @@ -79,3 +83,46 @@ def reset_demo_data(timestamp=None): except Exception as e: logger.exception(f"Error during daily demo data reset: {e}") raise + + +@app.periodic(cron="0 */12 * * *") # Every 12 hours +@app.task( + name="check_for_updates", +) +def check_for_updates(timestamp=None): + url = "https://api.github.com/repos/eitchtee/WYGIWYH/releases/latest" + + try: + response = requests.get(url, timeout=60) + response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx) + + data = response.json() + latest_version = data.get("tag_name") + + if latest_version: + try: + current_v = parse_version(settings.APP_VERSION) + except InvalidVersion: + current_v = parse_version("0.0.0") + try: + latest_v = parse_version(latest_version) + except InvalidVersion: + latest_v = parse_version("0.0.0") + + update_info = { + "update_available": False, + "current_version": str(current_v), + "latest_version": str(latest_v), + } + + if latest_v > current_v: + update_info["update_available"] = True + + # Cache the entire dictionary + cache.set("update_check", update_info, 60 * 60 * 25) + logger.info(f"Update check complete. Result: {update_info}") + else: + logger.warning("Could not find 'tag_name' in GitHub API response.") + + except requests.exceptions.RequestException as e: + logger.error(f"Failed to fetch updates from GitHub: {e}") diff --git a/app/apps/common/templatetags/cache_access.py b/app/apps/common/templatetags/cache_access.py new file mode 100644 index 0000000..154986f --- /dev/null +++ b/app/apps/common/templatetags/cache_access.py @@ -0,0 +1,17 @@ +# core/templatetags/update_tags.py +from django import template +from django.core.cache import cache + +register = template.Library() + + +@register.simple_tag +def get_update_check(): + """ + Retrieves the update status dictionary from the cache. + Returns a default dictionary if nothing is found. + """ + return cache.get("update_check") or { + "update_available": False, + "latest_version": "N/A", + } diff --git a/app/templates/includes/navbar.html b/app/templates/includes/navbar.html index d38b920..c0509e7 100644 --- a/app/templates/includes/navbar.html +++ b/app/templates/includes/navbar.html @@ -1,3 +1,4 @@ +{% load cache_access %} {% load settings %} {% load static %} {% load i18n %} @@ -162,9 +163,12 @@