host/app/hosting/tasks.py

125 lines
3.7 KiB
Python

"""
Background tasks for Org Social Host.
"""
import logging
import time
from datetime import timedelta
import requests
from django.conf import settings
from django.utils import timezone
from huey import crontab
from huey.contrib.djhuey import db_periodic_task
from .models import HostedFile
logger = logging.getLogger(__name__)
# Seconds to sleep between consecutive POST requests to the relay
RELAY_SYNC_REQUEST_DELAY = 1.0
@db_periodic_task(crontab(hour="0", minute="0"))
def cleanup_stale_files():
"""
Clean up files that haven't been updated within the TTL period.
Runs daily at midnight UTC.
"""
# Check if cleanup is enabled
if not settings.ENABLE_CLEANUP:
logger.info("Automatic cleanup is disabled. Skipping cleanup task.")
return
logger.info("Starting cleanup of stale files...")
# Calculate cutoff date
cutoff_date = timezone.now() - timedelta(days=settings.FILE_TTL_DAYS)
# Find stale files
stale_files = HostedFile.objects.filter(last_access__lt=cutoff_date)
count = stale_files.count()
if count == 0:
logger.info("No stale files found.")
return
logger.info(f"Found {count} stale files to delete.")
# Delete database records
for hosted_file in stale_files:
try:
nickname = hosted_file.nickname
hosted_file.delete()
logger.info(f"Deleted hosted file record: {nickname}")
except Exception as e:
logger.error(f"Error deleting file {hosted_file.nickname}: {e}")
logger.info(f"Cleanup completed. Deleted {count} stale files.")
def _build_public_feed_url(nickname: str) -> str:
"""Build the public social.org URL for a given nickname using SITE_DOMAIN."""
domain = settings.SITE_DOMAIN
scheme = "http" if domain.startswith("localhost") else "https"
return f"{scheme}://{domain}/{nickname}/social.org"
@db_periodic_task(crontab(minute="0"))
def sync_feeds_to_relay():
"""
Register all hosted feeds on the configured relay every hour.
Disabled when RELAY_URL is not set. The relay handles deduplication, so
this task simply re-announces all public feeds on every run.
"""
relay_url = settings.RELAY_URL
if not relay_url:
logger.info("RELAY_URL is not configured. Skipping relay sync task.")
return
feeds_endpoint = f"{relay_url}/feeds/"
hosted_files = HostedFile.objects.filter(
redirect_url__isnull=True,
).exclude(file_content="")
total = hosted_files.count()
if total == 0:
logger.info("No hosted feeds to sync to relay.")
return
logger.info(f"Syncing {total} feed(s) to relay {feeds_endpoint}")
succeeded = 0
failed = 0
for index, hosted_file in enumerate(hosted_files):
feed_url = _build_public_feed_url(hosted_file.nickname)
try:
response = requests.post(
feeds_endpoint,
json={"feed": feed_url},
timeout=10,
)
if response.status_code in (200, 201):
succeeded += 1
else:
failed += 1
logger.warning(
f"Relay rejected feed {feed_url}: "
f"HTTP {response.status_code} {response.text[:200]}"
)
except requests.RequestException as exc:
failed += 1
logger.warning(f"Failed to register feed {feed_url} on relay: {exc}")
# Throttle: pause between requests, except after the final one.
if index < total - 1:
time.sleep(RELAY_SYNC_REQUEST_DELAY)
logger.info(
f"Relay sync completed. Succeeded: {succeeded}, Failed: {failed}, Total: {total}"
)