import os from flask import Flask, session, request from sqlalchemy import inspect from dotenv import load_dotenv # 1. Загрузка переменных окружения - в самом верху load_dotenv() # 2. Импорт расширений из центрального файла from extensions import scheduler, db, migrate, login_manager, babel from models import init_encryption # 3. Фабрика приложений def create_app(): """ Создает и конфигурирует экземпляр Flask приложения. """ app = Flask(__name__) if not app.debug: import logging from logging.handlers import RotatingFileHandler # Создаем папку для логов, если ее нет log_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data') os.makedirs(log_dir, exist_ok=True) log_file = os.path.join(log_dir, 'olaper.log') # Создаем обработчик, который пишет логи в файл с ротацией # 10 МБ на файл, храним 5 старых файлов file_handler = RotatingFileHandler(log_file, maxBytes=1024*1024*10, backupCount=5, encoding='utf-8') # Устанавливаем формат сообщений file_handler.setFormatter(logging.Formatter( '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' )) # Устанавливаем уровень логирования file_handler.setLevel(logging.INFO) # Добавляем обработчик к логгеру приложения app.logger.addHandler(file_handler) app.logger.setLevel(logging.INFO) app.logger.info('Application startup') # --- Конфигурация приложения --- app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-super-secret-key-for-dev') # --- НАДЕЖНАЯ НАСТРОЙКА ПУТЕЙ --- # Получаем абсолютный путь к директории, где находится app.py basedir = os.path.abspath(os.path.dirname(__file__)) # Устанавливаем путь к папке data data_dir = os.path.join(basedir, os.environ.get('DATA_DIR', 'data')) # Создаем эту директорию, если ее не существует. Это ключевой момент. os.makedirs(data_dir, exist_ok=True) app.config['DATA_DIR'] = data_dir # Устанавливаем путь к БД db_path = os.path.join(data_dir, 'app.db') app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', f"sqlite:///{db_path}") app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['BABEL_DEFAULT_LOCALE'] = 'ru' app.config['ENCRYPTION_KEY'] = os.environ.get('ENCRYPTION_KEY') # --- Определяем селектор языка --- def get_locale(): if 'language' in session: return session['language'] return request.accept_languages.best_match(['ru', 'en']) # --- Инициализация расширений с приложением --- db.init_app(app) migrate.init_app(app, db) login_manager.init_app(app) babel.init_app(app, locale_selector=get_locale) scheduler.init_app(app) init_encryption(app) # --- Регистрация блюпринтов --- from routes import main_bp, execute_olap_export app.register_blueprint(main_bp) login_manager.login_view = 'main.login' login_manager.login_message = "Пожалуйста, войдите, чтобы получить доступ к этой странице." login_manager.login_message_category = "info" with app.app_context(): if inspect(db.engine).has_table('user_config'): from models import User, UserConfig from utils import _parse_cron_string all_configs = UserConfig.query.all() for config in all_configs: user_id = config.user_id mappings = config.mappings for sheet_title, params in mappings.items(): if isinstance(params, dict): cron_schedule = params.get('schedule_cron') if cron_schedule: job_id = f"user_{user_id}_sheet_{sheet_title}" try: if not scheduler.get_job(job_id): scheduler.add_job( id=job_id, func=execute_olap_export, trigger='cron', args=[app, user_id, sheet_title], **_parse_cron_string(cron_schedule) ) app.logger.info(f"Scheduled job loaded on startup: {job_id} with schedule '{cron_schedule}'") except Exception as e: app.logger.error(f"Failed to load job {job_id} with schedule '{cron_schedule}': {e}") else: app.logger.warning("Database tables not found. Skipping job loading on startup. Run 'flask init-db' to create the tables.") scheduler.start() # --- Регистрация команд CLI --- from models import User, UserConfig @app.cli.command('init-db') def init_db_command(): """Создает или пересоздает таблицы в базе данных.""" print("Creating database tables...") db.create_all() print("Database tables created successfully.") return app # --- Точка входа для запуска --- app = create_app() if __name__ == '__main__': # Для прямого запуска через `python app.py` (удобно для отладки) app.run(host='0.0.0.0', port=int(os.environ.get("PORT", 5005)))