7:from datetime import datetime, timezone 8:from sqlalchemy import (JSON, Boolean, Column, DateTime, Enum, Float, ForeignKey, 10:from sqlalchemy.orm import DeclarativeBase, relationship 12:from app.core import encryption 19:class Base(DeclarativeBase): 23:class TimestampMixin: 28:class User(Base, TimestampMixin): 42:class OAuthClientConfig(Base, TimestampMixin): 64:class GmailAccount(Base, TimestampMixin): 106:class OAuthToken(Base, TimestampMixin): 141:class RecipientList(Base, TimestampMixin): 153:class Recipient(Base, TimestampMixin): 176:class SuppressionEntry(Base, TimestampMixin): 186:class SubjectLine(Base, TimestampMixin): 195:class EmailTemplate(Base, TimestampMixin): 208:class Campaign(Base, TimestampMixin): 256:class CampaignRecipient(Base, TimestampMixin): 288:class Attachment(Base, TimestampMixin): 307:class CompanyPhoneNumber(Base, TimestampMixin): 317:class SendJob(Base, TimestampMixin): 335:class SendAttempt(Base, TimestampMixin): 352: error_class = Column(String(60), default="") # temporary|permanent|auth|quota|unknown 357:class EmailEvent(Base, TimestampMixin): 370:class AuditLog(Base, TimestampMixin): 383:class ApplicationSettings(Base, TimestampMixin): ===ATTACH SVC=== 2:import hashlib 3:import os 4:import re 5:import secrets 7:from app.core.config import settings 19:def allowed_ext(filename: str, allowed: str = None) -> bool: 25:def safe_join(rel_path: str) -> str: 34:def save_upload(filename: str, content: bytes) -> dict: 50:def read_attachment_bytes(att) -> bytes | None: 58:def delete_attachment_file(att) -> None: 65:def scan_for_malware(content: bytes, filename: str) -> tuple: ===CAMP SVC=== 14:async def start_campaign(db: AsyncSession, campaign: Campaign) -> dict: 46:async def pause_campaign(db: AsyncSession, campaign: Campaign) -> dict: 52:async def resume_campaign(db: AsyncSession, campaign: Campaign) -> dict: 56:async def cancel_campaign(db: AsyncSession, campaign: Campaign) -> dict: ===CE SVC=== 19:def utcnow(): 23:class RotationAssigner: 26: def __init__(self, accounts: list, strategy: str, weights: dict = None, max_per_account: int = 200): 37: def _available(self): 47: def next_account(self, account_id=None): 85: def status(self): 90:class SubjectSelector: 91: def __init__(self, subjects: List[SubjectLine], strategy: str = "round_robin"): 96: def next_subject(self) -> Optional[SubjectLine]: 108:class TemplateSequencer: 110: def __init__(self, templates: List[EmailTemplate], mode: str = "repeat"): 116: def next_template(self) -> Optional[EmailTemplate]: 133: def stopped(self): 137:class PhoneSelector: 138: def __init__(self, phones: List[CompanyPhoneNumber], mode: str = "round_robin"): 143: def next_phone(self) -> str: 155:async def get_setting(db: AsyncSession, key: str, default): 163:def get_setting_sync(key: str, default): 176:async def get_campaign_entities(db: AsyncSession, campaign: Campaign): 201:async def materialize_campaign(db: AsyncSession, campaign: Campaign, recipient_rows: Sequence[Recipient],