mirror of
https://github.com/ae-utbm/sith.git
synced 2025-04-16 02:50:22 +00:00
Serve upload files directly from nginx
This commit is contained in:
parent
91b30e7550
commit
67bc49fb21
@ -102,6 +102,7 @@ class OperationLogAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(QuickUploadImage)
|
@admin.register(QuickUploadImage)
|
||||||
class QuickUploadImageAdmin(admin.ModelAdmin):
|
class QuickUploadImageAdmin(admin.ModelAdmin):
|
||||||
list_display = ("uuid", "name", "uploader", "content_type", "date")
|
list_display = ("uuid", "uploader", "created_at", "name")
|
||||||
search_fields = ("uuid", "name", "uploader")
|
search_fields = ("uuid", "uploader", "name")
|
||||||
autocomplete_fields = ("uploader",)
|
autocomplete_fields = ("uploader",)
|
||||||
|
readonly_fields = ("width", "height", "size")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 4.2.20 on 2025-04-05 16:28
|
# Generated by Django 4.2.20 on 2025-04-06 09:55
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -15,13 +15,24 @@ class Migration(migrations.Migration):
|
|||||||
name="QuickUploadImage",
|
name="QuickUploadImage",
|
||||||
fields=[
|
fields=[
|
||||||
(
|
(
|
||||||
"uuid",
|
"id",
|
||||||
models.CharField(max_length=36, primary_key=True, serialize=False),
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
("uuid", models.UUIDField(db_index=True, unique=True)),
|
||||||
("name", models.CharField(max_length=100)),
|
("name", models.CharField(max_length=100)),
|
||||||
("image", models.ImageField(upload_to="upload")),
|
("image", models.ImageField(upload_to="upload/%Y/%m/%d")),
|
||||||
("content_type", models.CharField(max_length=50)),
|
(
|
||||||
("date", models.DateTimeField(auto_now=True, verbose_name="date")),
|
"created_at",
|
||||||
|
models.DateTimeField(auto_now_add=True, verbose_name="created at"),
|
||||||
|
),
|
||||||
|
("width", models.PositiveIntegerField(verbose_name="width")),
|
||||||
|
("height", models.PositiveIntegerField(verbose_name="height")),
|
||||||
|
("size", models.PositiveIntegerField(verbose_name="size")),
|
||||||
(
|
(
|
||||||
"uploader",
|
"uploader",
|
||||||
models.ForeignKey(
|
models.ForeignKey(
|
||||||
|
@ -1110,12 +1110,10 @@ class QuickUploadImage(models.Model):
|
|||||||
"""Images uploaded by user outside of the SithFile mechanism"""
|
"""Images uploaded by user outside of the SithFile mechanism"""
|
||||||
|
|
||||||
IMAGE_NAME_SIZE = 100
|
IMAGE_NAME_SIZE = 100
|
||||||
UUID_4_SIZE = 36
|
|
||||||
|
|
||||||
uuid = models.CharField(max_length=UUID_4_SIZE, blank=False, primary_key=True)
|
uuid = models.UUIDField(unique=True, db_index=True)
|
||||||
name = models.CharField(max_length=IMAGE_NAME_SIZE, blank=False)
|
name = models.CharField(max_length=IMAGE_NAME_SIZE, blank=False)
|
||||||
image = models.ImageField(upload_to="upload")
|
image = models.ImageField(upload_to="upload/%Y/%m/%d")
|
||||||
content_type = models.CharField(max_length=50, blank=False)
|
|
||||||
uploader = models.ForeignKey(
|
uploader = models.ForeignKey(
|
||||||
"User",
|
"User",
|
||||||
related_name="quick_uploads",
|
related_name="quick_uploads",
|
||||||
@ -1123,13 +1121,16 @@ class QuickUploadImage(models.Model):
|
|||||||
blank=True,
|
blank=True,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
)
|
)
|
||||||
date = models.DateTimeField(_("date"), auto_now=True)
|
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
|
||||||
|
width = models.PositiveIntegerField(_("width"))
|
||||||
|
height = models.PositiveIntegerField(_("height"))
|
||||||
|
size = models.PositiveIntegerField(_("size"))
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.name}{Path(self.image.path).suffix}"
|
return str(self.image.path)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("core:uploaded_image", kwargs={"image_uuid": self.uuid})
|
return self.image.url
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_uploaded(
|
def create_from_uploaded(
|
||||||
@ -1145,13 +1146,16 @@ class QuickUploadImage(models.Model):
|
|||||||
identifier = str(uuid4())
|
identifier = str(uuid4())
|
||||||
name = Path(image.name).stem[: cls.IMAGE_NAME_SIZE - 1]
|
name = Path(image.name).stem[: cls.IMAGE_NAME_SIZE - 1]
|
||||||
file = File(convert_image(image), name=f"{identifier}.webp")
|
file = File(convert_image(image), name=f"{identifier}.webp")
|
||||||
|
image = Image.open(file)
|
||||||
|
|
||||||
return cls.objects.create(
|
return cls.objects.create(
|
||||||
uuid=identifier,
|
uuid=identifier,
|
||||||
name=name,
|
name=name,
|
||||||
image=file,
|
image=file,
|
||||||
content_type="image/webp",
|
|
||||||
uploader=uploader,
|
uploader=uploader,
|
||||||
|
width=image.width,
|
||||||
|
height=image.height,
|
||||||
|
size=file.size,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,25 +63,10 @@ class UserProfileSchema(ModelSchema):
|
|||||||
class UploadedFileSchema(ModelSchema):
|
class UploadedFileSchema(ModelSchema):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = QuickUploadImage
|
model = QuickUploadImage
|
||||||
fields = ["uuid", "name", "content_type"]
|
fields = ["uuid", "name", "width", "height", "size"]
|
||||||
|
|
||||||
width: int
|
|
||||||
height: int
|
|
||||||
size: int
|
|
||||||
href: str
|
href: str
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def resolve_width(obj: QuickUploadImage):
|
|
||||||
return obj.image.width
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def resolve_height(obj: QuickUploadImage):
|
|
||||||
return obj.image.height
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def resolve_size(obj: QuickUploadImage):
|
|
||||||
return obj.image.size
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_href(obj: QuickUploadImage) -> str:
|
def resolve_href(obj: QuickUploadImage) -> str:
|
||||||
return obj.get_absolute_url()
|
return obj.get_absolute_url()
|
||||||
|
@ -26,13 +26,14 @@ const loadEasyMde = (textarea: HTMLTextAreaElement) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onSuccess(response.data.href);
|
onSuccess(response.data.href);
|
||||||
// Workaround function to add ! and image name to uploaded image
|
// Workaround function to add an image name to uploaded image
|
||||||
// Without this, you get [](url) instead of 
|
// Without this, you get  instead of 
|
||||||
let cursor = easymde.codemirror.getCursor();
|
let cursor = easymde.codemirror.getCursor();
|
||||||
easymde.codemirror.setSelection({ line: cursor.line, ch: cursor.ch - 1 });
|
|
||||||
easymde.codemirror.replaceSelection("!");
|
|
||||||
|
|
||||||
easymde.codemirror.setSelection({ line: cursor.line, ch: cursor.ch + 1 });
|
easymde.codemirror.setSelection({
|
||||||
|
line: cursor.line,
|
||||||
|
ch: cursor.ch - response.data.href.length - 3,
|
||||||
|
});
|
||||||
easymde.codemirror.replaceSelection(response.data.name);
|
easymde.codemirror.replaceSelection(response.data.name);
|
||||||
|
|
||||||
// Move cursor at the end of the url and add a new line
|
// Move cursor at the end of the url and add a new line
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from itertools import cycle
|
from itertools import cycle
|
||||||
|
from pathlib import Path
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@ -291,6 +292,15 @@ def test_apply_rights_recursively():
|
|||||||
),
|
),
|
||||||
200,
|
200,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
lambda: old_subscriber_user.make(),
|
||||||
|
SimpleUploadedFile(
|
||||||
|
"ttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestesttesttesttesttesttesttesttesttesttesttesttest.jpg",
|
||||||
|
content=RED_PIXEL_PNG,
|
||||||
|
content_type="image/jpg",
|
||||||
|
),
|
||||||
|
200,
|
||||||
|
), # very long file name
|
||||||
(
|
(
|
||||||
lambda: old_subscriber_user.make(),
|
lambda: old_subscriber_user.make(),
|
||||||
SimpleUploadedFile(
|
SimpleUploadedFile(
|
||||||
@ -329,4 +339,8 @@ def test_quick_upload_image(
|
|||||||
if expected_status != 200:
|
if expected_status != 200:
|
||||||
return
|
return
|
||||||
|
|
||||||
assert QuickUploadImage.objects.filter(pk=resp.json()["uuid"]).exists()
|
parsed = resp.json()
|
||||||
|
assert QuickUploadImage.objects.filter(uuid=parsed["uuid"]).exists()
|
||||||
|
assert (
|
||||||
|
parsed["name"] == Path(file.name).stem[: QuickUploadImage.IMAGE_NAME_SIZE - 1]
|
||||||
|
)
|
||||||
|
11
core/urls.py
11
core/urls.py
@ -30,7 +30,6 @@ from core.converters import (
|
|||||||
FourDigitYearConverter,
|
FourDigitYearConverter,
|
||||||
TwoDigitMonthConverter,
|
TwoDigitMonthConverter,
|
||||||
)
|
)
|
||||||
from core.models import QuickUploadImage
|
|
||||||
from core.views import (
|
from core.views import (
|
||||||
FileDeleteView,
|
FileDeleteView,
|
||||||
FileEditPropView,
|
FileEditPropView,
|
||||||
@ -214,16 +213,6 @@ urlpatterns = [
|
|||||||
"file/<int:file_id>/moderate/", FileModerateView.as_view(), name="file_moderate"
|
"file/<int:file_id>/moderate/", FileModerateView.as_view(), name="file_moderate"
|
||||||
),
|
),
|
||||||
path("file/<int:file_id>/download/", send_file, name="download"),
|
path("file/<int:file_id>/download/", send_file, name="download"),
|
||||||
path(
|
|
||||||
"file/<str:image_uuid>/uploads/",
|
|
||||||
lambda request, image_uuid: send_file(
|
|
||||||
request=request,
|
|
||||||
file_id=image_uuid,
|
|
||||||
file_class=QuickUploadImage,
|
|
||||||
file_attr="image",
|
|
||||||
),
|
|
||||||
name="uploaded_image",
|
|
||||||
),
|
|
||||||
# Page views
|
# Page views
|
||||||
path("page/", PageListView.as_view(), name="page_list"),
|
path("page/", PageListView.as_view(), name="page_list"),
|
||||||
path("page/create/", PageCreateView.as_view(), name="page_new"),
|
path("page/create/", PageCreateView.as_view(), name="page_new"),
|
||||||
|
@ -39,7 +39,7 @@ from core.auth.mixins import (
|
|||||||
CanViewMixin,
|
CanViewMixin,
|
||||||
can_view,
|
can_view,
|
||||||
)
|
)
|
||||||
from core.models import Notification, QuickUploadImage, SithFile, User
|
from core.models import Notification, SithFile, User
|
||||||
from core.views.mixins import AllowFragment
|
from core.views.mixins import AllowFragment
|
||||||
from core.views.widgets.ajax_select import (
|
from core.views.widgets.ajax_select import (
|
||||||
AutoCompleteSelectMultipleGroup,
|
AutoCompleteSelectMultipleGroup,
|
||||||
@ -87,7 +87,7 @@ def send_raw_file(path: Path) -> HttpResponse:
|
|||||||
def send_file(
|
def send_file(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
file_id: int | str,
|
file_id: int | str,
|
||||||
file_class: type[SithFile | QuickUploadImage] = SithFile,
|
file_class: type[SithFile] = SithFile,
|
||||||
file_attr: str = "file",
|
file_attr: str = "file",
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
"""Send a protected file, if the user can see it.
|
"""Send a protected file, if the user can see it.
|
||||||
|
@ -224,7 +224,7 @@ server {
|
|||||||
location /static/;
|
location /static/;
|
||||||
root /repertoire/du/projet;
|
root /repertoire/du/projet;
|
||||||
}
|
}
|
||||||
location ~ ^/data/(products|com|club_logos)/ {
|
location ~ ^/data/(products|com|club_logos|upload)/ {
|
||||||
root /repertoire/du/projet;
|
root /repertoire/du/projet;
|
||||||
}
|
}
|
||||||
location ~ ^/data/(SAS|profiles|users|.compressed|.thumbnails)/ {
|
location ~ ^/data/(SAS|profiles|users|.compressed|.thumbnails)/ {
|
||||||
|
@ -1572,6 +1572,14 @@ msgstr "Ceci n'est pas une miniature de dossier valide"
|
|||||||
msgid "You must provide a file"
|
msgid "You must provide a file"
|
||||||
msgstr "Vous devez fournir un fichier"
|
msgstr "Vous devez fournir un fichier"
|
||||||
|
|
||||||
|
#: core/models.py
|
||||||
|
msgid "width"
|
||||||
|
msgstr "largeur"
|
||||||
|
|
||||||
|
#: core/models.py
|
||||||
|
msgid "height"
|
||||||
|
msgstr "hauteur"
|
||||||
|
|
||||||
#: core/models.py
|
#: core/models.py
|
||||||
msgid "page unix name"
|
msgid "page unix name"
|
||||||
msgstr "nom unix de la page"
|
msgstr "nom unix de la page"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user