mirror of
https://github.com/narrrl/holy-flavor-rater.git
synced 2026-11-03 16:41:27 +01:00
fixed worker entrypoint and implemented live logs with manual job scheduling
This commit is contained in:
1 parent
ef93ea41ca
commit
31144e4704
5 files changed
+88
-27
No files matched your search
@@ -11,6 +11,7 @@ class Command(BaseCommand):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--output', type=str, help='Specific output path for the backup file')
|
||||
parser.add_argument('--retention', type=int, default=7, help='Number of days to keep old backups')
|
||||
parser.add_argument('--full', action='store_true', help='Create a full archive including media')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
db_conf = settings.DATABASES['default']
|
||||
@@ -22,29 +23,53 @@ class Command(BaseCommand):
|
||||
backup_dir = os.path.join(settings.BASE_DIR, 'backups')
|
||||
os.makedirs(backup_dir, exist_ok=True)
|
||||
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
|
||||
# 1. Database Backup
|
||||
if options['output']:
|
||||
backup_path = options['output']
|
||||
db_backup_path = options['output']
|
||||
else:
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
backup_path = os.path.join(backup_dir, f'db_backup_{timestamp}.sqlite3')
|
||||
db_backup_path = os.path.join(backup_dir, f'db_backup_{timestamp}.sqlite3')
|
||||
|
||||
self.stdout.write(f"Starting backup of {db_path}...")
|
||||
self.stdout.write(f"Cloning database {db_path}...")
|
||||
|
||||
try:
|
||||
# Use SQLite's online backup API for a consistent copy
|
||||
src = sqlite3.connect(db_path)
|
||||
dst = sqlite3.connect(backup_path)
|
||||
dst = sqlite3.connect(db_backup_path)
|
||||
with dst:
|
||||
src.backup(dst)
|
||||
dst.close()
|
||||
src.close()
|
||||
self.stdout.write(self.style.SUCCESS(f"Successfully backed up database to {backup_path}"))
|
||||
self.stdout.write(self.style.SUCCESS(f"DB backup created: {db_backup_path}"))
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.ERROR(f"Database backup failed: {e}"))
|
||||
self.stdout.write(self.style.ERROR(f"DB backup failed: {e}"))
|
||||
return
|
||||
|
||||
# 2. Cleanup Old Backups
|
||||
# 2. Full Archive (Optional)
|
||||
if options['full']:
|
||||
import tarfile
|
||||
archive_name = f"full_backup_{timestamp}.tar.gz"
|
||||
archive_path = os.path.join(backup_dir, archive_name)
|
||||
self.stdout.write(f"Creating full archive at {archive_path}...")
|
||||
|
||||
try:
|
||||
with tarfile.open(archive_path, "w:gz") as tar:
|
||||
# Add database
|
||||
tar.add(db_backup_path, arcname="db.sqlite3")
|
||||
# Add media
|
||||
media_dir = str(settings.MEDIA_ROOT)
|
||||
if os.path.exists(media_dir):
|
||||
tar.add(media_dir, arcname="media")
|
||||
|
||||
# Remove the raw sqlite backup after bundling it into the archive
|
||||
if not options['output']:
|
||||
os.remove(db_backup_path)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f"Full archive created successfully."))
|
||||
except Exception as e:
|
||||
self.stdout.write(self.style.ERROR(f"Archive creation failed: {e}"))
|
||||
|
||||
# 3. Cleanup Old Backups
|
||||
retention_days = options['retention']
|
||||
cutoff = datetime.now() - timedelta(days=retention_days)
|
||||
|
||||
|
||||
@@ -39,10 +39,18 @@ class Command(BaseCommand):
|
||||
self.stdout.write(f"Executing Job: {job.get_name_display()}...")
|
||||
job.status = 'running'
|
||||
job.last_run = timezone.now()
|
||||
job.last_output = "" # Reset output for new run
|
||||
job.save()
|
||||
|
||||
# Capture management command output
|
||||
out = StringIO()
|
||||
class LiveStream(StringIO):
|
||||
def write(self, s):
|
||||
super().write(s)
|
||||
# Periodically update the DB with current output
|
||||
job.last_output = self.getvalue()
|
||||
job.save(update_fields=['last_output'])
|
||||
|
||||
out = LiveStream()
|
||||
error = ""
|
||||
|
||||
try:
|
||||
@@ -51,7 +59,7 @@ class Command(BaseCommand):
|
||||
elif job.name == 'cleanup_duplicates':
|
||||
call_command('cleanup_duplicates', stdout=out)
|
||||
elif job.name == 'backup_db':
|
||||
call_command('backup_db', stdout=out)
|
||||
call_command('backup_db', '--full', stdout=out)
|
||||
elif job.name == 'seed_legacy':
|
||||
call_command('seed_legacy_flavors', stdout=out)
|
||||
|
||||
|
||||
+11
-5
@@ -912,13 +912,19 @@ class AdminViewSet(viewsets.ViewSet):
|
||||
try:
|
||||
job = Job.objects.get(pk=pk)
|
||||
interval = request.data.get('interval_hours')
|
||||
next_run = request.data.get('next_run')
|
||||
|
||||
if interval is not None:
|
||||
job.interval_hours = int(interval)
|
||||
# If we just enabled it, set next_run
|
||||
if job.interval_hours > 0 and not job.next_run:
|
||||
from django.utils import timezone
|
||||
job.next_run = timezone.now()
|
||||
job.save()
|
||||
|
||||
if next_run is not None:
|
||||
from django.utils.dateparse import parse_datetime
|
||||
job.next_run = parse_datetime(next_run)
|
||||
elif interval is not None and job.interval_hours > 0 and not job.next_run:
|
||||
from django.utils import timezone
|
||||
job.next_run = timezone.now()
|
||||
|
||||
job.save()
|
||||
return Response(JobSerializer(job).data)
|
||||
except Job.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
Reference in new issue
Block a user