From 50182091b9d3a877b990d2f8024afc3e5df27091 Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:24:58 +0300 Subject: [PATCH 01/14] Add health check on rabbitmq, switch dev port to 8081 --- dockerize/Makefile | 2 +- dockerize/docker-compose.yml | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/dockerize/Makefile b/dockerize/Makefile index c58599c3..745c12ce 100644 --- a/dockerize/Makefile +++ b/dockerize/Makefile @@ -236,7 +236,7 @@ devweb-runserver: devweb @echo "------------------------------------------------------------------" @echo "Running in DEVELOPMENT mode" @echo "------------------------------------------------------------------" - @docker compose -p $(PROJECT_ID) exec devweb python manage.py runserver 0.0.0.0:8080 + @docker compose -p $(PROJECT_ID) exec devweb python manage.py runserver 0.0.0.0:8081 dbseed: @echo diff --git a/dockerize/docker-compose.yml b/dockerize/docker-compose.yml index ec112781..3181b697 100644 --- a/dockerize/docker-compose.yml +++ b/dockerize/docker-compose.yml @@ -84,7 +84,7 @@ services: - ${QGISPLUGINS_MEDIA_VOLUME}:/home/web/media:rw ports: # for django test server - - "62202:8080" + - "62202:8081" # for ssh - "62203:22" networks: @@ -98,6 +98,11 @@ services: restart: unless-stopped networks: internal: + healthcheck: + test: ["CMD", "rabbitmqctl", "status"] + interval: 5s + timeout: 3s + retries: 5 beat: <<: *uwsgi-common @@ -112,9 +117,12 @@ services: <<: *uwsgi-common container_name: qgis-plugins-worker depends_on: - - db - - rabbitmq - - beat + db: + condition: service_started + rabbitmq: + condition: service_healthy + beat: + condition: service_started working_dir: /home/web/django_project entrypoint: [] command: celery -A plugins worker -l INFO From 7756fd997ace8ccd392b8bd6d0a0c4ac935e713e Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Wed, 19 Jun 2024 13:40:32 +0300 Subject: [PATCH 02/14] Handle thumbnail error in resources API --- qgis-app/api/serializers.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/qgis-app/api/serializers.py b/qgis-app/api/serializers.py index fe6d4a5a..c344d3ae 100644 --- a/qgis-app/api/serializers.py +++ b/qgis-app/api/serializers.py @@ -14,10 +14,13 @@ class ResourceBaseSerializer(serializers.ModelSerializer): resource_subtype = serializers.SerializerMethodField() thumbnail_full = serializers.ImageField(source="thumbnail_image") - # A thumbnail image, sorl options and read-only - thumbnail = HyperlinkedSorlImageField( - "128x128", options={"crop": "center"}, source="thumbnail_image", read_only=True - ) + try: + # A thumbnail image, sorl options and read-only + thumbnail = HyperlinkedSorlImageField( + "128x128", options={"crop": "center"}, source="thumbnail_image", read_only=True + ) + except FileNotFoundError: + thumbnail = serializers.ImageField(source="thumbnail_image") class Meta: fields = [ From 0cf31368ae5462ce5ac0a6183ad63a85f5556e67 Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Wed, 19 Jun 2024 14:23:37 +0300 Subject: [PATCH 03/14] Increase rabbit healthcheck interval and timeout --- dockerize/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dockerize/docker-compose.yml b/dockerize/docker-compose.yml index 3181b697..10619ed8 100644 --- a/dockerize/docker-compose.yml +++ b/dockerize/docker-compose.yml @@ -100,8 +100,8 @@ services: internal: healthcheck: test: ["CMD", "rabbitmqctl", "status"] - interval: 5s - timeout: 3s + interval: 10s + timeout: 5s retries: 5 beat: From b0bb9a417106cfade9770cfcf09ea409ec87f1ee Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:03:17 +0300 Subject: [PATCH 04/14] Handle missing template error --- qgis-app/api/views.py | 20 ++++++++--------- qgis-app/base/tests/test_middleware.py | 31 ++++++++++++++++++++++++++ qgis-app/middleware.py | 15 +++++++++++++ qgis-app/settings.py | 2 ++ 4 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 qgis-app/base/tests/test_middleware.py diff --git a/qgis-app/api/views.py b/qgis-app/api/views.py index 37ad531d..f62375d2 100644 --- a/qgis-app/api/views.py +++ b/qgis-app/api/views.py @@ -105,28 +105,28 @@ class ResourceAPIList(FlatMultipleModelAPIView): querylist = [ { - "queryset": Geopackage.approved_objects.all(), - "serializer_class": GeopackageSerializer, + "queryset": LayerDefinition.approved_objects.all(), + "serializer_class": LayerDefinitionSerializer, "filter_fn": filter_general, }, { - "queryset": Model.approved_objects.all(), - "serializer_class": ModelSerializer, + "queryset": Wavefront.approved_objects.all(), + "serializer_class": WavefrontSerializer, "filter_fn": filter_general, }, { - "queryset": Style.approved_objects.all(), - "serializer_class": StyleSerializer, + "queryset": Geopackage.approved_objects.all(), + "serializer_class": GeopackageSerializer, "filter_fn": filter_general, }, { - "queryset": LayerDefinition.approved_objects.all(), - "serializer_class": LayerDefinitionSerializer, + "queryset": Model.approved_objects.all(), + "serializer_class": ModelSerializer, "filter_fn": filter_general, }, { - "queryset": Wavefront.approved_objects.all(), - "serializer_class": WavefrontSerializer, + "queryset": Style.approved_objects.all(), + "serializer_class": StyleSerializer, "filter_fn": filter_general, }, ] diff --git a/qgis-app/base/tests/test_middleware.py b/qgis-app/base/tests/test_middleware.py new file mode 100644 index 00000000..177a04a3 --- /dev/null +++ b/qgis-app/base/tests/test_middleware.py @@ -0,0 +1,31 @@ +from django.test import TestCase, RequestFactory +from django.template import TemplateDoesNotExist +from middleware import HandleTemplateDoesNotExistMiddleware +from django.urls import path +from django.urls import reverse + +class HandleTemplateDoesNotExistMiddlewareTest(TestCase): + fixtures = ["fixtures/simplemenu.json"] + def setUp(self): + # Mock get_response function + self.factory = RequestFactory() + self.get_response = lambda request: None + self.middleware = HandleTemplateDoesNotExistMiddleware(self.get_response) + + def test_template_does_not_exist(self): + url = '/planet/template' + response = self.client.get(url) + + self.assertEqual(response.status_code, 404) + self.assertTemplateUsed( + response, '404.html' + ) + + def test_no_template_error(self): + request = self.factory.get('/planet/template') + + # Simulate a different exception + response = self.middleware.process_exception(request, Exception("Some other error")) + + # Check that the middleware does not handle this + self.assertIsNone(response) diff --git a/qgis-app/middleware.py b/qgis-app/middleware.py index e523ac6e..76f05d46 100644 --- a/qgis-app/middleware.py +++ b/qgis-app/middleware.py @@ -1,4 +1,19 @@ # -*- coding:utf-8 -*- +# myapp/middleware.py + +from django.template import TemplateDoesNotExist +from django.shortcuts import render +from django.http import HttpResponse +from django.utils.deprecation import MiddlewareMixin + +class HandleTemplateDoesNotExistMiddleware(MiddlewareMixin): + """Handle missing templates""" + def process_exception(self, request, exception): + if isinstance(exception, TemplateDoesNotExist): + return render(request, '404.html', status=404) + return None + + """ QGIS-DJANGO - MIDDLEWARE diff --git a/qgis-app/settings.py b/qgis-app/settings.py index 16d3cb2d..2900d0b5 100644 --- a/qgis-app/settings.py +++ b/qgis-app/settings.py @@ -86,6 +86,8 @@ # Added by Tim for advanced loggin options "django.middleware.cache.FetchFromCacheMiddleware", "middleware.XForwardedForMiddleware", + # Handle missing template + "middleware.HandleTemplateDoesNotExistMiddleware", ] ROOT_URLCONF = "urls" From 03c2f0c343ae0a1ad926a4edc062bd131c144161 Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:25:16 +0300 Subject: [PATCH 05/14] Rebuild index frequently --- qgis-app/plugins/tasks/__init__.py | 1 + qgis-app/plugins/tasks/rebuild_search_index.py | 10 ++++++++++ qgis-app/settings_docker.py | 7 +++++++ 3 files changed, 18 insertions(+) create mode 100644 qgis-app/plugins/tasks/rebuild_search_index.py diff --git a/qgis-app/plugins/tasks/__init__.py b/qgis-app/plugins/tasks/__init__.py index 1ee99c38..4a4a1481 100644 --- a/qgis-app/plugins/tasks/__init__.py +++ b/qgis-app/plugins/tasks/__init__.py @@ -1,3 +1,4 @@ from plugins.tasks.generate_plugins_xml import * # noqa from plugins.tasks.update_feedjack import * # noqa from plugins.tasks.update_qgis_versions import * # noqa +from plugins.tasks.rebuild_search_index import * # noqa diff --git a/qgis-app/plugins/tasks/rebuild_search_index.py b/qgis-app/plugins/tasks/rebuild_search_index.py new file mode 100644 index 00000000..198596d2 --- /dev/null +++ b/qgis-app/plugins/tasks/rebuild_search_index.py @@ -0,0 +1,10 @@ +from celery import shared_task +from celery.utils.log import get_task_logger + +logger = get_task_logger(__name__) + + +@shared_task +def rebuild_index(): + import subprocess + subprocess.call(['python', 'manage.py', 'rebuild_index']) \ No newline at end of file diff --git a/qgis-app/settings_docker.py b/qgis-app/settings_docker.py index a7f58444..fa6fe91d 100644 --- a/qgis-app/settings_docker.py +++ b/qgis-app/settings_docker.py @@ -156,6 +156,13 @@ 'update_qgis_versions': { 'task': 'plugins.tasks.update_qgis_versions.update_qgis_versions', 'schedule': crontab(minute='*/30'), # Execute every 30 minutes. + }, + # Index synchronization sometimes fails when deleting + # a plugin and None is listed in the search list. So I think + # it would be better if we rebuild the index frequently + 'rebuild_index': { + 'task': 'plugins.tasks.rebuild_index.rebuild_index', + 'schedule': crontab(day='*/1'), # Execute every day. } } # Set plugin token access and refresh validity to a very long duration From 9df7b3cd8f138e634d5e4c598c04fb27480747f0 Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:09:18 +0300 Subject: [PATCH 06/14] Fix rebuild index schedule --- qgis-app/settings_docker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qgis-app/settings_docker.py b/qgis-app/settings_docker.py index fa6fe91d..f3c6d085 100644 --- a/qgis-app/settings_docker.py +++ b/qgis-app/settings_docker.py @@ -162,7 +162,7 @@ # it would be better if we rebuild the index frequently 'rebuild_index': { 'task': 'plugins.tasks.rebuild_index.rebuild_index', - 'schedule': crontab(day='*/1'), # Execute every day. + 'schedule': crontab(minute=0, hour=3), # Execute every day at 3 AM. } } # Set plugin token access and refresh validity to a very long duration From 651d5b97c45b6154b521184b7a7be1c9e1814b3d Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:52:13 +0300 Subject: [PATCH 07/14] Handle missing thumbnail in resource API --- qgis-app/api/serializers.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/qgis-app/api/serializers.py b/qgis-app/api/serializers.py index c344d3ae..1cc151f9 100644 --- a/qgis-app/api/serializers.py +++ b/qgis-app/api/serializers.py @@ -6,21 +6,17 @@ from styles.models import Style from layerdefinitions.models import LayerDefinition from wavefronts.models import Wavefront - +from sorl.thumbnail import get_thumbnail +from django.conf import settings +from os.path import exists +from django.templatetags.static import static class ResourceBaseSerializer(serializers.ModelSerializer): creator = serializers.ReadOnlyField(source="get_creator_name") resource_type = serializers.SerializerMethodField() resource_subtype = serializers.SerializerMethodField() thumbnail_full = serializers.ImageField(source="thumbnail_image") - - try: - # A thumbnail image, sorl options and read-only - thumbnail = HyperlinkedSorlImageField( - "128x128", options={"crop": "center"}, source="thumbnail_image", read_only=True - ) - except FileNotFoundError: - thumbnail = serializers.ImageField(source="thumbnail_image") + thumbnail = serializers.SerializerMethodField() class Meta: fields = [ @@ -47,6 +43,23 @@ def get_resource_type(self, obj): return "3DModel" return self.Meta.model.__name__ + def get_thumbnail(self, obj): + request = self.context.get('request') + try: + if obj.thumbnail_image and exists(obj.thumbnail_image.path): + thumbnail = get_thumbnail(obj.thumbnail_image, "128x128", crop="center") + if request is not None: + return request.build_absolute_uri(thumbnail.url) + return thumbnail.url + except Exception as e: + pass + + # Return a full URL to a default image if no thumbnail exists or if there's an error + default_url = static("images/qgis-icon-32x32.png") + if request is not None: + return request.build_absolute_uri(default_url) + return default_url + class GeopackageSerializer(ResourceBaseSerializer): class Meta(ResourceBaseSerializer.Meta): From 9e5b62cd2b3a4081cb2adf8ac6a3dcca352659cf Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:27:08 +0300 Subject: [PATCH 08/14] Add test cases for missing thumbnail handler --- qgis-app/api/tests/test_views.py | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/qgis-app/api/tests/test_views.py b/qgis-app/api/tests/test_views.py index bc372648..ddbbfd89 100644 --- a/qgis-app/api/tests/test_views.py +++ b/qgis-app/api/tests/test_views.py @@ -1,6 +1,7 @@ import io import json import zipfile +import os from django.contrib.auth.models import Group, User from django.core.files.base import ContentFile @@ -275,3 +276,48 @@ def test_download_resource_should_be_a_file_in_a_zip(self): self.assertIn("a_filename", zip_file.namelist()[0]) self.assertNotIn(".zip", zip_file.namelist()) zip_file.close() + + def test_thumbnail_exists(self): + url = reverse("resource-list") + response = self.client.get(url) + json_parse = json.loads(response.content) + self.assertEqual(json_parse["total"], 5) + result = json_parse["results"] + for i, d in enumerate(result): + if d["resource_type"] == "Geopackage": + g_index = i + elif d["resource_type"] == "Model": + m_index = i + elif d["resource_type"] == "Style": + s_index = i + elif d["resource_type"] == "LayerDefinition": + l_index = i + elif d["resource_type"] == "3DModel": + w_index = i + + expected_url = 'http://testserver/media/cache' + self.assertTrue(str(result[s_index]['thumbnail']).startswith(expected_url)) + + def test_thumbnail_missing(self): + # Ensure that the object's thumbnail_image is missing + os.remove(self.style.thumbnail_image.path) + url = reverse("resource-list") + response = self.client.get(url) + json_parse = json.loads(response.content) + self.assertEqual(json_parse["total"], 5) + result = json_parse["results"] + for i, d in enumerate(result): + if d["resource_type"] == "Geopackage": + g_index = i + elif d["resource_type"] == "Model": + m_index = i + elif d["resource_type"] == "Style": + s_index = i + elif d["resource_type"] == "LayerDefinition": + l_index = i + elif d["resource_type"] == "3DModel": + w_index = i + + expected_url = 'http://testserver/static/images/qgis-icon-32x32.png' + print(result[s_index]['thumbnail']) + self.assertTrue(str(result[s_index]['thumbnail']).startswith(expected_url)) \ No newline at end of file From 575db923fc65a9b6bde3f576dacc6503d1a552f6 Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Mon, 24 Jun 2024 09:40:02 +0300 Subject: [PATCH 09/14] Add OSError handler --- qgis-app/middleware.py | 31 +++++++++++++++++++++++-------- qgis-app/settings.py | 2 ++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/qgis-app/middleware.py b/qgis-app/middleware.py index 76f05d46..094d65e8 100644 --- a/qgis-app/middleware.py +++ b/qgis-app/middleware.py @@ -5,14 +5,8 @@ from django.shortcuts import render from django.http import HttpResponse from django.utils.deprecation import MiddlewareMixin - -class HandleTemplateDoesNotExistMiddleware(MiddlewareMixin): - """Handle missing templates""" - def process_exception(self, request, exception): - if isinstance(exception, TemplateDoesNotExist): - return render(request, '404.html', status=404) - return None - +import logging +import sentry_sdk """ QGIS-DJANGO - MIDDLEWARE @@ -22,6 +16,7 @@ def process_exception(self, request, exception): @license: GNU AGPL, see COPYING for details. """ +logger = logging.getLogger(__name__) def XForwardedForMiddleware(get_response): # One-time configuration and initialization. @@ -43,3 +38,23 @@ def middleware(request): return response return middleware + +class HandleTemplateDoesNotExistMiddleware(MiddlewareMixin): + """Handle missing templates""" + def process_exception(self, request, exception): + if isinstance(exception, TemplateDoesNotExist): + return render(request, '404.html', status=404) + return None + +class HandleOSErrorMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + try: + response = self.get_response(request) + except OSError as e: + logger.error("OSError occurred", exc_info=True) + sentry_sdk.capture_exception(e) + raise e + return response \ No newline at end of file diff --git a/qgis-app/settings.py b/qgis-app/settings.py index 2900d0b5..53894dbc 100644 --- a/qgis-app/settings.py +++ b/qgis-app/settings.py @@ -88,6 +88,8 @@ "middleware.XForwardedForMiddleware", # Handle missing template "middleware.HandleTemplateDoesNotExistMiddleware", + # Handle OSError + "middleware.HandleOSErrorMiddleware", ] ROOT_URLCONF = "urls" From 91c6cd13167a7166ab189b30d3f165c5fe514b11 Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:09:12 +0300 Subject: [PATCH 10/14] Handle invalid order_by parameter for resources --- qgis-app/base/views/processing_view.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qgis-app/base/views/processing_view.py b/qgis-app/base/views/processing_view.py index a250f38c..b01e83f5 100644 --- a/qgis-app/base/views/processing_view.py +++ b/qgis-app/base/views/processing_view.py @@ -172,6 +172,11 @@ class ResourceSearchMixin(object): """ def get_queryset_search(self, qs): + # Allowed fields for ordering + allowed_order_by_fields = [ + "name", "type", "download_count" + "creator", "upload_date", "modified_date" + ] q = self.request.GET.get("q") if q: qs = qs.annotate( @@ -192,7 +197,8 @@ def get_queryset_search(self, qs): elif order_by == "type": qs = qs.order_by("style_type__name") else: - qs = qs.order_by(order_by) + if order_by.lstrip('-') in allowed_order_by_fields: + qs = qs.order_by(order_by) return qs def get_queryset_search_and_is_creator(self, qs): From f5373a08116561ff244440b6124765887ee11cd5 Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:25:31 +0300 Subject: [PATCH 11/14] Handle null values in contry_code and country_name --- qgis-app/plugins/tests/test_download.py | 19 ++++++++++++++++++- qgis-app/plugins/views.py | 4 ++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/qgis-app/plugins/tests/test_download.py b/qgis-app/plugins/tests/test_download.py index 34f204bd..6ebc097a 100644 --- a/qgis-app/plugins/tests/test_download.py +++ b/qgis-app/plugins/tests/test_download.py @@ -65,4 +65,21 @@ def test_version_download_per_country(self): self.assertEqual(response.status_code, 200) self.assertTrue(download_record.country_code == 'ID') - self.assertTrue(download_record.country_name == 'Indonesia') \ No newline at end of file + self.assertTrue(download_record.country_name == 'Indonesia') + + + def test_download_per_country_with_invalid_ip(self): + download_url = reverse('version_download', args=[self.plugin.package_name, self.version.version]) + c = Client(REMOTE_ADDR='123.456.789.100') + response = c.get(download_url) + + self.version.refresh_from_db() + self.plugin.refresh_from_db() + download_record = PluginVersionDownload.objects.get( + plugin_version=self.version, + download_date=timezone.now().date() + ) + + self.assertEqual(response.status_code, 200) + self.assertTrue(download_record.country_code == 'N/D') + self.assertTrue(download_record.country_name == 'N/D') \ No newline at end of file diff --git a/qgis-app/plugins/views.py b/qgis-app/plugins/views.py index 43f10054..6f517f6b 100644 --- a/qgis-app/plugins/views.py +++ b/qgis-app/plugins/views.py @@ -1534,6 +1534,10 @@ def version_download(request, package_name, version): country_code = 'N/D' country_name = 'N/D' + # Handle null values + country_code = country_code or 'N/D' + country_name = country_name or 'N/D' + download_record, created = PluginVersionDownload.objects.get_or_create( plugin_version = version, country_code = country_code, From 26de01a966302810ecd95a8745cb7621a1f06ebf Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:04:39 +0300 Subject: [PATCH 12/14] Handle RequestDataTooBig error --- qgis-app/middleware.py | 15 +++++++++++++-- qgis-app/settings.py | 2 ++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/qgis-app/middleware.py b/qgis-app/middleware.py index 094d65e8..5c8172cd 100644 --- a/qgis-app/middleware.py +++ b/qgis-app/middleware.py @@ -3,7 +3,8 @@ from django.template import TemplateDoesNotExist from django.shortcuts import render -from django.http import HttpResponse +from django.core.exceptions import RequestDataTooBig +from django.http import JsonResponse from django.utils.deprecation import MiddlewareMixin import logging import sentry_sdk @@ -57,4 +58,14 @@ def __call__(self, request): logger.error("OSError occurred", exc_info=True) sentry_sdk.capture_exception(e) raise e - return response \ No newline at end of file + return response +class HandleRequestDataTooBigMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + try: + response = self.get_response(request) + return response + except RequestDataTooBig: + return JsonResponse({'error': 'Request data is too large. Please upload smaller files.'}, status=413) \ No newline at end of file diff --git a/qgis-app/settings.py b/qgis-app/settings.py index 53894dbc..e24d7767 100644 --- a/qgis-app/settings.py +++ b/qgis-app/settings.py @@ -90,6 +90,8 @@ "middleware.HandleTemplateDoesNotExistMiddleware", # Handle OSError "middleware.HandleOSErrorMiddleware", + # Handle RequestDataTooBig error + "middleware.HandleRequestDataTooBigMiddleware", ] ROOT_URLCONF = "urls" From 6268c9ac65e5456513642b77bde29baf2fd5123b Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:50:19 +0300 Subject: [PATCH 13/14] Handle beat connection error --- dockerize/docker-compose.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dockerize/docker-compose.yml b/dockerize/docker-compose.yml index 10619ed8..e72fcb8f 100644 --- a/dockerize/docker-compose.yml +++ b/dockerize/docker-compose.yml @@ -107,6 +107,9 @@ services: beat: <<: *uwsgi-common container_name: qgis-plugins-beat + depends_on: + rabbitmq: + condition: service_healthy working_dir: /home/web/django_project entrypoint: [ ] command: celery --app=plugins.celery:app beat -s /home/web/celerybeat-schedule/schedule -l INFO @@ -119,8 +122,6 @@ services: depends_on: db: condition: service_started - rabbitmq: - condition: service_healthy beat: condition: service_started working_dir: /home/web/django_project From a5de0aa7d85fb2fffeb4f557dd554ebba1c0fad4 Mon Sep 17 00:00:00 2001 From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com> Date: Mon, 24 Jun 2024 14:57:52 +0300 Subject: [PATCH 14/14] Validate svg icons with ET --- qgis-app/plugins/templatetags/plugin_utils.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/qgis-app/plugins/templatetags/plugin_utils.py b/qgis-app/plugins/templatetags/plugin_utils.py index 182c410c..0bd4ca49 100755 --- a/qgis-app/plugins/templatetags/plugin_utils.py +++ b/qgis-app/plugins/templatetags/plugin_utils.py @@ -1,5 +1,6 @@ from django import template from PIL import Image, UnidentifiedImageError +import xml.etree.ElementTree as ET register = template.Library() @@ -34,8 +35,23 @@ def file_extension(value): def is_image_valid(image): if not image: return False + # Check if the file is an SVG by extension + if image.path.lower().endswith('.svg'): + return _validate_svg(image.path) + return _validate_image(image.path) + + +def _validate_svg(file_path): + try: + # Parse the SVG file to ensure it's well-formed XML + ET.parse(file_path) + return True + except (ET.ParseError, FileNotFoundError): + return False + +def _validate_image(file_path): try: - img = Image.open(image.path) + img = Image.open(file_path) img.verify() return True except (FileNotFoundError, UnidentifiedImageError):