95 lines
4.0 KiB
Python
95 lines
4.0 KiB
Python
"""
|
|
app/admin/__init__.py — Admin app factory (admin.mydomain.com).
|
|
"""
|
|
|
|
import logging
|
|
from datetime import timedelta
|
|
from flask import Flask
|
|
from app.extensions import db, migrate, csrf, mail, scheduler, admin_login_manager, admin_jwt, limiter
|
|
from app.security import apply_security_headers, check_admin_ip
|
|
from config import get_config
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def create_admin_app(config_override=None):
|
|
import os as _os
|
|
# Resolve paths relative to this file to avoid CWD-dependent behaviour.
|
|
# app/admin/__init__.py → ../../templates = <project_root>/templates
|
|
_here = _os.path.dirname(_os.path.abspath(__file__))
|
|
_root = _os.path.normpath(_os.path.join(_here, "..", ".."))
|
|
flask_app = Flask(
|
|
__name__,
|
|
template_folder=_os.path.join(_root, "templates"),
|
|
static_folder=_os.path.join(_root, "static", "admin"),
|
|
static_url_path="/static",
|
|
)
|
|
|
|
# ── Config ────────────────────────────────────────────────
|
|
cfg = config_override or get_config()
|
|
flask_app.config.from_object(cfg)
|
|
|
|
# Admin portal uses a separate secret key and session cookie name
|
|
import os
|
|
flask_app.config["SECRET_KEY"] = os.environ.get(
|
|
"ADMIN_SECRET_KEY", flask_app.config["SECRET_KEY"]
|
|
)
|
|
flask_app.config["SESSION_COOKIE_NAME"] = "salon_pos_admin_session"
|
|
flask_app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(
|
|
seconds=int(os.environ.get("SESSION_TIMEOUT_ADMIN", 3600))
|
|
)
|
|
flask_app.config["SESSION_COOKIE_HTTPONLY"] = True
|
|
flask_app.config["SESSION_COOKIE_SAMESITE"] = "Lax"
|
|
|
|
# ── Extensions ────────────────────────────────────────────
|
|
db.init_app(flask_app)
|
|
migrate.init_app(flask_app, db)
|
|
csrf.init_app(flask_app)
|
|
mail.init_app(flask_app)
|
|
limiter.init_app(flask_app)
|
|
|
|
# Admin login manager
|
|
admin_login_manager.login_view = "admin_auth.login"
|
|
admin_login_manager.login_message_category = "warning"
|
|
admin_login_manager.session_protection = "strong"
|
|
admin_login_manager.init_app(flask_app)
|
|
|
|
# Admin JWT
|
|
admin_jwt.init_app(flask_app)
|
|
|
|
# ── User loader ───────────────────────────────────────────
|
|
from app.models.platform import SystemUser
|
|
|
|
@admin_login_manager.user_loader
|
|
def load_admin_user(user_id: str):
|
|
if not user_id.startswith("system:"):
|
|
return None
|
|
try:
|
|
uid = int(user_id.split(":")[1])
|
|
except (ValueError, IndexError):
|
|
return None
|
|
return SystemUser.query.get(uid)
|
|
|
|
# ── Security ──────────────────────────────────────────────
|
|
apply_security_headers(flask_app)
|
|
check_admin_ip(flask_app)
|
|
|
|
# ── Blueprints ────────────────────────────────────────────
|
|
from app.admin.auth.routes import admin_auth_bp
|
|
from app.admin.system_users.routes import system_users_bp
|
|
from app.admin.tenants.routes import tenants_bp
|
|
from app.admin.plans.routes import plans_bp
|
|
from app.admin.billing.routes import billing_bp
|
|
from app.admin.settings_override.routes import settings_override_bp
|
|
from app.admin.audit_log.routes import audit_log_bp
|
|
from app.admin.analytics.routes import analytics_bp
|
|
|
|
for bp in [admin_auth_bp, system_users_bp, tenants_bp, plans_bp,
|
|
billing_bp, settings_override_bp, audit_log_bp, analytics_bp]:
|
|
flask_app.register_blueprint(bp)
|
|
|
|
# ── Import all models for Migrate ─────────────────────────
|
|
import app.models # noqa: F401
|
|
|
|
logger.info("Admin app created (env=%s)", flask_app.config.get("FLASK_ENV"))
|
|
return flask_app |