Add backend foundation: database models, security, and mail processing service
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
"""Models package"""
|
||||
from app.models.database_models import (
|
||||
User, MailAccount, ProcessingRun, ProcessingLog,
|
||||
NotificationConfig, MailServerPreset, SubscriptionPlan, AuditLog,
|
||||
SubscriptionTier, MailProtocol, AccountStatus, NotificationChannel
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"User", "MailAccount", "ProcessingRun", "ProcessingLog",
|
||||
"NotificationConfig", "MailServerPreset", "SubscriptionPlan", "AuditLog",
|
||||
"SubscriptionTier", "MailProtocol", "AccountStatus", "NotificationChannel"
|
||||
]
|
||||
@@ -0,0 +1,328 @@
|
||||
"""
|
||||
Database models for the multi-tenant POP3 forwarder application.
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from sqlalchemy import (
|
||||
Column, Integer, String, Boolean, DateTime, ForeignKey,
|
||||
Text, Enum as SQLEnum, JSON, Float, Index
|
||||
)
|
||||
from sqlalchemy.orm import relationship
|
||||
import enum
|
||||
|
||||
from app.core.database import Base
|
||||
|
||||
|
||||
class SubscriptionTier(str, enum.Enum):
|
||||
"""Subscription tier levels"""
|
||||
FREE = "free"
|
||||
BASIC = "basic"
|
||||
PRO = "pro"
|
||||
ENTERPRISE = "enterprise"
|
||||
|
||||
|
||||
class MailProtocol(str, enum.Enum):
|
||||
"""Supported mail protocols"""
|
||||
POP3 = "pop3"
|
||||
POP3_SSL = "pop3_ssl"
|
||||
IMAP = "imap"
|
||||
IMAP_SSL = "imap_ssl"
|
||||
|
||||
|
||||
class AccountStatus(str, enum.Enum):
|
||||
"""Mail account status"""
|
||||
ACTIVE = "active"
|
||||
INACTIVE = "inactive"
|
||||
ERROR = "error"
|
||||
TESTING = "testing"
|
||||
|
||||
|
||||
class NotificationChannel(str, enum.Enum):
|
||||
"""Notification channel types"""
|
||||
EMAIL = "email"
|
||||
TELEGRAM = "telegram"
|
||||
WEBHOOK = "webhook"
|
||||
SLACK = "slack"
|
||||
DISCORD = "discord"
|
||||
|
||||
|
||||
class User(Base):
|
||||
"""User model - represents a user account"""
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String(255), unique=True, index=True, nullable=False)
|
||||
hashed_password = Column(String(255), nullable=True) # Nullable for OAuth-only users
|
||||
full_name = Column(String(255))
|
||||
is_active = Column(Boolean, default=True)
|
||||
is_superuser = Column(Boolean, default=False)
|
||||
|
||||
# OAuth
|
||||
google_id = Column(String(255), unique=True, index=True, nullable=True)
|
||||
oauth_provider = Column(String(50), nullable=True)
|
||||
|
||||
# Subscription
|
||||
subscription_tier = Column(SQLEnum(SubscriptionTier), default=SubscriptionTier.FREE)
|
||||
subscription_status = Column(String(50), default="active") # active, canceled, past_due
|
||||
stripe_customer_id = Column(String(255), unique=True, nullable=True)
|
||||
stripe_subscription_id = Column(String(255), unique=True, nullable=True)
|
||||
subscription_expires_at = Column(DateTime, nullable=True)
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
last_login_at = Column(DateTime, nullable=True)
|
||||
|
||||
# Relationships
|
||||
mail_accounts = relationship("MailAccount", back_populates="user", cascade="all, delete-orphan")
|
||||
notifications = relationship("NotificationConfig", back_populates="user", cascade="all, delete-orphan")
|
||||
logs = relationship("ProcessingLog", back_populates="user", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class MailAccount(Base):
|
||||
"""Mail account configuration (POP3/IMAP)"""
|
||||
__tablename__ = "mail_accounts"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
# Account details
|
||||
name = Column(String(255), nullable=False) # User-friendly name
|
||||
email_address = Column(String(255), nullable=False)
|
||||
|
||||
# Server configuration
|
||||
protocol = Column(SQLEnum(MailProtocol), default=MailProtocol.POP3_SSL)
|
||||
host = Column(String(255), nullable=False)
|
||||
port = Column(Integer, nullable=False)
|
||||
use_ssl = Column(Boolean, default=True)
|
||||
use_tls = Column(Boolean, default=False)
|
||||
|
||||
# Credentials (encrypted)
|
||||
username = Column(String(255), nullable=False)
|
||||
encrypted_password = Column(Text, nullable=False)
|
||||
|
||||
# Forwarding destination
|
||||
forward_to = Column(String(255), nullable=False)
|
||||
|
||||
# Status and settings
|
||||
status = Column(SQLEnum(AccountStatus), default=AccountStatus.ACTIVE)
|
||||
is_enabled = Column(Boolean, default=True)
|
||||
check_interval_minutes = Column(Integer, default=5)
|
||||
max_emails_per_check = Column(Integer, default=50)
|
||||
delete_after_forward = Column(Boolean, default=True)
|
||||
|
||||
# Auto-detection metadata
|
||||
provider_name = Column(String(100), nullable=True) # e.g., "Gmail", "GMX"
|
||||
auto_detected = Column(Boolean, default=False)
|
||||
|
||||
# Statistics
|
||||
total_emails_processed = Column(Integer, default=0)
|
||||
total_emails_failed = Column(Integer, default=0)
|
||||
last_check_at = Column(DateTime, nullable=True)
|
||||
last_successful_check_at = Column(DateTime, nullable=True)
|
||||
last_error_at = Column(DateTime, nullable=True)
|
||||
last_error_message = Column(Text, nullable=True)
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
# Relationships
|
||||
user = relationship("User", back_populates="mail_accounts")
|
||||
processing_runs = relationship("ProcessingRun", back_populates="mail_account", cascade="all, delete-orphan")
|
||||
|
||||
# Indexes
|
||||
__table_args__ = (
|
||||
Index('idx_user_email', 'user_id', 'email_address'),
|
||||
Index('idx_status_enabled', 'status', 'is_enabled'),
|
||||
)
|
||||
|
||||
|
||||
class ProcessingRun(Base):
|
||||
"""Records of email processing runs for each mail account"""
|
||||
__tablename__ = "processing_runs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
mail_account_id = Column(Integer, ForeignKey("mail_accounts.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
# Run details
|
||||
started_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
completed_at = Column(DateTime, nullable=True)
|
||||
duration_seconds = Column(Float, nullable=True)
|
||||
|
||||
# Results
|
||||
emails_fetched = Column(Integer, default=0)
|
||||
emails_forwarded = Column(Integer, default=0)
|
||||
emails_failed = Column(Integer, default=0)
|
||||
|
||||
# Status
|
||||
status = Column(String(50), default="running") # running, completed, failed
|
||||
error_message = Column(Text, nullable=True)
|
||||
|
||||
# Relationships
|
||||
mail_account = relationship("MailAccount", back_populates="processing_runs")
|
||||
|
||||
# Indexes
|
||||
__table_args__ = (
|
||||
Index('idx_account_started', 'mail_account_id', 'started_at'),
|
||||
)
|
||||
|
||||
|
||||
class ProcessingLog(Base):
|
||||
"""Detailed logs of individual email processing attempts"""
|
||||
__tablename__ = "processing_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
mail_account_id = Column(Integer, ForeignKey("mail_accounts.id", ondelete="CASCADE"), nullable=False)
|
||||
processing_run_id = Column(Integer, ForeignKey("processing_runs.id", ondelete="CASCADE"), nullable=True)
|
||||
|
||||
# Log details
|
||||
timestamp = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
|
||||
level = Column(String(20), nullable=False) # INFO, WARNING, ERROR
|
||||
message = Column(Text, nullable=False)
|
||||
|
||||
# Email metadata (if applicable)
|
||||
email_subject = Column(String(500), nullable=True)
|
||||
email_from = Column(String(255), nullable=True)
|
||||
email_size_bytes = Column(Integer, nullable=True)
|
||||
|
||||
# Status
|
||||
success = Column(Boolean, default=True)
|
||||
error_details = Column(JSON, nullable=True)
|
||||
|
||||
# Relationships
|
||||
user = relationship("User", back_populates="logs")
|
||||
|
||||
# Indexes
|
||||
__table_args__ = (
|
||||
Index('idx_user_timestamp', 'user_id', 'timestamp'),
|
||||
Index('idx_account_timestamp', 'mail_account_id', 'timestamp'),
|
||||
)
|
||||
|
||||
|
||||
class NotificationConfig(Base):
|
||||
"""User notification channel configurations"""
|
||||
__tablename__ = "notification_configs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
|
||||
|
||||
# Channel details
|
||||
channel = Column(SQLEnum(NotificationChannel), nullable=False)
|
||||
is_enabled = Column(Boolean, default=True)
|
||||
|
||||
# Channel-specific configuration (stored as JSON)
|
||||
config = Column(JSON, nullable=False)
|
||||
# Examples:
|
||||
# EMAIL: {"address": "user@example.com"}
|
||||
# TELEGRAM: {"bot_token": "xxx", "chat_id": "yyy"}
|
||||
# WEBHOOK: {"url": "https://example.com/webhook", "headers": {...}}
|
||||
|
||||
# Notification preferences
|
||||
notify_on_errors = Column(Boolean, default=True)
|
||||
notify_on_success = Column(Boolean, default=False)
|
||||
notify_threshold = Column(Integer, default=3) # Notify after N consecutive errors
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
# Relationships
|
||||
user = relationship("User", back_populates="notifications")
|
||||
|
||||
# Indexes
|
||||
__table_args__ = (
|
||||
Index('idx_user_channel', 'user_id', 'channel'),
|
||||
)
|
||||
|
||||
|
||||
class MailServerPreset(Base):
|
||||
"""Predefined mail server configurations for common providers"""
|
||||
__tablename__ = "mail_server_presets"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Provider info
|
||||
provider_name = Column(String(100), unique=True, nullable=False, index=True)
|
||||
provider_domain = Column(String(255), nullable=False) # e.g., "gmail.com"
|
||||
|
||||
# Server configurations (can have multiple protocols)
|
||||
configs = Column(JSON, nullable=False)
|
||||
# Example:
|
||||
# {
|
||||
# "pop3_ssl": {"host": "pop.gmail.com", "port": 995, "ssl": true},
|
||||
# "imap_ssl": {"host": "imap.gmail.com", "port": 993, "ssl": true}
|
||||
# }
|
||||
|
||||
# Metadata
|
||||
is_verified = Column(Boolean, default=False)
|
||||
popularity_score = Column(Integer, default=0) # For sorting recommendations
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
|
||||
class SubscriptionPlan(Base):
|
||||
"""Available subscription plans and their features"""
|
||||
__tablename__ = "subscription_plans"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Plan details
|
||||
tier = Column(SQLEnum(SubscriptionTier), unique=True, nullable=False)
|
||||
name = Column(String(100), nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
|
||||
# Pricing
|
||||
price_monthly = Column(Float, nullable=False)
|
||||
price_yearly = Column(Float, nullable=True)
|
||||
|
||||
# Stripe integration
|
||||
stripe_price_id_monthly = Column(String(255), nullable=True)
|
||||
stripe_price_id_yearly = Column(String(255), nullable=True)
|
||||
|
||||
# Features/Limits
|
||||
max_mail_accounts = Column(Integer, nullable=False)
|
||||
max_emails_per_day = Column(Integer, nullable=False)
|
||||
check_interval_minutes = Column(Integer, nullable=False)
|
||||
support_level = Column(String(50), default="community") # community, email, priority
|
||||
features = Column(JSON, nullable=True) # Additional features as JSON
|
||||
|
||||
# Status
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
|
||||
class AuditLog(Base):
|
||||
"""Audit trail for security and compliance"""
|
||||
__tablename__ = "audit_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Who
|
||||
user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
||||
user_email = Column(String(255), nullable=True) # Cached for deleted users
|
||||
ip_address = Column(String(45), nullable=True) # IPv4 or IPv6
|
||||
|
||||
# What
|
||||
action = Column(String(100), nullable=False, index=True)
|
||||
resource_type = Column(String(50), nullable=True)
|
||||
resource_id = Column(Integer, nullable=True)
|
||||
|
||||
# Details
|
||||
details = Column(JSON, nullable=True)
|
||||
status = Column(String(20), default="success") # success, failure
|
||||
|
||||
# When
|
||||
timestamp = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
|
||||
|
||||
# Indexes
|
||||
__table_args__ = (
|
||||
Index('idx_user_action', 'user_id', 'action'),
|
||||
Index('idx_timestamp_action', 'timestamp', 'action'),
|
||||
)
|
||||
@@ -0,0 +1,304 @@
|
||||
"""
|
||||
Pydantic schemas for API request/response validation.
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, Any, List
|
||||
from pydantic import BaseModel, EmailStr, Field, validator
|
||||
from enum import Enum
|
||||
|
||||
|
||||
# Enums matching database models
|
||||
class SubscriptionTier(str, Enum):
|
||||
FREE = "free"
|
||||
BASIC = "basic"
|
||||
PRO = "pro"
|
||||
ENTERPRISE = "enterprise"
|
||||
|
||||
|
||||
class MailProtocol(str, Enum):
|
||||
POP3 = "pop3"
|
||||
POP3_SSL = "pop3_ssl"
|
||||
IMAP = "imap"
|
||||
IMAP_SSL = "imap_ssl"
|
||||
|
||||
|
||||
class AccountStatus(str, Enum):
|
||||
ACTIVE = "active"
|
||||
INACTIVE = "inactive"
|
||||
ERROR = "error"
|
||||
TESTING = "testing"
|
||||
|
||||
|
||||
class NotificationChannel(str, Enum):
|
||||
EMAIL = "email"
|
||||
TELEGRAM = "telegram"
|
||||
WEBHOOK = "webhook"
|
||||
SLACK = "slack"
|
||||
DISCORD = "discord"
|
||||
|
||||
|
||||
# User Schemas
|
||||
class UserBase(BaseModel):
|
||||
email: EmailStr
|
||||
full_name: Optional[str] = None
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
password: Optional[str] = None
|
||||
|
||||
|
||||
class UserUpdate(BaseModel):
|
||||
full_name: Optional[str] = None
|
||||
email: Optional[EmailStr] = None
|
||||
|
||||
|
||||
class UserResponse(UserBase):
|
||||
id: int
|
||||
is_active: bool
|
||||
subscription_tier: SubscriptionTier
|
||||
subscription_status: str
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class UserDetailResponse(UserResponse):
|
||||
google_id: Optional[str] = None
|
||||
oauth_provider: Optional[str] = None
|
||||
stripe_customer_id: Optional[str] = None
|
||||
subscription_expires_at: Optional[datetime] = None
|
||||
last_login_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Authentication Schemas
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
token_type: str = "bearer"
|
||||
|
||||
|
||||
class TokenPayload(BaseModel):
|
||||
sub: Optional[int] = None
|
||||
exp: Optional[int] = None
|
||||
type: Optional[str] = None
|
||||
|
||||
|
||||
class GoogleAuthRequest(BaseModel):
|
||||
code: str
|
||||
redirect_uri: str
|
||||
|
||||
|
||||
# Mail Account Schemas
|
||||
class MailAccountBase(BaseModel):
|
||||
name: str = Field(..., max_length=255)
|
||||
email_address: EmailStr
|
||||
protocol: MailProtocol = MailProtocol.POP3_SSL
|
||||
host: str = Field(..., max_length=255)
|
||||
port: int = Field(..., gt=0, lt=65536)
|
||||
use_ssl: bool = True
|
||||
use_tls: bool = False
|
||||
username: str = Field(..., max_length=255)
|
||||
forward_to: EmailStr
|
||||
is_enabled: bool = True
|
||||
check_interval_minutes: int = Field(default=5, gt=0, le=1440)
|
||||
max_emails_per_check: int = Field(default=50, gt=0, le=1000)
|
||||
delete_after_forward: bool = True
|
||||
|
||||
|
||||
class MailAccountCreate(MailAccountBase):
|
||||
password: str # Will be encrypted before storage
|
||||
|
||||
|
||||
class MailAccountUpdate(BaseModel):
|
||||
name: Optional[str] = Field(None, max_length=255)
|
||||
password: Optional[str] = None
|
||||
forward_to: Optional[EmailStr] = None
|
||||
is_enabled: Optional[bool] = None
|
||||
check_interval_minutes: Optional[int] = Field(None, gt=0, le=1440)
|
||||
max_emails_per_check: Optional[int] = Field(None, gt=0, le=1000)
|
||||
delete_after_forward: Optional[bool] = None
|
||||
|
||||
|
||||
class MailAccountResponse(MailAccountBase):
|
||||
id: int
|
||||
user_id: int
|
||||
status: AccountStatus
|
||||
provider_name: Optional[str] = None
|
||||
auto_detected: bool
|
||||
total_emails_processed: int
|
||||
total_emails_failed: int
|
||||
last_check_at: Optional[datetime] = None
|
||||
last_successful_check_at: Optional[datetime] = None
|
||||
last_error_at: Optional[datetime] = None
|
||||
last_error_message: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
# Don't expose password or username in responses
|
||||
password: str = Field(exclude=True, default="")
|
||||
username: str = Field(exclude=True, default="")
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class MailAccountTestRequest(BaseModel):
|
||||
"""Test connection to mail server"""
|
||||
host: str
|
||||
port: int
|
||||
protocol: MailProtocol
|
||||
username: str
|
||||
password: str
|
||||
use_ssl: bool = True
|
||||
use_tls: bool = False
|
||||
|
||||
|
||||
class MailAccountTestResponse(BaseModel):
|
||||
success: bool
|
||||
message: str
|
||||
details: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class MailAccountAutoDetectRequest(BaseModel):
|
||||
"""Auto-detect mail server settings"""
|
||||
email_address: EmailStr
|
||||
|
||||
|
||||
class MailAccountAutoDetectResponse(BaseModel):
|
||||
success: bool
|
||||
suggestions: List[Dict[str, Any]]
|
||||
|
||||
|
||||
# Processing Run Schemas
|
||||
class ProcessingRunResponse(BaseModel):
|
||||
id: int
|
||||
mail_account_id: int
|
||||
started_at: datetime
|
||||
completed_at: Optional[datetime] = None
|
||||
duration_seconds: Optional[float] = None
|
||||
emails_fetched: int
|
||||
emails_forwarded: int
|
||||
emails_failed: int
|
||||
status: str
|
||||
error_message: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Processing Log Schemas
|
||||
class ProcessingLogResponse(BaseModel):
|
||||
id: int
|
||||
timestamp: datetime
|
||||
level: str
|
||||
message: str
|
||||
email_subject: Optional[str] = None
|
||||
email_from: Optional[str] = None
|
||||
success: bool
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Notification Config Schemas
|
||||
class NotificationConfigBase(BaseModel):
|
||||
channel: NotificationChannel
|
||||
is_enabled: bool = True
|
||||
config: Dict[str, Any]
|
||||
notify_on_errors: bool = True
|
||||
notify_on_success: bool = False
|
||||
notify_threshold: int = Field(default=3, gt=0, le=100)
|
||||
|
||||
|
||||
class NotificationConfigCreate(NotificationConfigBase):
|
||||
pass
|
||||
|
||||
|
||||
class NotificationConfigUpdate(BaseModel):
|
||||
is_enabled: Optional[bool] = None
|
||||
config: Optional[Dict[str, Any]] = None
|
||||
notify_on_errors: Optional[bool] = None
|
||||
notify_on_success: Optional[bool] = None
|
||||
notify_threshold: Optional[int] = Field(None, gt=0, le=100)
|
||||
|
||||
|
||||
class NotificationConfigResponse(NotificationConfigBase):
|
||||
id: int
|
||||
user_id: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Subscription Schemas
|
||||
class SubscriptionPlanResponse(BaseModel):
|
||||
id: int
|
||||
tier: SubscriptionTier
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
price_monthly: float
|
||||
price_yearly: Optional[float] = None
|
||||
max_mail_accounts: int
|
||||
max_emails_per_day: int
|
||||
check_interval_minutes: int
|
||||
support_level: str
|
||||
features: Optional[Dict[str, Any]] = None
|
||||
is_active: bool
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class SubscriptionCheckoutRequest(BaseModel):
|
||||
tier: SubscriptionTier
|
||||
billing_period: str = Field(..., pattern="^(monthly|yearly)$")
|
||||
success_url: str
|
||||
cancel_url: str
|
||||
|
||||
|
||||
class SubscriptionCheckoutResponse(BaseModel):
|
||||
checkout_url: str
|
||||
session_id: str
|
||||
|
||||
|
||||
# Statistics Schemas
|
||||
class AccountStatistics(BaseModel):
|
||||
total_accounts: int
|
||||
active_accounts: int
|
||||
inactive_accounts: int
|
||||
error_accounts: int
|
||||
total_emails_processed: int
|
||||
total_emails_failed: int
|
||||
|
||||
|
||||
class ProcessingStatistics(BaseModel):
|
||||
last_24h_processed: int
|
||||
last_24h_failed: int
|
||||
last_7d_processed: int
|
||||
last_7d_failed: int
|
||||
success_rate: float
|
||||
|
||||
|
||||
class DashboardStatistics(BaseModel):
|
||||
account_stats: AccountStatistics
|
||||
processing_stats: ProcessingStatistics
|
||||
recent_runs: List[ProcessingRunResponse]
|
||||
recent_errors: List[ProcessingLogResponse]
|
||||
|
||||
|
||||
# Mail Server Preset Schemas
|
||||
class MailServerPresetResponse(BaseModel):
|
||||
id: int
|
||||
provider_name: str
|
||||
provider_domain: str
|
||||
configs: Dict[str, Any]
|
||||
is_verified: bool
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
Reference in New Issue
Block a user