From 496d9e709c61239238284a85138d22c7d1ee4bad Mon Sep 17 00:00:00 2001 From: Guillaume Pellerin Date: Sun, 3 May 2026 17:34:48 +0200 Subject: [PATCH] add S3 storage options for Media file --- app/local_settings.py.sample | 4 ++ app/settings.py | 52 ++++++++++++++---- teleforma/admin.py | 10 ++-- teleforma/exam/models.py | 15 +++--- ..._s3_alter_documentprivate_file_and_more.py | 30 +++++++++++ teleforma/models/core.py | 29 +++++----- teleforma/views/core.py | 53 ++++++++++++------- 7 files changed, 139 insertions(+), 54 deletions(-) create mode 100644 teleforma/migrations/0038_media_file_s3_alter_documentprivate_file_and_more.py diff --git a/app/local_settings.py.sample b/app/local_settings.py.sample index 1ecfb00c..0e6d0696 100644 --- a/app/local_settings.py.sample +++ b/app/local_settings.py.sample @@ -68,3 +68,7 @@ DEFAULT_TO_EMAIL = 'recipient@recipient.org' # default recipient, for your tests EMAIL_SUBJECT_PREFIX = "[PREFIX]" # prefix title in email SITE_TITLE = 'Your Site' SITE_TAGLINE = 'This is a Mezzo site' + +AWS_ACCESS_KEY_ID="" +AWS_SECRET_ACCESS_KEY="" +AWS_S3_ENCRYPTION=True \ No newline at end of file diff --git a/app/settings.py b/app/settings.py index da5bf07a..444b9595 100644 --- a/app/settings.py +++ b/app/settings.py @@ -158,9 +158,9 @@ USE_L10N = True USE_TZ = False -######################## +################# # MEDIA / STATIC -######################## +################# # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" @@ -172,8 +172,6 @@ MEDIA_ROOT = '/srv/media/' #MEDIA_URL = 'http://pre-barreau.com/archives/' MEDIA_URL = '/media/' -DEFAULT_FILE_STORAGE='django.core.files.storage.FileSystemStorage' - FILE_UPLOAD_TEMP_DIR = '/tmp' # Absolute path to the directory static files should be collected to. @@ -438,6 +436,7 @@ JAZZMIN_SETTINGS = { "site_title": "CRFPA", "site_header": "CRFPA", "site_logo": "teleforma/images/logo_pb.png", + "site_brand": "Admin CRFPA", # # Links to put along the top menu # "topmenu_links": [ @@ -521,7 +520,7 @@ JAZZMIN_UI_TWEAKS = { "sidebar_nav_legacy_style": False, "sidebar_nav_flat_style": False, "theme": "default", - "dark_mode_theme": "darkly", + "default_theme_mode": "auto", "button_classes": { "primary": "btn-outline-primary", "secondary": "btn-outline-secondary", @@ -608,6 +607,42 @@ AWS_S3_FILE_OVERWRITE=False AWS_STORAGE_BUCKET_NAME="" MEDIA_S3_BUCKET = '/media/bucket/' + +######################## +# STORAGES +######################## + +DEFAULT_FILE_STORAGE='django.core.files.storage.FileSystemStorage' + +STORAGES = { + "default": { + "BACKEND": "django.core.files.storage.FileSystemStorage", + "OPTIONS": { + }, + }, + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", + }, + "private_documents_s3": { + "BACKEND": "storages.backends.s3.S3Storage", + "OPTIONS": { + "bucket_name": "crfpa-private", + "region_name": "gra", + "endpoint_url": "https://s3.gra.io.cloud.ovh.net/", + }, + }, + "media_s3": { + "BACKEND": "storages.backends.s3.S3Storage", + "OPTIONS": { + "bucket_name": "pb-crfpa-medias", + "region_name": "eu-west-par", + "endpoint_url": "https://s3.eu-west-par.io.cloud.ovh.net/", + }, + }, +} + + + ######################## # TELEFORMA ######################## @@ -632,14 +667,12 @@ TELEFORMA_EXAM_SCRIPT_SERVICE_URL = '/webviewer/teleforma.html' TELEFORMA_EXAM_USE_S3 = True TELEFORMA_DOCUMENTS_USE_S3 = False - TELEFORMA_PRIVATE_DOCUMENTS_MODE = True #wether the downloaded docs should be signed TELEFORMA_PRIVATE_DOCUMENTS_USE_S3 = True -AWS_STORAGE_BUCKET_NAME_PRIVATE_DOCUMENT="crfpa-private" -TELEFORMA_MEDIA_USE_S3 = False TELEFORMA_MEDIA_DOWNLOAD = False -AWS_STORAGE_BUCKET_NAME_MEDIA="crfpa-media" +TELEFORMA_MEDIA_USE_S3 = True + ORAL_OPTION_PRICE = 250 FASICLE_OPTION_PRICE = 110 @@ -706,6 +739,7 @@ CORS_ALLOWED_ORIGINS = [ CORS_ALLOW_ALL_ORIGINS = False + ################## # LOCAL SETTINGS # ################## diff --git a/teleforma/admin.py b/teleforma/admin.py index 742bd938..557cc725 100644 --- a/teleforma/admin.py +++ b/teleforma/admin.py @@ -382,11 +382,11 @@ def duplicate_medias(modeladmin, request, queryset): class MediaAdmin(admin.ModelAdmin): - def get_form(self, request, obj=None, **kwargs): - form = super(MediaAdmin, self).get_form(request, obj, **kwargs) - period = get_periods(request) - form.base_fields['conference'].queryset = Conference.objects.filter(period=period) - return form + # def get_form(self, request, obj=None, **kwargs): + # form = super(MediaAdmin, self).get_form(request, obj, **kwargs) + # period = get_periods(request) + # form.base_fields['conference'].queryset = Conference.objects.filter(period=period) + # return form list_per_page = 30 exclude = ['readers'] diff --git a/teleforma/exam/models.py b/teleforma/exam/models.py index fe091812..6eb56e7a 100755 --- a/teleforma/exam/models.py +++ b/teleforma/exam/models.py @@ -46,7 +46,7 @@ import uuid from django.conf import settings from django.contrib.auth.models import User from django.contrib.sites.models import Site -from django.core.files.storage import FileSystemStorage +from django.core.files.storage import FileSystemStorage, storages from django.db import models from django.db.models.signals import post_save from django.template.loader import render_to_string @@ -90,10 +90,13 @@ REJECT_REASON = (('unreadable', _('unreadable')), cache_path = settings.MEDIA_ROOT + 'cache/' script_path = settings.MEDIA_ROOT + 'scripts/' -if settings.TELEFORMA_EXAM_USE_S3: - storage = S3Boto3Storage -else: - storage = FileSystemStorage + +def script_storage(): + if settings.TELEFORMA_EXAM_USE_S3: + return storages["private_documents_s3"] + else: + return storages["default"] + def sha1sum_file(filename): @@ -266,7 +269,7 @@ class Script(BaseResource): upload_to='scripts/%Y/%m/%d', max_length=1024, blank=True, - storage=storage + storage=script_storage ) box_uuid = models.CharField(_('Box UUID'), max_length=256, blank=True) score = models.FloatField(_('score'), blank=True, diff --git a/teleforma/migrations/0038_media_file_s3_alter_documentprivate_file_and_more.py b/teleforma/migrations/0038_media_file_s3_alter_documentprivate_file_and_more.py new file mode 100644 index 00000000..979abb0d --- /dev/null +++ b/teleforma/migrations/0038_media_file_s3_alter_documentprivate_file_and_more.py @@ -0,0 +1,30 @@ +# Generated by Django 5.2.13 on 2026-05-02 15:04 + +import django.core.files.storage +import teleforma.models.core +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('teleforma', '0037_merge_20260114_1638'), + ] + + operations = [ + migrations.AddField( + model_name='media', + name='file_s3', + field=models.FileField(blank=True, max_length=1024, null=True, storage=teleforma.models.core.media_storage, upload_to='items/%Y/%m/%d', verbose_name='S3 file'), + ), + migrations.AlterField( + model_name='documentprivate', + name='file', + field=models.FileField(blank=True, db_column='filename', max_length=1024, storage=teleforma.models.core.private_document_storage, upload_to='private/documents/%Y/%m/%d', verbose_name='file'), + ), + migrations.AlterField( + model_name='media', + name='file', + field=models.FileField(blank=True, max_length=1024, null=True, storage=django.core.files.storage.FileSystemStorage(), upload_to='items/%Y/%m/%d', verbose_name='file'), + ), + ] diff --git a/teleforma/models/core.py b/teleforma/models/core.py index 0b49463a..5917f184 100644 --- a/teleforma/models/core.py +++ b/teleforma/models/core.py @@ -49,7 +49,7 @@ import django.db.models as models from django.conf import settings from django.contrib.auth.models import User from django.core.files import File -from django.core.files.storage import FileSystemStorage +from django.core.files.storage import FileSystemStorage, storages from django.core.paginator import InvalidPage from django.db import models from django.forms.fields import FileField @@ -115,19 +115,18 @@ STATUS_CHOICES = ( WEIGHT_CHOICES = get_nint_choices(5) -if settings.TELEFORMA_MEDIA_USE_S3: - media_s3_storage = S3Boto3Storage( - bucket=settings.AWS_STORAGE_BUCKET_NAME_MEDIA) - media_local_storage = FileSystemStorage -else: - media_s3_storage = FileSystemStorage - media_local_storage = FileSystemStorage +def private_document_storage(): + if settings.TELEFORMA_PRIVATE_DOCUMENTS_USE_S3: + return storages["private_documents_s3"] + else: + return storages["default"] + -if settings.TELEFORMA_PRIVATE_DOCUMENTS_USE_S3: - private_document_storage = S3Boto3Storage( - bucket=settings.AWS_STORAGE_BUCKET_NAME_PRIVATE_DOCUMENT) -else: - private_document_storage = FileSystemStorage +def media_storage(): + if settings.TELEFORMA_MEDIA_USE_S3: + return storages["media_s3"] + else: + return storages["default"] def get_random_hash(): @@ -1050,7 +1049,7 @@ class Media(MediaBase): max_length=1024, null=True, blank=True, - storage=media_local_storage + storage=storages["default"] ) file_s3 = models.FileField( _('S3 file'), @@ -1058,7 +1057,7 @@ class Media(MediaBase): max_length=1024, null=True, blank=True, - storage=media_s3_storage + storage=media_storage, ) poster_file = models.FileField( _('poster file'), upload_to='items/%Y/%m/%d', max_length=255, null=True, blank=False) diff --git a/teleforma/views/core.py b/teleforma/views/core.py index f1c1983a..19069e06 100644 --- a/teleforma/views/core.py +++ b/teleforma/views/core.py @@ -280,19 +280,22 @@ def serve_media(file, content_type="", buffering=True, streaming=False, bucket=F if not settings.DEBUG: if bucket: - url = settings.MEDIA_S3_BUCKET + file.url.split(settings.AWS_S3_ENDPOINT_URL)[-1] + # url = settings.MEDIA_S3_BUCKET + file.url.split(settings.AWS_S3_ENDPOINT_URL)[-1] + return HttpResponseRedirect(file.url) else: - url = file.url - return nginx_media_accel(url, content_type=content_type, + return nginx_media_accel(file.url, content_type=content_type, buffering=buffering, streaming=streaming) else: - response = StreamingHttpResponse( - stream_from_file(file.path), content_type=content_type) - filename = os.path.basename(file.path) - if not streaming: - response['Content-Disposition'] = 'attachment; ' + \ - 'filename=' + filename - return response + if bucket: + return HttpResponseRedirect(file.url) + else: + response = StreamingHttpResponse( + stream_from_file(file.path), content_type=content_type) + filename = os.path.basename(file.path) + if not streaming: + response['Content-Disposition'] = 'attachment; ' + \ + 'filename=' + filename + return response def nginx_media_accel(url, content_type="", buffering=True, streaming=False): @@ -652,17 +655,29 @@ class MediaView(CourseAccessMixin, DetailView): media = self.get_object() if not media.mime_type: media.set_mime_type() + + if media.course: + course = media.course + else: + course = media.conference.course + if media.course_type: + course_type = media.course_type + else: + course_type = media.conference.course_type + if media.course: + room_name = media.course.code + else: + room_name = media.conference.course.code + if media.conference.web_class_group: + room_name += '_' + media.conference.public_id + context['mime_type'] = media.mime_type - context['course'] = media.course - context['type'] = media.course_type + context['course'] = course + context['type'] = course_type # context['notes'] = media.notes.all().filter(author=self.request.user) content_type = ContentType.objects.get( app_label="teleforma", model="course") - room_name = media.course.code - if media.conference.web_class_group: - room_name += '_' + media.conference.public_id - access = get_access(media, context['all_courses']) if not access: context['access_error'] = access_error @@ -704,15 +719,15 @@ class MediaView(CourseAccessMixin, DetailView): media_detail_url = request.build_absolute_uri( reverse("teleforma-media-detail", kwargs={"period_id": period_id, "pk": media.id})) - if get_access(media, courses) and referer == media_detail_url: + if (get_access(media, courses) and referer == media_detail_url) or settings.DEBUG: media_reads = MediaRead.objects.filter(media=media, user=request.user) if not media_reads: media_read = MediaRead(media=media, user=request.user) media_read.save() - return serve_media(media.file, + return serve_media(media.get_file(), content_type=media.mime_type, streaming=streaming, - bucket=False, + bucket=settings.TELEFORMA_MEDIA_USE_S3, ) else: raise Http404("You don't have access to this media.") -- 2.47.3