125 lines
3.7 KiB
Python
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}"
|
|
)
|