Add production installer and observability tooling
This commit is contained in:
+73
-36
@@ -1,68 +1,105 @@
|
||||
import atexit
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from flask import Flask
|
||||
from flask_login import LoginManager
|
||||
|
||||
from .core import (
|
||||
LicenseService,
|
||||
MonitorEngine,
|
||||
RuntimeState,
|
||||
User,
|
||||
ensure_secret_key,
|
||||
load_license_key,
|
||||
)
|
||||
from .routes import register_routes
|
||||
|
||||
|
||||
def configure_logging() -> None:
|
||||
logging.basicConfig(
|
||||
level=os.getenv("VFM_LOG_LEVEL", "INFO").upper(),
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
)
|
||||
|
||||
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
||||
from .core import (
|
||||
LicenseService,
|
||||
MonitorEngine,
|
||||
RuntimeState,
|
||||
User,
|
||||
ensure_secret_key,
|
||||
load_license_key,
|
||||
)
|
||||
from .routes import register_routes
|
||||
|
||||
|
||||
def configure_logging() -> None:
|
||||
logging.basicConfig(
|
||||
level=os.getenv("VFM_LOG_LEVEL", "INFO").upper(),
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
)
|
||||
|
||||
|
||||
def is_production_mode() -> bool:
|
||||
value = os.getenv("VFM_ENV", "").strip().lower()
|
||||
return value in {"prod", "production"}
|
||||
|
||||
|
||||
def env_flag(name: str, default: bool = False) -> bool:
|
||||
raw_value = os.getenv(name)
|
||||
if raw_value is None:
|
||||
return default
|
||||
return raw_value.strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
|
||||
def create_app(base_dir: str | Path | None = None, start_monitor: bool = True) -> Flask:
|
||||
configure_logging()
|
||||
|
||||
resolved_base_dir = Path(base_dir) if base_dir else Path(__file__).resolve().parent.parent
|
||||
package_root = Path(__file__).resolve().parent.parent
|
||||
template_dir = resolved_base_dir / "templates"
|
||||
static_dir = resolved_base_dir / "static"
|
||||
if not template_dir.exists():
|
||||
template_dir = package_root / "templates"
|
||||
if not static_dir.exists():
|
||||
static_dir = package_root / "static"
|
||||
secret_path = resolved_base_dir / "app_secret.key"
|
||||
default_password = os.getenv("VFM_DEFAULT_PASSWORD", "admin")
|
||||
|
||||
app = Flask(
|
||||
__name__,
|
||||
template_folder=str(resolved_base_dir / "templates"),
|
||||
template_folder=str(template_dir),
|
||||
static_folder=str(static_dir),
|
||||
)
|
||||
app.secret_key = os.getenv("VFM_APP_SECRET") or ensure_secret_key(secret_path)
|
||||
|
||||
runtime_state = RuntimeState(resolved_base_dir, default_password)
|
||||
license_service = LicenseService(
|
||||
resolved_base_dir / "license.key",
|
||||
load_license_key(require_env=is_production_mode()),
|
||||
app.config.update(
|
||||
SESSION_COOKIE_HTTPONLY=True,
|
||||
SESSION_COOKIE_SAMESITE="Lax",
|
||||
SESSION_COOKIE_SECURE=env_flag("VFM_SESSION_COOKIE_SECURE", is_production_mode()),
|
||||
REMEMBER_COOKIE_HTTPONLY=True,
|
||||
REMEMBER_COOKIE_SAMESITE="Lax",
|
||||
MAX_CONTENT_LENGTH=int(os.getenv("VFM_MAX_CONTENT_LENGTH", str(1024 * 1024))),
|
||||
)
|
||||
|
||||
app.extensions["runtime_state"] = runtime_state
|
||||
app.extensions["license_service"] = license_service
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = "login"
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User(user_id)
|
||||
|
||||
if env_flag("VFM_TRUST_PROXY"):
|
||||
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
|
||||
|
||||
runtime_state = RuntimeState(resolved_base_dir, default_password)
|
||||
license_service = LicenseService(
|
||||
resolved_base_dir / "license.key",
|
||||
load_license_key(require_env=is_production_mode()),
|
||||
)
|
||||
|
||||
app.extensions["runtime_state"] = runtime_state
|
||||
app.extensions["license_service"] = license_service
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = "login"
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User(user_id)
|
||||
|
||||
register_routes(app)
|
||||
|
||||
@app.after_request
|
||||
def apply_security_headers(response):
|
||||
response.headers["X-Content-Type-Options"] = "nosniff"
|
||||
response.headers["X-Frame-Options"] = "SAMEORIGIN"
|
||||
response.headers["Referrer-Policy"] = "same-origin"
|
||||
response.headers["Cache-Control"] = "no-store"
|
||||
return response
|
||||
|
||||
if start_monitor:
|
||||
monitor_engine = MonitorEngine(runtime_state, license_service)
|
||||
app.extensions["monitor_engine"] = monitor_engine
|
||||
monitor_engine.start()
|
||||
atexit.register(monitor_engine.stop)
|
||||
|
||||
return app
|
||||
|
||||
Reference in New Issue
Block a user