from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager from flask_bcrypt import Bcrypt import redis import os from datetime import timedelta db = SQLAlchemy() bcrypt = Bcrypt() login_manager = LoginManager() redis_client = None def create_app(): app = Flask(__name__) # Configuration app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-production') app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///data/fina.db') app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size app.config['UPLOAD_FOLDER'] = os.path.abspath('uploads') app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) app.config['WTF_CSRF_TIME_LIMIT'] = None # Initialize extensions db.init_app(app) bcrypt.init_app(app) login_manager.init_app(app) login_manager.login_view = 'auth.login' # Redis connection global redis_client try: redis_url = os.environ.get('REDIS_URL', 'redis://localhost:6379/0') redis_client = redis.from_url(redis_url, decode_responses=True) except Exception as e: print(f"Redis connection failed: {e}") redis_client = None # Create upload directories os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) os.makedirs(os.path.join(app.config['UPLOAD_FOLDER'], 'documents'), exist_ok=True) os.makedirs(os.path.join(app.config['UPLOAD_FOLDER'], 'avatars'), exist_ok=True) os.makedirs('data', exist_ok=True) # Register blueprints from app.routes import auth, main, expenses, admin, documents, settings app.register_blueprint(auth.bp) app.register_blueprint(main.bp) app.register_blueprint(expenses.bp) app.register_blueprint(admin.bp) app.register_blueprint(documents.bp) app.register_blueprint(settings.bp) # Serve uploaded files from flask import send_from_directory, url_for @app.route('/uploads/') def uploaded_file(filename): """Serve uploaded files (avatars, documents)""" upload_dir = os.path.join(app.root_path, '..', app.config['UPLOAD_FOLDER']) return send_from_directory(upload_dir, filename) # Add avatar_url filter for templates @app.template_filter('avatar_url') def avatar_url_filter(avatar_path): """Generate correct URL for avatar (either static or uploaded)""" if avatar_path.startswith('icons/'): # Default avatar in static folder return url_for('static', filename=avatar_path) else: # Uploaded avatar return '/' + avatar_path # Create database tables with app.app_context(): db.create_all() return app from app.models import User @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id))