Add 4-layer access control stack (subscription → module → menu → permissions): - P1: Wire requires_permission into menu sidebar filtering - P2: Expose window.USER_PERMISSIONS for Alpine.js client-side gating - P3: Add page-level permission guards on store routes - P4: Role CRUD API endpoints and role editor UI - P5: Audit trail for all role/permission changes Includes unit tests (menu permission filtering, role CRUD service) and integration tests (role API endpoints). All 404 core+tenancy tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
96 lines
2.6 KiB
Python
96 lines
2.6 KiB
Python
# app/modules/analytics/routes/pages/store.py
|
|
"""
|
|
Analytics Store Page Routes (HTML rendering).
|
|
|
|
Store pages for analytics dashboard.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
from fastapi.responses import HTMLResponse
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api.deps import (
|
|
get_db,
|
|
get_resolved_store_code,
|
|
require_store_page_permission,
|
|
)
|
|
from app.modules.core.services.platform_settings_service import (
|
|
platform_settings_service, # MOD-004 - shared platform service
|
|
)
|
|
from app.modules.tenancy.models import Store, User
|
|
from app.templates_config import templates
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
# ============================================================================
|
|
# HELPER: Build Store Dashboard Context
|
|
# ============================================================================
|
|
|
|
|
|
def get_store_context(
|
|
request: Request,
|
|
db: Session,
|
|
current_user: User,
|
|
store_code: str,
|
|
**extra_context,
|
|
) -> dict:
|
|
"""
|
|
Build template context for store dashboard pages.
|
|
|
|
Resolves locale/currency using the platform settings service with
|
|
store override support.
|
|
"""
|
|
# Load store from database
|
|
store = db.query(Store).filter(Store.subdomain == store_code).first()
|
|
|
|
# Get platform defaults
|
|
platform_config = platform_settings_service.get_storefront_config(db)
|
|
|
|
# Resolve with store override
|
|
storefront_locale = platform_config["locale"]
|
|
storefront_currency = platform_config["currency"]
|
|
|
|
if store and store.storefront_locale:
|
|
storefront_locale = store.storefront_locale
|
|
|
|
context = {
|
|
"request": request,
|
|
"user": current_user,
|
|
"store": store,
|
|
"store_code": store_code,
|
|
"storefront_locale": storefront_locale,
|
|
"storefront_currency": storefront_currency,
|
|
**extra_context,
|
|
}
|
|
|
|
return context
|
|
|
|
|
|
# ============================================================================
|
|
# ANALYTICS PAGE
|
|
# ============================================================================
|
|
|
|
|
|
@router.get(
|
|
"/analytics", response_class=HTMLResponse, include_in_schema=False
|
|
)
|
|
async def store_analytics_page(
|
|
request: Request,
|
|
store_code: str = Depends(get_resolved_store_code),
|
|
current_user: User = Depends(require_store_page_permission("analytics.view")),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
Render analytics and reports page.
|
|
JavaScript loads analytics data via API.
|
|
"""
|
|
return templates.TemplateResponse(
|
|
"analytics/store/analytics.html",
|
|
get_store_context(request, db, current_user, store_code),
|
|
)
|