Store background images in DB, persist SQLite via volume
This commit is contained in:
parent
5c8c57f04f
commit
978b6fa24b
7 changed files with 22 additions and 36 deletions
|
|
@ -11,7 +11,7 @@ RUN pip install --no-cache-dir -r requirements.txt gunicorn
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN python manage.py collectstatic --noinput
|
RUN python manage.py collectstatic --noinput && mkdir -p /app/data
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -13,7 +10,7 @@ class Migration(migrations.Migration):
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='userprofile',
|
model_name='userprofile',
|
||||||
name='background_image',
|
name='background_image_data',
|
||||||
field=models.FileField(blank=True, null=True, upload_to=accounts.models._bg_upload_path),
|
field=models.TextField(blank=True),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,12 @@ from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
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):
|
class UserProfile(models.Model):
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
|
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
|
||||||
lastfm_session_key = models.CharField(max_length=100, blank=True)
|
lastfm_session_key = models.CharField(max_length=100, blank=True)
|
||||||
lastfm_username = models.CharField(max_length=100, blank=True)
|
lastfm_username = models.CharField(max_length=100, blank=True)
|
||||||
lastfm_scrobble = models.BooleanField(default=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:
|
def has_lastfm(self) -> bool:
|
||||||
return bool(self.lastfm_session_key)
|
return bool(self.lastfm_session_key)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import os
|
import base64
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import authenticate, login, get_user_model
|
from django.contrib.auth import authenticate, login, get_user_model
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
|
@ -123,35 +123,29 @@ def upload_background(request):
|
||||||
return JsonResponse({'error': 'no file'}, status=400)
|
return JsonResponse({'error': 'no file'}, status=400)
|
||||||
|
|
||||||
ext = f.name.rsplit('.', 1)[-1].lower() if '.' in f.name else ''
|
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)
|
return JsonResponse({'error': 'only jpg, png, or webp allowed'}, status=400)
|
||||||
|
|
||||||
if f.size > settings.BG_MAX_BYTES:
|
if f.size > settings.BG_MAX_BYTES:
|
||||||
return JsonResponse({'error': 'file too large (max 5 MB)'}, status=400)
|
return JsonResponse({'error': 'file too large (max 5 MB)'}, status=400)
|
||||||
|
|
||||||
profile = request.user.profile
|
data = base64.b64encode(f.read()).decode('ascii')
|
||||||
# Delete old file from disk before replacing
|
data_url = f"data:{mime_map[ext]};base64,{data}"
|
||||||
if profile.background_image:
|
|
||||||
old_path = profile.background_image.path
|
|
||||||
if os.path.exists(old_path):
|
|
||||||
os.remove(old_path)
|
|
||||||
|
|
||||||
profile.background_image = f
|
profile = request.user.profile
|
||||||
profile.save(update_fields=['background_image'])
|
profile.background_image_data = data_url
|
||||||
return JsonResponse({'ok': True, 'url': profile.background_image.url})
|
profile.save(update_fields=['background_image_data'])
|
||||||
|
return JsonResponse({'ok': True, 'url': data_url})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@require_http_methods(['POST'])
|
@require_http_methods(['POST'])
|
||||||
def delete_background(request):
|
def delete_background(request):
|
||||||
profile = request.user.profile
|
profile = request.user.profile
|
||||||
if profile.background_image:
|
profile.background_image_data = ''
|
||||||
path = profile.background_image.path
|
profile.save(update_fields=['background_image_data'])
|
||||||
if os.path.exists(path):
|
return redirect('settings')
|
||||||
os.remove(path)
|
|
||||||
profile.background_image = None
|
|
||||||
profile.save(update_fields=['background_image'])
|
|
||||||
return JsonResponse({'ok': True})
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ WSGI_APPLICATION = 'diora.wsgi.application'
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': BASE_DIR / 'db.sqlite3',
|
'NAME': BASE_DIR / 'data' / 'db.sqlite3',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@
|
||||||
<!-- Background section -->
|
<!-- Background section -->
|
||||||
<section class="settings-section">
|
<section class="settings-section">
|
||||||
<h2>Background</h2>
|
<h2>Background</h2>
|
||||||
{% if request.user.profile.background_image %}
|
{% if request.user.profile.background_image_data %}
|
||||||
<p>
|
<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>
|
</p>
|
||||||
<form method="post" action="{% url 'delete_background' %}" class="inline-form">
|
<form method="post" action="{% url 'delete_background' %}" class="inline-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@
|
||||||
<link rel="apple-touch-icon" href="/static/icon-192.png">
|
<link rel="apple-touch-icon" href="/static/icon-192.png">
|
||||||
<link rel="stylesheet" href="/static/css/app.css">
|
<link rel="stylesheet" href="/static/css/app.css">
|
||||||
<title>{% block title %}diora{% endblock %}</title>
|
<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>
|
<style>
|
||||||
body {
|
body {
|
||||||
background-image: url('{{ user.profile.background_image.url }}');
|
background-image: url('{{ user.profile.background_image_data }}');
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
</style>
|
</style>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</head>
|
</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">
|
<nav class="navbar">
|
||||||
<a href="/" class="navbar-brand">diora</a>
|
<a href="/" class="navbar-brand">diora</a>
|
||||||
<div class="navbar-links">
|
<div class="navbar-links">
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue