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 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) 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, ) @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_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()