|
7 | 7 | import json |
8 | 8 | import datetime |
9 | 9 | import stripe |
| 10 | +import logging |
10 | 11 | from functools import wraps |
11 | | -from flask import Flask, render_template, request, redirect, url_for, jsonify, flash |
| 12 | +from flask import Flask, render_template, request, redirect, url_for, jsonify, flash, send_from_directory |
12 | 13 | from flask_login import LoginManager, current_user, login_required, login_user, logout_user, UserMixin |
13 | 14 | from flask_sqlalchemy import SQLAlchemy |
14 | 15 | from dotenv import load_dotenv |
15 | 16 | from botocore.client import Config |
16 | 17 |
|
| 18 | +# Reduce AWS SDK logging noise |
| 19 | +logging.getLogger('botocore').setLevel(logging.WARNING) |
| 20 | +logging.getLogger('boto3').setLevel(logging.WARNING) |
| 21 | +logging.getLogger('urllib3').setLevel(logging.WARNING) |
| 22 | + |
17 | 23 | # --- 1. Initialize Extensions (globally) --- |
18 | 24 | db = SQLAlchemy() |
19 | 25 | login_manager = LoginManager() |
@@ -43,6 +49,15 @@ def create_app(): |
43 | 49 | app.secret_key = os.environ.get('SECRET_KEY') |
44 | 50 | app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL') |
45 | 51 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False |
| 52 | + # Add connection pool settings for better reliability |
| 53 | + app.config['SQLALCHEMY_ENGINE_OPTIONS'] = { |
| 54 | + 'pool_pre_ping': True, # Verify connections before use |
| 55 | + 'pool_recycle': 300, # Recycle connections every 5 minutes |
| 56 | + 'connect_args': { |
| 57 | + 'connect_timeout': 10, |
| 58 | + 'sslmode': 'require' |
| 59 | + } |
| 60 | + } |
46 | 61 | # Stripe configuration |
47 | 62 | stripe.api_key = os.environ.get('STRIPE_SECRET_KEY') |
48 | 63 | STRIPE_WEBHOOK_SECRET = os.environ.get('STRIPE_WEBHOOK_SECRET') |
@@ -196,18 +211,36 @@ def callback(): |
196 | 211 |
|
197 | 212 | print(f"Creating/finding user: {users_email}") |
198 | 213 |
|
199 | | - user = User.query.filter_by(google_id=unique_id).first() |
200 | | - if not user: |
201 | | - print("Creating new user") |
202 | | - user = User(google_id=unique_id, name=users_name, email=users_email) |
203 | | - db.session.add(user) |
204 | | - db.session.commit() |
205 | | - else: |
206 | | - print("User found, logging in") |
207 | | - |
208 | | - login_user(user) |
209 | | - print("User logged in successfully, redirecting to index") |
210 | | - return redirect(url_for("index")) |
| 214 | + # Database operations with retry logic for connection issues |
| 215 | + max_retries = 3 |
| 216 | + for attempt in range(max_retries): |
| 217 | + try: |
| 218 | + user = User.query.filter_by(google_id=unique_id).first() |
| 219 | + if not user: |
| 220 | + print("Creating new user") |
| 221 | + user = User(google_id=unique_id, name=users_name, email=users_email) |
| 222 | + db.session.add(user) |
| 223 | + db.session.commit() |
| 224 | + else: |
| 225 | + print("User found, logging in") |
| 226 | + |
| 227 | + login_user(user) |
| 228 | + print("User logged in successfully, redirecting to index") |
| 229 | + return redirect(url_for("index")) |
| 230 | + |
| 231 | + except Exception as db_error: |
| 232 | + print(f"Database attempt {attempt + 1} failed: {db_error}") |
| 233 | + if attempt < max_retries - 1: |
| 234 | + # Rollback and retry |
| 235 | + db.session.rollback() |
| 236 | + import time |
| 237 | + time.sleep(1) # Wait 1 second before retry |
| 238 | + continue |
| 239 | + else: |
| 240 | + # Final attempt failed |
| 241 | + db.session.rollback() |
| 242 | + print(f"All database attempts failed for user {users_email}") |
| 243 | + return "Database temporarily unavailable. Please try signing in again in a moment.", 503 |
211 | 244 |
|
212 | 245 | print("Email not verified by Google") |
213 | 246 | return "User email not available or not verified by Google.", 400 |
@@ -243,6 +276,17 @@ def decorated_function(*args, **kwargs): |
243 | 276 | return decorated_function |
244 | 277 |
|
245 | 278 | # --- Core Application Routes --- |
| 279 | + @app.route('/favicon.ico') |
| 280 | + def favicon(): |
| 281 | + """Serve favicon from root directory""" |
| 282 | + try: |
| 283 | + # Try to serve from root directory (one level up from api/) |
| 284 | + return send_from_directory(os.path.join(app.root_path, '..'), 'favicon.ico', mimetype='image/vnd.microsoft.icon') |
| 285 | + except Exception as e: |
| 286 | + print(f"Favicon error: {e}") |
| 287 | + # Return a 204 No Content response to prevent browser errors |
| 288 | + return '', 204 |
| 289 | + |
246 | 290 | @app.route('/') |
247 | 291 | @login_required |
248 | 292 | def index(): |
|
0 commit comments