Jun 26 MT-1 phase

This commit is contained in:
2026-06-26 16:37:44 -04:00
parent fc39491891
commit 07cf2e5816
9 changed files with 300 additions and 55 deletions
+33 -54
View File
@@ -11,7 +11,9 @@ import os
import logging
from logging.handlers import RotatingFileHandler
db = SQLAlchemy()
from app.tenancy.routing import RoutingSession
db = SQLAlchemy(session_options={'class_': RoutingSession})
login_manager = LoginManager()
migrate = Migrate()
mail = Mail()
@@ -37,6 +39,13 @@ def create_app(config_name='default'):
csrf.init_app(app) # enables CSRF protection for all web routes
limiter.init_app(app) # rate limiting — applied per-route via @limiter.limit()
# ── Multi-tenant resolution (MT-1) ───────────────────────────────────────
# Registers the before_request Host→tenant resolver. Inert (no-op) unless
# config MULTI_TENANT_ENABLED is True, so the single-tenant deployment is
# unaffected until tenants are provisioned and the flag is flipped.
from app.tenancy.middleware import init_tenancy
init_tenancy(app)
login_manager.login_view = 'auth.login'
login_manager.login_message = 'Please log in to access this page.'
login_manager.login_message_category = 'info'
@@ -103,56 +112,32 @@ def create_app(config_name='default'):
@app.context_processor
def inject_notification_count():
if not current_user.is_authenticated:
return {
'unread_notification_count': 0,
'pending_verification_count': 0,
'open_support_tickets_count': 0,
}
# ── Unread notification count (all roles) ──────────────────────────
# Computed first, in its own try/except, so a failure in the
# director-specific queries below never zeroes out the bell badge.
try:
from app.models.notification import Notification
unread = Notification.query.filter_by(
user_id=current_user.id, is_read=False
).count()
except Exception as exc:
import logging as _logging
_logging.getLogger(__name__).warning(
'inject_notification_count: unread query failed: %s', exc
)
unread = 0
# ── Director/admin-only counts ─────────────────────────────────────
pv_count = 0
open_support = 0
if current_user.role in ('admin', 'director'):
try:
if current_user.is_authenticated:
from app.models.notification import Notification
from app.models.issue import Issue
pv_count = Issue.query.filter_by(
status='pending_verification'
unread = Notification.query.filter_by(
user_id=current_user.id, is_read=False
).count()
except Exception as exc:
import logging as _logging
_logging.getLogger(__name__).warning(
'inject_notification_count: pv_count query failed: %s', exc
)
try:
from app.models.support import SupportTicket
open_support = SupportTicket.query.filter_by(status='open').count()
except Exception as exc:
import logging as _logging
_logging.getLogger(__name__).warning(
'inject_notification_count: support_tickets query failed: %s', exc
)
return {
'unread_notification_count': unread,
'pending_verification_count': pv_count,
'open_support_tickets_count': open_support,
}
# Pending verification count — only computed for director+ roles
pv_count = 0
if current_user.role in ('admin', 'director'):
pv_count = Issue.query.filter_by(
status='pending_verification'
).count()
# Open support tickets — admin/director only
open_support = 0
if current_user.role in ('admin', 'director'):
from app.models.support import SupportTicket
open_support = SupportTicket.query.filter_by(status='open').count()
return {
'unread_notification_count': unread,
'pending_verification_count': pv_count,
'open_support_tickets_count': open_support,
}
except Exception:
pass
return {'unread_notification_count': 0, 'pending_verification_count': 0, 'open_support_tickets_count': 0}
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
@@ -164,8 +149,6 @@ def create_app(config_name='default'):
from app.routes import customers # Phase 5 — Customer management
from app.routes import scheduled_reports # Phase 6 — Scheduled reports
from app.routes import support # Support chat + admin tickets
from app.routes import broadcast # Admin broadcast notifications
from app.routes import devices # Admin device registry
app.register_blueprint(auth.bp)
app.register_blueprint(dashboard.bp)
@@ -180,8 +163,6 @@ def create_app(config_name='default'):
app.register_blueprint(customers.bp)
app.register_blueprint(scheduled_reports.bp)
app.register_blueprint(support.bp)
app.register_blueprint(broadcast.bp)
app.register_blueprint(devices.bp)
# ── Mobile API (Phase 7 / Phase A / Phase B / Phase C) ───────────────────
# The /api/v1 blueprint group uses JWT Bearer tokens — no CSRF cookies needed.
@@ -199,7 +180,6 @@ def create_app(config_name='default'):
from app.api.notifications import bp as _api_notifications_bp
from app.api.stats import bp as _api_stats_bp
from app.api.comments import bp as _api_comments_bp
from app.api.devices import bp as _api_devices_bp
csrf.exempt(_api_auth_bp)
csrf.exempt(_api_facilities_bp)
csrf.exempt(_api_templates_bp)
@@ -209,7 +189,6 @@ def create_app(config_name='default'):
csrf.exempt(_api_notifications_bp)
csrf.exempt(_api_stats_bp)
csrf.exempt(_api_comments_bp)
csrf.exempt(_api_devices_bp)
register_api(app)
# ── Security response headers ─────────────────────────────────────────