"""Singleton Flask extension instances — initialized, not configured, here. Configuration happens in create_app() via .init_app(), so nothing here holds app- or request-scoped state that must survive a process restart (factor 6: stateless processes). """ from flask_caching import Cache from flask_limiter import Limiter from flask_limiter.util import get_remote_address from flask_login import LoginManager from flask_migrate import Migrate from flask_sqlalchemy import SQLAlchemy from flask_wtf import CSRFProtect db = SQLAlchemy() cache = Cache() csrf = CSRFProtect() login_manager = LoginManager() login_manager.login_view = "auth.login" migrate = Migrate() # In-memory storage (Chapter 12: "in-memory or DB-backed... do not require # Redis"). CAVEAT (flagged): each of up to 60 entry processes (Ch03) keeps # its own counters, so the effective ceiling across the whole app is up to # (per-process limit x concurrent processes hit), not one hard global cap. # Acceptable for this app's threat model (slowing down /login and /uploads # brute-forcing), but not a strict global rate guarantee. limiter = Limiter(key_func=get_remote_address, storage_uri="memory://")