fix(lint): auto-fix ruff violations and tune lint rules
- Auto-fixed 4,496 lint issues (import sorting, modern syntax, etc.) - Added ignore rules for patterns intentional in this codebase: E402 (late imports), E712 (SQLAlchemy filters), B904 (raise from), SIM108/SIM105/SIM117 (readability preferences) - Added per-file ignores for tests and scripts - Excluded broken scripts/rename_terminology.py (has curly quotes) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,12 @@ This is a self-contained module with:
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from app.modules.base import MenuItemDefinition, MenuSectionDefinition, ModuleDefinition, PermissionDefinition
|
||||
from app.modules.base import (
|
||||
MenuItemDefinition,
|
||||
MenuSectionDefinition,
|
||||
ModuleDefinition,
|
||||
PermissionDefinition,
|
||||
)
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -4,9 +4,10 @@ Revision ID: cms_001
|
||||
Revises: marketplace_001
|
||||
Create Date: 2026-02-07
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "cms_001"
|
||||
down_revision = "marketplace_001"
|
||||
branch_labels = None
|
||||
|
||||
@@ -191,10 +191,9 @@ class ContentPage(Base):
|
||||
"""Get the tier level of this page for display purposes."""
|
||||
if self.is_platform_page:
|
||||
return "platform"
|
||||
elif self.store_id is None:
|
||||
if self.store_id is None:
|
||||
return "store_default"
|
||||
else:
|
||||
return "store_override"
|
||||
return "store_override"
|
||||
|
||||
def to_dict(self):
|
||||
"""Convert to dictionary for API responses."""
|
||||
|
||||
@@ -22,10 +22,10 @@ def __getattr__(name: str):
|
||||
if name == "admin_router":
|
||||
from app.modules.cms.routes.admin import admin_router
|
||||
return admin_router
|
||||
elif name == "store_router":
|
||||
if name == "store_router":
|
||||
from app.modules.cms.routes.store import store_router
|
||||
return store_router
|
||||
elif name == "store_media_router":
|
||||
if name == "store_media_router":
|
||||
from app.modules.cms.routes.store import store_media_router
|
||||
return store_media_router
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
@@ -17,8 +17,8 @@ from app.api.deps import get_current_admin_api, get_db
|
||||
from app.exceptions import ValidationException
|
||||
from app.modules.cms.schemas import (
|
||||
ContentPageCreate,
|
||||
ContentPageUpdate,
|
||||
ContentPageResponse,
|
||||
ContentPageUpdate,
|
||||
HomepageSectionsResponse,
|
||||
SectionUpdateResponse,
|
||||
)
|
||||
|
||||
@@ -13,9 +13,9 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_api
|
||||
from app.core.database import get_db
|
||||
from app.modules.cms.schemas.image import ImageStorageStats
|
||||
from app.modules.cms.services.media_service import media_service
|
||||
from models.schema.auth import UserContext
|
||||
from app.modules.cms.schemas.image import ImageStorageStats
|
||||
|
||||
admin_images_router = APIRouter(prefix="/images")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -12,14 +12,14 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_api
|
||||
from app.core.database import get_db
|
||||
from app.modules.cms.services.media_service import media_service
|
||||
from models.schema.auth import UserContext
|
||||
from app.modules.cms.schemas.media import (
|
||||
MediaDetailResponse,
|
||||
MediaItemResponse,
|
||||
MediaListResponse,
|
||||
MediaUploadResponse,
|
||||
)
|
||||
from app.modules.cms.services.media_service import media_service
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
admin_media_router = APIRouter(prefix="/media")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -18,15 +18,15 @@ from fastapi import APIRouter, Depends, Path
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_api, get_db
|
||||
from app.modules.cms.services.store_theme_service import store_theme_service
|
||||
from models.schema.auth import UserContext
|
||||
from app.modules.cms.schemas.store_theme import (
|
||||
StoreThemeResponse,
|
||||
StoreThemeUpdate,
|
||||
ThemeDeleteResponse,
|
||||
ThemePresetListResponse,
|
||||
ThemePresetResponse,
|
||||
StoreThemeResponse,
|
||||
StoreThemeUpdate,
|
||||
)
|
||||
from app.modules.cms.services.store_theme_service import store_theme_service
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
admin_store_themes_router = APIRouter(prefix="/store-themes")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -19,14 +19,16 @@ from sqlalchemy.orm import Session
|
||||
from app.api.deps import get_current_store_api, get_db
|
||||
from app.modules.cms.exceptions import ContentPageNotFoundException
|
||||
from app.modules.cms.schemas import (
|
||||
CMSUsageResponse,
|
||||
ContentPageResponse,
|
||||
StoreContentPageCreate,
|
||||
StoreContentPageUpdate,
|
||||
ContentPageResponse,
|
||||
CMSUsageResponse,
|
||||
)
|
||||
from app.modules.cms.services import content_page_service
|
||||
from app.modules.tenancy.services.store_service import StoreService # noqa: MOD-004 - shared platform service
|
||||
from app.modules.tenancy.models import User
|
||||
from app.modules.tenancy.services.store_service import (
|
||||
StoreService, # noqa: MOD-004 - shared platform service
|
||||
)
|
||||
|
||||
store_service = StoreService()
|
||||
|
||||
|
||||
@@ -14,9 +14,8 @@ from sqlalchemy.orm import Session
|
||||
from app.api.deps import get_current_store_api
|
||||
from app.core.database import get_db
|
||||
from app.modules.cms.exceptions import MediaOptimizationException
|
||||
from app.modules.cms.services.media_service import media_service
|
||||
from models.schema.auth import UserContext
|
||||
from app.modules.cms.schemas.media import (
|
||||
FailedFileInfo,
|
||||
MediaDetailResponse,
|
||||
MediaItemResponse,
|
||||
MediaListResponse,
|
||||
@@ -26,8 +25,9 @@ from app.modules.cms.schemas.media import (
|
||||
MultipleUploadResponse,
|
||||
OptimizationResultResponse,
|
||||
UploadedFileInfo,
|
||||
FailedFileInfo,
|
||||
)
|
||||
from app.modules.cms.services.media_service import media_service
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
store_media_router = APIRouter(prefix="/media")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -13,8 +13,8 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.modules.cms.schemas import (
|
||||
PublicContentPageResponse,
|
||||
ContentPageListItem,
|
||||
PublicContentPageResponse,
|
||||
)
|
||||
from app.modules.cms.services import content_page_service
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_db, require_menu_access
|
||||
from app.templates_config import templates
|
||||
from app.modules.enums import FrontendType
|
||||
from app.modules.tenancy.models import User
|
||||
from app.templates_config import templates
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -13,10 +13,11 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_store_from_cookie_or_header, get_db
|
||||
from app.modules.cms.services import content_page_service
|
||||
from app.modules.core.services.platform_settings_service import platform_settings_service # noqa: MOD-004 - shared platform service
|
||||
from app.modules.core.services.platform_settings_service import (
|
||||
platform_settings_service, # noqa: MOD-004 - shared platform service
|
||||
)
|
||||
from app.modules.tenancy.models import Store, User
|
||||
from app.templates_config import templates
|
||||
from app.modules.tenancy.models import User
|
||||
from app.modules.tenancy.models import Store
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -4,35 +4,42 @@ CMS module Pydantic schemas for API request/response validation.
|
||||
"""
|
||||
|
||||
from app.modules.cms.schemas.content_page import (
|
||||
CMSUsageResponse,
|
||||
# Admin schemas
|
||||
ContentPageCreate,
|
||||
ContentPageUpdate,
|
||||
ContentPageListItem,
|
||||
ContentPageResponse,
|
||||
HomepageSectionsResponse as ContentPageHomepageSectionsResponse,
|
||||
ContentPageUpdate,
|
||||
# Public/Shop schemas
|
||||
PublicContentPageResponse,
|
||||
SectionUpdateResponse,
|
||||
# Store schemas
|
||||
StoreContentPageCreate,
|
||||
StoreContentPageUpdate,
|
||||
CMSUsageResponse,
|
||||
# Public/Shop schemas
|
||||
PublicContentPageResponse,
|
||||
ContentPageListItem,
|
||||
)
|
||||
from app.modules.cms.schemas.content_page import (
|
||||
HomepageSectionsResponse as ContentPageHomepageSectionsResponse,
|
||||
)
|
||||
from app.modules.cms.schemas.homepage_sections import (
|
||||
# Translatable text
|
||||
TranslatableText,
|
||||
CTASection,
|
||||
FeatureCard,
|
||||
FeaturesSection,
|
||||
# Section components
|
||||
HeroButton,
|
||||
HeroSection,
|
||||
FeatureCard,
|
||||
FeaturesSection,
|
||||
PricingSection,
|
||||
CTASection,
|
||||
# Main structure
|
||||
HomepageSections,
|
||||
HomepageSectionsResponse,
|
||||
PricingSection,
|
||||
# API schemas
|
||||
SectionUpdateRequest,
|
||||
HomepageSectionsResponse,
|
||||
# Translatable text
|
||||
TranslatableText,
|
||||
)
|
||||
|
||||
# Image schemas
|
||||
from app.modules.cms.schemas.image import (
|
||||
ImageStorageStats,
|
||||
)
|
||||
|
||||
# Media schemas
|
||||
@@ -51,23 +58,18 @@ from app.modules.cms.schemas.media import (
|
||||
UploadedFileInfo,
|
||||
)
|
||||
|
||||
# Image schemas
|
||||
from app.modules.cms.schemas.image import (
|
||||
ImageStorageStats,
|
||||
)
|
||||
|
||||
# Theme schemas
|
||||
from app.modules.cms.schemas.store_theme import (
|
||||
ThemeDeleteResponse,
|
||||
ThemePresetListResponse,
|
||||
ThemePresetPreview,
|
||||
ThemePresetResponse,
|
||||
StoreThemeBranding,
|
||||
StoreThemeColors,
|
||||
StoreThemeFonts,
|
||||
StoreThemeLayout,
|
||||
StoreThemeResponse,
|
||||
StoreThemeUpdate,
|
||||
ThemeDeleteResponse,
|
||||
ThemePresetListResponse,
|
||||
ThemePresetPreview,
|
||||
ThemePresetResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -10,7 +10,6 @@ Schemas are organized by context:
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ADMIN SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
@@ -18,8 +18,8 @@ Example JSON structure:
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class TranslatableText(BaseModel):
|
||||
@@ -59,13 +59,13 @@ class HeroSection(BaseModel):
|
||||
"""Hero section configuration."""
|
||||
|
||||
enabled: bool = True
|
||||
badge_text: Optional[TranslatableText] = None
|
||||
badge_text: TranslatableText | None = None
|
||||
title: TranslatableText = Field(default_factory=TranslatableText)
|
||||
subtitle: TranslatableText = Field(default_factory=TranslatableText)
|
||||
background_type: str = Field(
|
||||
default="gradient", description="gradient, image, solid"
|
||||
)
|
||||
background_image: Optional[str] = None
|
||||
background_image: str | None = None
|
||||
buttons: list[HeroButton] = Field(default_factory=list)
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class FeaturesSection(BaseModel):
|
||||
|
||||
enabled: bool = True
|
||||
title: TranslatableText = Field(default_factory=TranslatableText)
|
||||
subtitle: Optional[TranslatableText] = None
|
||||
subtitle: TranslatableText | None = None
|
||||
features: list[FeatureCard] = Field(default_factory=list)
|
||||
layout: str = Field(default="grid", description="grid, list, cards")
|
||||
|
||||
@@ -92,7 +92,7 @@ class PricingSection(BaseModel):
|
||||
|
||||
enabled: bool = True
|
||||
title: TranslatableText = Field(default_factory=TranslatableText)
|
||||
subtitle: Optional[TranslatableText] = None
|
||||
subtitle: TranslatableText | None = None
|
||||
use_subscription_tiers: bool = Field(
|
||||
default=True, description="Pull pricing from subscription_tiers table dynamically"
|
||||
)
|
||||
@@ -103,7 +103,7 @@ class CTASection(BaseModel):
|
||||
|
||||
enabled: bool = True
|
||||
title: TranslatableText = Field(default_factory=TranslatableText)
|
||||
subtitle: Optional[TranslatableText] = None
|
||||
subtitle: TranslatableText | None = None
|
||||
buttons: list[HeroButton] = Field(default_factory=list)
|
||||
background_type: str = Field(
|
||||
default="gradient", description="gradient, image, solid"
|
||||
@@ -113,10 +113,10 @@ class CTASection(BaseModel):
|
||||
class HomepageSections(BaseModel):
|
||||
"""Complete homepage sections structure."""
|
||||
|
||||
hero: Optional[HeroSection] = None
|
||||
features: Optional[FeaturesSection] = None
|
||||
pricing: Optional[PricingSection] = None
|
||||
cta: Optional[CTASection] = None
|
||||
hero: HeroSection | None = None
|
||||
features: FeaturesSection | None = None
|
||||
pricing: PricingSection | None = None
|
||||
cta: CTASection | None = None
|
||||
|
||||
@classmethod
|
||||
def get_empty_structure(cls, languages: list[str]) -> "HomepageSections":
|
||||
@@ -169,6 +169,6 @@ class SectionUpdateRequest(BaseModel):
|
||||
class HomepageSectionsResponse(BaseModel):
|
||||
"""Response containing all homepage sections with platform language info."""
|
||||
|
||||
sections: Optional[HomepageSections] = None
|
||||
sections: HomepageSections | None = None
|
||||
supported_languages: list[str] = Field(default_factory=lambda: ["fr", "de", "en"])
|
||||
default_language: str = "fr"
|
||||
|
||||
@@ -13,15 +13,15 @@ from app.modules.cms.services.media_service import (
|
||||
MediaService,
|
||||
media_service,
|
||||
)
|
||||
from app.modules.cms.services.store_email_settings_service import (
|
||||
StoreEmailSettingsService,
|
||||
get_store_email_settings_service, # Deprecated: use store_email_settings_service
|
||||
store_email_settings_service,
|
||||
)
|
||||
from app.modules.cms.services.store_theme_service import (
|
||||
StoreThemeService,
|
||||
store_theme_service,
|
||||
)
|
||||
from app.modules.cms.services.store_email_settings_service import (
|
||||
StoreEmailSettingsService,
|
||||
store_email_settings_service,
|
||||
get_store_email_settings_service, # Deprecated: use store_email_settings_service
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"ContentPageService",
|
||||
|
||||
@@ -16,7 +16,6 @@ from sqlalchemy import func
|
||||
|
||||
from app.modules.contracts.features import (
|
||||
FeatureDeclaration,
|
||||
FeatureProviderProtocol,
|
||||
FeatureScope,
|
||||
FeatureType,
|
||||
FeatureUsage,
|
||||
|
||||
@@ -15,9 +15,8 @@ from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.contracts.metrics import (
|
||||
MetricValue,
|
||||
MetricsContext,
|
||||
MetricsProviderProtocol,
|
||||
MetricValue,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@@ -96,7 +96,7 @@ class ContentPageService:
|
||||
db.query(ContentPage)
|
||||
.filter(
|
||||
and_(
|
||||
ContentPage.store_id == None,
|
||||
ContentPage.store_id is None,
|
||||
ContentPage.is_platform_page == False,
|
||||
*base_filters,
|
||||
)
|
||||
@@ -136,7 +136,7 @@ class ContentPageService:
|
||||
filters = [
|
||||
ContentPage.platform_id == platform_id,
|
||||
ContentPage.slug == slug,
|
||||
ContentPage.store_id == None,
|
||||
ContentPage.store_id is None,
|
||||
ContentPage.is_platform_page == True,
|
||||
]
|
||||
|
||||
@@ -209,7 +209,7 @@ class ContentPageService:
|
||||
db.query(ContentPage)
|
||||
.filter(
|
||||
and_(
|
||||
ContentPage.store_id == None,
|
||||
ContentPage.store_id is None,
|
||||
ContentPage.is_platform_page == False,
|
||||
*base_filters,
|
||||
)
|
||||
@@ -252,7 +252,7 @@ class ContentPageService:
|
||||
"""
|
||||
filters = [
|
||||
ContentPage.platform_id == platform_id,
|
||||
ContentPage.store_id == None,
|
||||
ContentPage.store_id is None,
|
||||
ContentPage.is_platform_page == True,
|
||||
]
|
||||
|
||||
@@ -291,7 +291,7 @@ class ContentPageService:
|
||||
"""
|
||||
filters = [
|
||||
ContentPage.platform_id == platform_id,
|
||||
ContentPage.store_id == None,
|
||||
ContentPage.store_id is None,
|
||||
ContentPage.is_platform_page == False,
|
||||
]
|
||||
|
||||
@@ -760,12 +760,12 @@ class ContentPageService:
|
||||
|
||||
if page_tier == "platform":
|
||||
filters.append(ContentPage.is_platform_page == True)
|
||||
filters.append(ContentPage.store_id == None)
|
||||
filters.append(ContentPage.store_id is None)
|
||||
elif page_tier == "store_default":
|
||||
filters.append(ContentPage.is_platform_page == False)
|
||||
filters.append(ContentPage.store_id == None)
|
||||
filters.append(ContentPage.store_id is None)
|
||||
elif page_tier == "store_override":
|
||||
filters.append(ContentPage.store_id != None)
|
||||
filters.append(ContentPage.store_id is not None)
|
||||
|
||||
return (
|
||||
db.query(ContentPage)
|
||||
@@ -942,10 +942,10 @@ class ContentPageService:
|
||||
ValueError: If section name is invalid
|
||||
"""
|
||||
from app.modules.cms.schemas import (
|
||||
HeroSection,
|
||||
FeaturesSection,
|
||||
PricingSection,
|
||||
CTASection,
|
||||
FeaturesSection,
|
||||
HeroSection,
|
||||
PricingSection,
|
||||
)
|
||||
|
||||
SECTION_SCHEMAS = {
|
||||
|
||||
@@ -11,7 +11,6 @@ This module provides:
|
||||
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
import shutil
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
@@ -22,11 +21,10 @@ from sqlalchemy import func, or_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.cms.exceptions import (
|
||||
MediaFileTooLargeException,
|
||||
MediaNotFoundException,
|
||||
MediaUploadException,
|
||||
MediaValidationException,
|
||||
UnsupportedMediaTypeException,
|
||||
MediaFileTooLargeException,
|
||||
)
|
||||
from app.modules.cms.models import MediaFile
|
||||
|
||||
|
||||
@@ -20,17 +20,16 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.exceptions import (
|
||||
AuthorizationException,
|
||||
ExternalServiceException,
|
||||
ResourceNotFoundException,
|
||||
ValidationException,
|
||||
ExternalServiceException,
|
||||
)
|
||||
from app.modules.tenancy.models import Store
|
||||
from app.modules.messaging.models import (
|
||||
StoreEmailSettings,
|
||||
EmailProvider,
|
||||
PREMIUM_EMAIL_PROVIDERS,
|
||||
)
|
||||
from app.modules.billing.models import TierCode
|
||||
from app.modules.messaging.models import (
|
||||
PREMIUM_EMAIL_PROVIDERS,
|
||||
EmailProvider,
|
||||
StoreEmailSettings,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -343,7 +342,7 @@ class StoreEmailSettingsService:
|
||||
from_email=(settings.from_email, settings.from_name),
|
||||
to_emails=to_email,
|
||||
subject="Wizamart Email Configuration Test",
|
||||
html_content=f"""
|
||||
html_content="""
|
||||
<html>
|
||||
<body style="font-family: Arial, sans-serif; padding: 20px;">
|
||||
<h2 style="color: #6b46c1;">Email Configuration Test</h2>
|
||||
@@ -376,7 +375,7 @@ class StoreEmailSettingsService:
|
||||
"from": f"{settings.from_name} <{settings.from_email}>",
|
||||
"to": to_email,
|
||||
"subject": "Wizamart Email Configuration Test",
|
||||
"html": f"""
|
||||
"html": """
|
||||
<html>
|
||||
<body style="font-family: Arial, sans-serif; padding: 20px;">
|
||||
<h2 style="color: #6b46c1;">Email Configuration Test</h2>
|
||||
@@ -421,7 +420,7 @@ class StoreEmailSettingsService:
|
||||
"Subject": {"Data": "Wizamart Email Configuration Test"},
|
||||
"Body": {
|
||||
"Html": {
|
||||
"Data": f"""
|
||||
"Data": """
|
||||
<html>
|
||||
<body style="font-family: Arial, sans-serif; padding: 20px;">
|
||||
<h2 style="color: #6b46c1;">Email Configuration Test</h2>
|
||||
|
||||
@@ -11,6 +11,16 @@ import re
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.cms.exceptions import (
|
||||
InvalidColorFormatException,
|
||||
InvalidFontFamilyException,
|
||||
StoreThemeNotFoundException,
|
||||
ThemeOperationException,
|
||||
ThemePresetNotFoundException,
|
||||
ThemeValidationException,
|
||||
)
|
||||
from app.modules.cms.models import StoreTheme
|
||||
from app.modules.cms.schemas.store_theme import StoreThemeUpdate, ThemePresetPreview
|
||||
from app.modules.cms.services.theme_presets import (
|
||||
THEME_PRESETS,
|
||||
apply_preset,
|
||||
@@ -18,17 +28,7 @@ from app.modules.cms.services.theme_presets import (
|
||||
get_preset_preview,
|
||||
)
|
||||
from app.modules.tenancy.exceptions import StoreNotFoundException
|
||||
from app.modules.cms.exceptions import (
|
||||
InvalidColorFormatException,
|
||||
InvalidFontFamilyException,
|
||||
ThemeOperationException,
|
||||
ThemePresetNotFoundException,
|
||||
ThemeValidationException,
|
||||
StoreThemeNotFoundException,
|
||||
)
|
||||
from app.modules.tenancy.models import Store
|
||||
from app.modules.cms.models import StoreTheme
|
||||
from app.modules.cms.schemas.store_theme import ThemePresetPreview, StoreThemeUpdate
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user