pounce/backend/app/main.py
yves.gugger 6b6ec01484 feat: Add missing features - Stripe payments, password reset, rate limiting, contact form, newsletter
Backend:
- Add Stripe API endpoints (checkout, portal, webhook) in subscription.py
- Add password reset (forgot-password, reset-password) in auth.py
- Add email verification endpoints
- Add rate limiting with slowapi
- Add contact form and newsletter API (contact.py)
- Add webhook endpoint for Stripe (webhooks.py)
- Add NewsletterSubscriber model
- Extend User model with password reset and email verification tokens
- Extend email_service with new templates (password reset, verification, contact, newsletter)
- Update env.example with all new environment variables

Frontend:
- Add /forgot-password page
- Add /reset-password page with token handling
- Add /verify-email page with auto-verification
- Add forgot password link to login page
- Connect contact form to API
- Add API methods for all new endpoints

Documentation:
- Update README with new API endpoints
- Update environment variables documentation
- Update pages overview
2025-12-08 14:37:42 +01:00

169 lines
4.2 KiB
Python

"""FastAPI application entry point."""
import logging
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
from app.api import api_router
from app.config import get_settings
from app.database import init_db
from app.scheduler import start_scheduler, stop_scheduler
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
settings = get_settings()
# Rate limiter configuration
limiter = Limiter(
key_func=get_remote_address,
default_limits=["200/minute"], # Global default
storage_uri="memory://", # In-memory storage (use Redis in production)
)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan handler."""
# Startup
logger.info(f"Starting {settings.app_name}...")
# Initialize database
await init_db()
logger.info("Database initialized")
# Start scheduler
start_scheduler()
logger.info("Scheduler started")
yield
# Shutdown
stop_scheduler()
logger.info("Application shutdown complete")
# Create FastAPI application
app = FastAPI(
title=settings.app_name,
description="""
# pounce API
Domain availability monitoring and portfolio management service.
## Features
- **Domain Monitoring**: Track domains and get notified when they become available
- **TLD Pricing**: Real-time TLD price comparison across registrars
- **Portfolio Management**: Track your domain investments and valuations
- **Smart Pounce Auctions**: Find undervalued domains in auctions
## Authentication
Most endpoints require authentication via Bearer token.
Get a token via POST /api/v1/auth/login
## Rate Limits
- Default: 200 requests/minute per IP
- Auth endpoints: 10 requests/minute
- Contact form: 5 requests/hour
## Support
For API issues, contact support@pounce.ch
""",
version="1.0.0",
lifespan=lifespan,
redirect_slashes=False,
docs_url="/docs",
redoc_url="/redoc",
)
# Add rate limiter to app state
app.state.limiter = limiter
# Custom rate limit exceeded handler
@app.exception_handler(RateLimitExceeded)
async def rate_limit_handler(request: Request, exc: RateLimitExceeded):
return JSONResponse(
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
content={
"error": "rate_limit_exceeded",
"detail": "Too many requests. Please slow down.",
"retry_after": exc.detail,
},
)
# Get allowed origins from environment
ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "").split(",")
if not ALLOWED_ORIGINS or ALLOWED_ORIGINS == [""]:
ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://10.42.0.73:3000",
]
# Add production origins
SITE_URL = os.getenv("SITE_URL", "")
if SITE_URL and SITE_URL not in ALLOWED_ORIGINS:
ALLOWED_ORIGINS.append(SITE_URL)
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include API routes
app.include_router(api_router, prefix="/api/v1")
@app.get("/")
async def root():
"""Root endpoint - API info."""
return {
"name": settings.app_name,
"version": "1.0.0",
"status": "running",
"docs": "/docs",
"health": "/health",
}
@app.get("/health")
async def health_check():
"""Health check endpoint for monitoring."""
return {
"status": "healthy",
"service": settings.app_name,
"version": "1.0.0",
}
# Rate-limited endpoints - apply specific limits to sensitive routes
from fastapi import Depends
@app.middleware("http")
async def add_rate_limit_headers(request: Request, call_next):
"""Add rate limit info to response headers."""
response = await call_next(request)
# Add CORS headers for rate limit info
response.headers["X-RateLimit-Policy"] = "200/minute"
return response