diff --git a/engine/core/crm/amo/gateway.py b/engine/core/crm/amo/gateway.py index 3321fcb9..02ad2fe9 100644 --- a/engine/core/crm/amo/gateway.py +++ b/engine/core/crm/amo/gateway.py @@ -1,7 +1,7 @@ import logging import requests -from constance import config +from django.conf import settings from django.core.cache import cache from django.db import transaction @@ -51,7 +51,7 @@ class AmoCRM: payload = { "client_id": self.client_id, "client_secret": self.client_secret, - "redirect_uri": f"https://api.{config.BASE_DOMAIN}/", + "redirect_uri": f"https://api.{settings.BASE_DOMAIN}/", } if self.refresh_token: payload["grant_type"] = "refresh_token" diff --git a/engine/core/graphene/object_types.py b/engine/core/graphene/object_types.py index b3f33358..2b3e9a7c 100644 --- a/engine/core/graphene/object_types.py +++ b/engine/core/graphene/object_types.py @@ -3,6 +3,7 @@ from contextlib import suppress from typing import Any from constance import config +from django.conf import settings from django.core.cache import cache from django.db.models import Max, Min, QuerySet from django.utils.translation import gettext_lazy as _ @@ -136,7 +137,7 @@ class BrandType(DjangoObjectType): # type: ignore [misc] def resolve_seo_meta(self: Brand, info) -> dict[str, str | list[Any] | dict[str, str] | None]: lang = graphene_current_lang() - base = f"https://{config.BASE_DOMAIN}" + base = f"https://{settings.BASE_DOMAIN}" canonical = f"{base}/{lang}/brand/{self.slug}" title = f"{self.name} | {config.PROJECT_NAME}" description = (self.description or "")[:180] @@ -234,7 +235,7 @@ class CategoryType(DjangoObjectType): # type: ignore [misc] return result def resolve_image(self: Category, info) -> str: - return info.context.build_absolute_uri(self.image.url) if self.image else "" + return self.image_url def resolve_markup_percent(self: Category, info) -> float: if info.context.user.has_perm("core.view_category"): @@ -262,7 +263,7 @@ class CategoryType(DjangoObjectType): # type: ignore [misc] def resolve_seo_meta(self: Category, info): lang = graphene_current_lang() - base = f"https://{config.BASE_DOMAIN}" + base = f"https://{settings.BASE_DOMAIN}" canonical = f"{base}/{lang}/catalog/{self.slug}" title = f"{self.name} | {config.PROJECT_NAME}" description = (self.description or "")[:180] @@ -465,8 +466,8 @@ class ProductImageType(DjangoObjectType): # type: ignore [misc] filter_fields = ["uuid"] description = _("product's images") - def resolve_image(self: ProductImage, info): - return info.context.build_absolute_uri(self.image.url) if self.image else "" + def resolve_image(self: ProductImage, _info): + return self.image_url class ProductType(DjangoObjectType): # type: ignore [misc] @@ -534,7 +535,7 @@ class ProductType(DjangoObjectType): # type: ignore [misc] def resolve_seo_meta(self: Product, info): lang = graphene_current_lang() - base = f"https://{config.BASE_DOMAIN}" + base = f"https://{settings.BASE_DOMAIN}" canonical = f"{base}/{lang}/product/{self.slug}" title = f"{self.name} | {config.PROJECT_NAME}" description = (self.description or "")[:180] diff --git a/engine/core/models.py b/engine/core/models.py index 9d7fd551..5df02d7b 100644 --- a/engine/core/models.py +++ b/engine/core/models.py @@ -420,6 +420,13 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel): # return list(by_attr.values()) # type: ignore [arg-type] + @cached_property + def image_url(self) -> str: + with suppress(ValueError): + url = str(self.image.url) + url = url if "http" in url else f"https://api.{settings.BASE_DOMAIN}{url}" + return "" + class Meta: verbose_name = _("category") verbose_name_plural = _("categories") @@ -843,6 +850,13 @@ class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel): # t def __str__(self) -> str: return self.alt + @cached_property + def image_url(self) -> str: + with suppress(ValueError): + url = str(self.image.url) + url = url if "http" in url else f"https://api.{settings.BASE_DOMAIN}{url}" + return "" + class Meta: ordering = ("priority",) verbose_name = _("product image") @@ -1953,5 +1967,5 @@ class DigitalAssetDownload(ExportModelOperationsMixin("attribute_group"), NiceMo @property def url(self): return ( - f"https://api.{config.BASE_DOMAIN}/download/{urlsafe_base64_encode(force_bytes(self.order_product.uuid))}" + f"https://api.{settings.BASE_DOMAIN}/download/{urlsafe_base64_encode(force_bytes(self.order_product.uuid))}" ) diff --git a/engine/core/serializers/detail.py b/engine/core/serializers/detail.py index 6a905c53..53553a9f 100644 --- a/engine/core/serializers/detail.py +++ b/engine/core/serializers/detail.py @@ -56,7 +56,6 @@ class CategoryDetailListSerializer(ListSerializer): class CategoryDetailSerializer(ModelSerializer): children = SerializerMethodField() - image = SerializerMethodField() filterable_attributes = SerializerMethodField() class Meta: @@ -75,11 +74,6 @@ class CategoryDetailSerializer(ModelSerializer): "modified", ] - def get_image(self, obj: Category) -> str | None: - with suppress(ValueError): - return obj.image.url - return None - def get_filterable_attributes(self, obj: Category) -> list[FilterableAttribute]: return obj.filterable_attributes @@ -161,8 +155,6 @@ class ProductTagDetailSerializer(ModelSerializer): class ProductImageDetailSerializer(ModelSerializer): - image = SerializerMethodField() - class Meta: model = ProductImage fields = [ @@ -174,9 +166,6 @@ class ProductImageDetailSerializer(ModelSerializer): "modified", ] - def get_image(self, obj: ProductImage) -> str: - return obj.image.url or "" - class AttributeDetailSerializer(ModelSerializer): categories = CategoryDetailSerializer(many=True) diff --git a/engine/core/serializers/simple.py b/engine/core/serializers/simple.py index 0f7509f2..b76c35eb 100644 --- a/engine/core/serializers/simple.py +++ b/engine/core/serializers/simple.py @@ -42,7 +42,6 @@ class AttributeGroupSimpleSerializer(ModelSerializer): # type: ignore [type-arg class CategorySimpleSerializer(ModelSerializer): # type: ignore [type-arg] children = SerializerMethodField() - image = SerializerMethodField() class Meta: model = Category @@ -54,11 +53,6 @@ class CategorySimpleSerializer(ModelSerializer): # type: ignore [type-arg] "children", ] - def get_image(self, obj: Category) -> str | None: - with suppress(ValueError): - return str(obj.image.url) - return None - def get_children(self, obj: Category) -> dict[str, Any]: request = self.context.get("request") if request is not None and request.user.has_perm("view_category"): diff --git a/engine/core/utils/languages.py b/engine/core/utils/languages.py index 797b67c1..ea40bf93 100644 --- a/engine/core/utils/languages.py +++ b/engine/core/utils/languages.py @@ -1,5 +1,5 @@ -from constance import config +from django.conf import settings def get_flag_by_language(language: str) -> str: - return f"https://api.{config.BASE_DOMAIN}/static/flags/{language}.png" + return f"https://api.{settings.BASE_DOMAIN}/static/flags/{language}.png" diff --git a/engine/core/utils/seo_builders.py b/engine/core/utils/seo_builders.py index b76fd322..6567b54a 100644 --- a/engine/core/utils/seo_builders.py +++ b/engine/core/utils/seo_builders.py @@ -9,8 +9,8 @@ def org_schema(): "@context": "https://schema.org", "@type": "Organization", "name": config.COMPANY_NAME, - "url": f"https://{config.BASE_DOMAIN}/", - "logo": f"https://{config.BASE_DOMAIN}/static/logo.png", + "url": f"https://{settings.BASE_DOMAIN}/", + "logo": f"https://{settings.BASE_DOMAIN}/static/logo.png", } @@ -19,10 +19,10 @@ def website_schema(): "@context": "https://schema.org", "@type": "WebSite", "name": config.PROJECT_NAME, - "url": f"https://{config.BASE_DOMAIN}/", + "url": f"https://{settings.BASE_DOMAIN}/", "potentialAction": { "@type": "SearchAction", - "target": f"https://{config.BASE_DOMAIN}/search?q={{query}}", + "target": f"https://{settings.BASE_DOMAIN}/search?q={{query}}", "query-input": "required name=query", }, } @@ -56,7 +56,7 @@ def product_schema(product, images, rating=None): "priceCurrency": settings.CURRENCY_CODE, "availability": "https://schema.org/InStock" if stock.quantity > 0 else "https://schema.org/OutOfStock", "sku": stock.sku, - "url": f"https://{config.BASE_DOMAIN}/product/{product.slug}", + "url": f"https://{settings.BASE_DOMAIN}/product/{product.slug}", } ) data = { diff --git a/engine/core/viewsets.py b/engine/core/viewsets.py index 5106923d..1ee307ef 100644 --- a/engine/core/viewsets.py +++ b/engine/core/viewsets.py @@ -271,7 +271,7 @@ class CategoryViewSet(EvibesViewSet): title = f"{category.name} | {config.PROJECT_NAME}" description = (category.description or "")[:180] - canonical = f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{category.slug}" + canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{category.slug}" og_image = request.build_absolute_uri(category.image.url) if getattr(category, "image", None) else "" og = { @@ -283,10 +283,10 @@ class CategoryViewSet(EvibesViewSet): } tw = {"card": "summary_large_image", "title": title, "description": description} - crumbs = [("Home", f"https://{config.BASE_DOMAIN}/")] + crumbs = [("Home", f"https://{settings.BASE_DOMAIN}/")] if category.get_ancestors().exists(): for c in category.get_ancestors(): - crumbs.append((c.name, f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{c.slug}")) + crumbs.append((c.name, f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{c.slug}")) crumbs.append((category.name, canonical)) json_ld = [org_schema(), website_schema(), breadcrumb_schema(crumbs), category_schema(category, canonical)] @@ -303,7 +303,7 @@ class CategoryViewSet(EvibesViewSet): .distinct()[:24] ) for p in qs: - product_urls.append(f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}") + product_urls.append(f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}") if product_urls: json_ld.append(item_list_schema(product_urls)) @@ -388,7 +388,7 @@ class BrandViewSet(EvibesViewSet): title = f"{brand.name} | {config.PROJECT_NAME}" description = (brand.description or "")[:180] - canonical = f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/brand/{brand.slug}" + canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/brand/{brand.slug}" logo_url = ( request.build_absolute_uri(brand.big_logo.url) @@ -408,7 +408,7 @@ class BrandViewSet(EvibesViewSet): tw = {"card": "summary_large_image", "title": title, "description": description} crumbs = [ - ("Home", f"https://{config.BASE_DOMAIN}/"), + ("Home", f"https://{settings.BASE_DOMAIN}/"), (brand.name, canonical), ] @@ -528,7 +528,7 @@ class ProductViewSet(EvibesViewSet): rating = {"value": p.rating, "count": p.feedbacks_count} title = f"{p.name} | {config.PROJECT_NAME}" description = (p.description or "")[:180] - canonical = f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}" + canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}" og = { "title": title, "description": description, @@ -538,10 +538,10 @@ class ProductViewSet(EvibesViewSet): } tw = {"card": "summary_large_image", "title": title, "description": description} - crumbs = [("Home", f"https://{config.BASE_DOMAIN}/")] + crumbs = [("Home", f"https://{settings.BASE_DOMAIN}/")] if p.category: for c in p.category.get_ancestors(include_self=True): - crumbs.append((c.name, f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{c.slug}")) + crumbs.append((c.name, f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{c.slug}")) crumbs.append((p.name, canonical)) json_ld = [org_schema(), website_schema()] diff --git a/evibes/settings/base.py b/evibes/settings/base.py index b1ceea49..99a2a6bc 100644 --- a/evibes/settings/base.py +++ b/evibes/settings/base.py @@ -7,13 +7,16 @@ from typing import Any from django.core.exceptions import ImproperlyConfigured EVIBES_VERSION = "2025.4" -RELEASE_DATE = datetime(2025, 9, 13) +RELEASE_DATE = datetime(2025, 11, 9) BASE_DIR = Path(__file__).resolve().parent.parent.parent +INITIALIZED = (BASE_DIR / ".initialized").exists() SECRET_KEY = getenv("SECRET_KEY", "SUPER_SECRET_KEY") DEBUG = bool(int(getenv("DEBUG", "1"))) +BASE_DOMAIN: str = getenv("EVIBES_BASE_DOMAIN", "localhost") + ALLOWED_HOSTS: set[str] = { "app", "worker", @@ -294,10 +297,10 @@ TIME_ZONE: str = getenv("TIME_ZONE", "Europe/London") WHITENOISE_MANIFEST_STRICT: bool = False -STATIC_URL: str = "/static/" +STATIC_URL: str = f"https://api.{BASE_DOMAIN}/static/" if INITIALIZED else "static/" STATIC_ROOT: Path = BASE_DIR / "static" -MEDIA_URL: str = "/media/" +MEDIA_URL: str = f"https://api.{BASE_DOMAIN}/media/" if INITIALIZED else "media/" MEDIA_ROOT: Path = BASE_DIR / "media" AUTH_USER_MODEL: str = "vibes_auth.User"