Store background images in DB, persist SQLite via volume
All checks were successful
Build and push Docker image / build (push) Successful in 12s
Test / test (push) Successful in 14s

This commit is contained in:
marwin 2026-03-16 20:24:20 +01:00
parent 5c8c57f04f
commit 978b6fa24b
7 changed files with 22 additions and 36 deletions

View file

@ -11,7 +11,7 @@ RUN pip install --no-cache-dir -r requirements.txt gunicorn
COPY . .
RUN python manage.py collectstatic --noinput
RUN python manage.py collectstatic --noinput && mkdir -p /app/data
EXPOSE 8000

View file

@ -1,6 +1,3 @@
# Generated by Django 4.2.29 on 2026-03-15 21:37
import accounts.models
from django.db import migrations, models
@ -13,7 +10,7 @@ class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name='userprofile',
name='background_image',
field=models.FileField(blank=True, null=True, upload_to=accounts.models._bg_upload_path),
name='background_image_data',
field=models.TextField(blank=True),
),
]

View file

@ -4,17 +4,12 @@ from django.db.models.signals import post_save
from django.dispatch import receiver
def _bg_upload_path(instance, filename):
ext = filename.rsplit('.', 1)[-1].lower()
return f'backgrounds/bg_{instance.user_id}.{ext}'
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
lastfm_session_key = models.CharField(max_length=100, blank=True)
lastfm_username = models.CharField(max_length=100, blank=True)
lastfm_scrobble = models.BooleanField(default=True)
background_image = models.FileField(upload_to=_bg_upload_path, null=True, blank=True)
background_image_data = models.TextField(blank=True) # base64 data URL
def has_lastfm(self) -> bool:
return bool(self.lastfm_session_key)

View file

@ -1,4 +1,4 @@
import os
import base64
from django.conf import settings
from django.contrib.auth import authenticate, login, get_user_model
from django.contrib.auth.decorators import login_required
@ -123,35 +123,29 @@ def upload_background(request):
return JsonResponse({'error': 'no file'}, status=400)
ext = f.name.rsplit('.', 1)[-1].lower() if '.' in f.name else ''
if ext not in ('jpg', 'jpeg', 'png', 'webp'):
mime_map = {'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'png': 'image/png', 'webp': 'image/webp'}
if ext not in mime_map:
return JsonResponse({'error': 'only jpg, png, or webp allowed'}, status=400)
if f.size > settings.BG_MAX_BYTES:
return JsonResponse({'error': 'file too large (max 5 MB)'}, status=400)
profile = request.user.profile
# Delete old file from disk before replacing
if profile.background_image:
old_path = profile.background_image.path
if os.path.exists(old_path):
os.remove(old_path)
data = base64.b64encode(f.read()).decode('ascii')
data_url = f"data:{mime_map[ext]};base64,{data}"
profile.background_image = f
profile.save(update_fields=['background_image'])
return JsonResponse({'ok': True, 'url': profile.background_image.url})
profile = request.user.profile
profile.background_image_data = data_url
profile.save(update_fields=['background_image_data'])
return JsonResponse({'ok': True, 'url': data_url})
@login_required
@require_http_methods(['POST'])
def delete_background(request):
profile = request.user.profile
if profile.background_image:
path = profile.background_image.path
if os.path.exists(path):
os.remove(path)
profile.background_image = None
profile.save(update_fields=['background_image'])
return JsonResponse({'ok': True})
profile.background_image_data = ''
profile.save(update_fields=['background_image_data'])
return redirect('settings')
@login_required

View file

@ -59,7 +59,7 @@ WSGI_APPLICATION = 'diora.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
'NAME': BASE_DIR / 'data' / 'db.sqlite3',
}
}

View file

@ -42,9 +42,9 @@
<!-- Background section -->
<section class="settings-section">
<h2>Background</h2>
{% if request.user.profile.background_image %}
{% if request.user.profile.background_image_data %}
<p>
<img src="{{ request.user.profile.background_image.url }}" class="bg-preview" alt="Your background">
<img src="{{ request.user.profile.background_image_data }}" class="bg-preview" alt="Your background">
</p>
<form method="post" action="{% url 'delete_background' %}" class="inline-form">
{% csrf_token %}

View file

@ -12,10 +12,10 @@
<link rel="apple-touch-icon" href="/static/icon-192.png">
<link rel="stylesheet" href="/static/css/app.css">
<title>{% block title %}diora{% endblock %}</title>
{% if user.is_authenticated and user.profile.background_image %}
{% if user.is_authenticated and user.profile.background_image_data %}
<style>
body {
background-image: url('{{ user.profile.background_image.url }}');
background-image: url('{{ user.profile.background_image_data }}');
background-size: cover;
background-position: center;
background-attachment: fixed;
@ -23,7 +23,7 @@
</style>
{% endif %}
</head>
<body{% if user.is_authenticated and user.profile.background_image %} data-bg="{{ user.profile.background_image.url }}"{% endif %}>
<body{% if user.is_authenticated and user.profile.background_image_data %} data-bg="1"{% endif %}>
<nav class="navbar">
<a href="/" class="navbar-brand">diora</a>
<div class="navbar-links">