Files
orion/app/modules/tenancy/models/store_domain.py
Samir Boulahtit 79c985ee39
Some checks failed
CI / ruff (push) Failing after 7s
CI / pytest (push) Failing after 0s
CI / architecture (push) Failing after 8s
CI / dependency-scanning (push) Successful in 26s
CI / audit (push) Successful in 8s
CI / docs (push) Has been skipped
fix(lint): use plain comments for architecture validator codes
Replace # noqa: SVC-006 with # SVC-006 to avoid ruff warnings about
unknown codes. Updated architecture validators to match the new format
by checking for the code string directly instead of the noqa: prefix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 23:24:57 +01:00

111 lines
3.2 KiB
Python

# app/modules/tenancy/models/store_domain.py
"""
Store Domain Model - Maps custom domains to stores
"""
from sqlalchemy import (
Boolean,
Column,
DateTime,
ForeignKey,
Index,
Integer,
String,
UniqueConstraint,
)
from sqlalchemy.orm import relationship
from app.core.database import Base
from models.database.base import TimestampMixin
class StoreDomain(Base, TimestampMixin):
"""
Maps custom domains to stores for multi-domain routing.
Examples:
- customdomain1.com -> Store 1
- shop.mybusiness.com -> Store 2
- www.customdomain1.com -> Store 1 (www is stripped)
"""
__tablename__ = "store_domains"
id = Column(Integer, primary_key=True, index=True)
store_id = Column(
Integer, ForeignKey("stores.id", ondelete="CASCADE"), nullable=False
)
# Domain configuration
domain = Column(String(255), nullable=False, unique=True, index=True)
is_primary = Column(Boolean, default=False, nullable=False)
is_active = Column(Boolean, default=True, nullable=False)
# SSL/TLS status (for monitoring)
ssl_status = Column(
String(50), default="pending"
) # pending, active, expired, error
ssl_verified_at = Column(DateTime(timezone=True), nullable=True)
# DNS verification (to confirm domain ownership)
verification_token = Column(String(100), unique=True, nullable=True)
is_verified = Column(Boolean, default=False, nullable=False)
verified_at = Column(DateTime(timezone=True), nullable=True)
# Platform association (for platform context resolution from custom domains)
platform_id = Column(
Integer,
ForeignKey("platforms.id", ondelete="SET NULL"),
nullable=True,
index=True,
comment="Platform this domain is associated with (for platform context resolution)",
)
# Relationships
store = relationship("Store", back_populates="domains")
platform = relationship("Platform")
# Constraints
__table_args__ = (
UniqueConstraint("store_id", "domain", name="uq_store_domain"),
Index("idx_domain_active", "domain", "is_active"),
Index("idx_store_domain_primary", "store_id", "is_primary"),
Index("idx_store_domain_platform", "platform_id"),
)
def __repr__(self):
return f"<StoreDomain(domain='{self.domain}', store_id={self.store_id})>"
@property
def full_url(self):
"""Return full URL with https"""
return f"https://{self.domain}"
@classmethod
def normalize_domain(cls, domain: str) -> str:
"""
Normalize domain for consistent storage.
Examples:
- https://example.com -> example.com
- www.example.com -> example.com
- EXAMPLE.COM -> example.com
"""
# Remove protocol
domain = domain.replace("https://", "").replace("http://", "") # SEC-034
# Remove trailing slash
domain = domain.rstrip("/")
# Remove www prefix (optional - depends on your preference)
# if domain.startswith("www."):
# domain = domain[4:]
# Convert to lowercase
domain = domain.lower()
return domain
__all__ = ["StoreDomain"]