feat: Add OAuth email integration and production deployment config

- Add OAuthCredential model for storing Google/Microsoft OAuth tokens
- Add email provider auto-detection endpoint (Gmail, Outlook, Yahoo, etc.)
- Add EmailConfigWizard frontend component with step-by-step setup
- Add OAuth flow endpoints for Google and Microsoft XOAUTH2
- Update production settings to make AWS, Sentry, Mailgun optional
- Update Traefik config for wildcard subdomain routing
- Add logo resize utility script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-11-29 21:26:17 -05:00
parent cfc1b36ada
commit 7b0cf62019
22 changed files with 3075 additions and 96 deletions

65
resize_logo.py Normal file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""
Script to resize the SmoothSchedule logo to 120x120 pixels.
Maintains aspect ratio and centers on a transparent canvas.
Usage: python resize_logo.py
"""
from PIL import Image
from pathlib import Path
# Paths
PROJECT_ROOT = Path(__file__).parent
SOURCE_LOGO = PROJECT_ROOT / "frontend" / "src" / "assets" / "smooth_schedule_icon.png"
OUTPUT_LOGO = PROJECT_ROOT / "smooth_schedule_logo_120x120.png"
def resize_logo(source: Path, output: Path, size: tuple = (120, 120)):
"""Resize logo to fit within specified dimensions, maintaining aspect ratio."""
if not source.exists():
print(f"Error: Source logo not found at {source}")
return False
try:
with Image.open(source) as img:
# Convert to RGBA if needed (preserves transparency)
if img.mode != 'RGBA':
img = img.convert('RGBA')
original_size = img.size
# Calculate scaling to fit within the target size while maintaining aspect ratio
width_ratio = size[0] / img.width
height_ratio = size[1] / img.height
scale = min(width_ratio, height_ratio)
new_width = int(img.width * scale)
new_height = int(img.height * scale)
# Resize maintaining aspect ratio
resized = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
# Create a transparent canvas of the target size
canvas = Image.new('RGBA', size, (0, 0, 0, 0))
# Center the resized image on the canvas
x_offset = (size[0] - new_width) // 2
y_offset = (size[1] - new_height) // 2
canvas.paste(resized, (x_offset, y_offset))
# Save the final image
canvas.save(output, 'PNG', optimize=True)
print(f"Successfully resized logo:")
print(f" Source: {source}")
print(f" Output: {output}")
print(f" Original size: {original_size}")
print(f" Scaled size: {new_width}x{new_height}")
print(f" Canvas size: {size}")
return True
except Exception as e:
print(f"Error resizing logo: {e}")
return False
if __name__ == "__main__":
resize_logo(SOURCE_LOGO, OUTPUT_LOGO)