Add Gmail API injection, provider presets, delivery method, and frontend wizard

- Add gmail_service.py with Gmail API users.messages.insert() for direct email injection
- Add DeliveryMethod enum and GmailCredential model to database models
- Add delivery_method field to MailAccount schema and model
- Create providers.py endpoint with 12 provider presets (Gmail, GMX, WEB.DE, Outlook, Yahoo, AOL, T-Online, 1&1/IONOS, Freenet, Posteo, mail.de, iCloud)
- Expand MailServerAutoDetect with 30+ domain mappings
- Update Celery tasks to prefer Gmail API injection, with SMTP fallback
- Add ProviderWizard.tsx frontend component for quick provider setup
- Update AddMailAccountModal.tsx with wizard integration
- Add Google API Python client libraries to requirements.txt
- Add Gmail API config settings
- Add unit tests for Gmail service and provider presets

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Agent-Logs-Url: https://github.com/christianlouis/pop_puller_to_gmail/sessions/de3ef930-a980-4958-8a9d-a2c802918e81
This commit is contained in:
copilot-swe-agent[bot]
2026-03-22 19:00:30 +00:00
parent 6435af3251
commit 40150da3a9
14 changed files with 1580 additions and 191 deletions
+39
View File
@@ -29,6 +29,12 @@ class MailProtocol(str, enum.Enum):
IMAP_SSL = "imap_ssl"
class DeliveryMethod(str, enum.Enum):
"""How emails are delivered to Gmail"""
SMTP = "smtp" # Forward via SMTP (legacy)
GMAIL_API = "gmail_api" # Inject via Gmail API (preferred)
class AccountStatus(str, enum.Enum):
"""Mail account status"""
ACTIVE = "active"
@@ -104,6 +110,9 @@ class MailAccount(Base):
# Forwarding destination
forward_to = Column(String(255), nullable=False)
# Delivery method
delivery_method = Column(SQLEnum(DeliveryMethod), default=DeliveryMethod.GMAIL_API)
# Status and settings
status = Column(SQLEnum(AccountStatus), default=AccountStatus.ACTIVE)
is_enabled = Column(Boolean, default=True)
@@ -326,3 +335,33 @@ class AuditLog(Base):
Index('idx_user_action', 'user_id', 'action'),
Index('idx_timestamp_action', 'timestamp', 'action'),
)
class GmailCredential(Base):
"""Stores OAuth2 credentials for Gmail API access (per-user)"""
__tablename__ = "gmail_credentials"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, unique=True)
# Gmail account email
gmail_email = Column(String(255), nullable=False)
# OAuth2 tokens (encrypted)
encrypted_access_token = Column(Text, nullable=False)
encrypted_refresh_token = Column(Text, nullable=True)
# Token metadata
token_expiry = Column(DateTime, nullable=True)
scopes = Column(JSON, nullable=True)
# Status
is_valid = Column(Boolean, default=True)
last_verified_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)
# Relationships
user = relationship("User", backref="gmail_credential")