import os from flask import Flask from flask_login import LoginManager from flask_sqlalchemy import SQLAlchemy from dotenv import load_dotenv load_dotenv() db = SQLAlchemy() login_manager = LoginManager() login_manager.login_view = "auth.login" login_manager.login_message = "Войдите для доступа к этой странице." login_manager.login_message_category = "error" @login_manager.user_loader def load_user(user_id): from app.models import User return db.session.get(User, int(user_id)) def create_app(setup_database=True): app = Flask(__name__) app.config["SECRET_KEY"] = os.getenv("SECRET_KEY", "dev-secret-change-me") app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv( "DATABASE_URL", "postgresql://photohost:photohost_secret@localhost:5432/photohost", ) app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {"pool_pre_ping": True} app.config["UPLOAD_FOLDER"] = os.getenv("UPLOAD_FOLDER", "uploads") app.config["MAX_CONTENT_LENGTH"] = int(os.getenv("MAX_UPLOAD_MB", "10")) * 1024 * 1024 app.config["ALLOWED_EXTENSIONS"] = {"png", "jpg", "jpeg", "gif", "webp", "bmp"} app.config["GIT_REPO_PATH"] = os.getenv("GIT_REPO_PATH", "/repo") app.config["ALLOW_GIT_DEPLOY"] = os.getenv("ALLOW_GIT_DEPLOY", "false").lower() in ( "1", "true", "yes", ) app.config["DEFAULT_GROUP_QUOTA_MB"] = int(os.getenv("DEFAULT_GROUP_QUOTA_MB", "100")) os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) db.init_app(app) login_manager.init_app(app) from .routes import bp as main_bp, cabinet_bp from .auth import bp as auth_bp from .admin import bp as admin_bp from .folders import bp as folders_bp from .legal import bp as legal_bp from .passkey import bp as passkey_bp app.register_blueprint(main_bp) app.register_blueprint(cabinet_bp) app.register_blueprint(auth_bp) app.register_blueprint(admin_bp) app.register_blueprint(folders_bp) app.register_blueprint(legal_bp) app.register_blueprint(passkey_bp) register_request_hooks(app) register_cli(app) # Ensure models are registered even when DB setup runs in init_db.py. with app.app_context(): from app.models import ( # noqa: F401 AdBanner, Folder, FolderInvite, FolderMember, PasswordResetToken, Photo, SiteSettings, User, UserGroup, UserPasskey, UserSession, ) @app.context_processor def inject_banners(): from app.banner_service import get_banners_by_position try: return {"site_banners": get_banners_by_position()} except Exception: return {"site_banners": {}} if setup_database: with app.app_context(): from app.bootstrap import ( create_first_admin, ensure_default_group, ensure_site_settings, run_schema_migrations, ) db.create_all() run_schema_migrations() if os.getenv("SKIP_DB_INIT") != "1": ensure_default_group(app) ensure_site_settings(app) create_first_admin(app) return app def register_request_hooks(app): @app.before_request def validate_tracked_session(): from flask import flash, redirect, request, session, url_for from flask_login import current_user, logout_user from app.session_service import ensure_user_session, touch_user_session, validate_user_session if not current_user.is_authenticated: return None endpoint = request.endpoint or "" if endpoint.startswith("static") or endpoint.startswith("passkey.") or endpoint.startswith("legal."): return None if endpoint in ("main.health",): return None if not validate_user_session(current_user.id): if session.get("sid"): logout_user() flash("Сессия завершена. Войдите снова.", "error") return redirect(url_for("auth.login")) ensure_user_session(current_user) else: touch_user_session() return None def register_cli(app): @app.cli.command("create-admin") def create_admin_command(): """Create or update admin user interactively.""" from getpass import getpass from app.models import User username = input("Username: ").strip() email = input("Email: ").strip() password = getpass("Password: ") password2 = getpass("Confirm password: ") if not username or not email or not password: print("All fields are required.") return if password != password2: print("Passwords do not match.") return user = User.query.filter_by(username=username).first() if user: user.email = email user.is_admin = True user.set_password(password) print(f"User '{username}' updated and promoted to admin.") else: user = User(username=username, email=email, is_admin=True) user.set_password(password) db.session.add(user) print(f"Admin '{username}' created.") db.session.commit()