feat(merchant): extract merchant portal as first-class frontend with auth, Tailwind fixes, and Gitea CI
Some checks failed
Some checks failed
- Extract login/dashboard from billing module into core (matching admin pattern) - Add merchant auth API with path-isolated cookies (path=/merchants) - Add merchant base layout with sidebar/header partials and Alpine.js init - Add frontend detection and login redirect for MERCHANT type - Wire merchant token in shared api-client.js (get/clear) - Migrate billing templates to merchant base with dark mode support - Fix Tailwind: rename shop→storefront in sources and config - DRY Makefile tailwind targets with TAILWIND_FRONTENDS loop - Rebuild all Tailwind outputs (production minified) - Add Gitea Actions CI workflow (ruff, pytest, architecture, docs) - Add Gitea deployment documentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,14 +3,14 @@
|
||||
Merchant Billing Page Routes (HTML rendering).
|
||||
|
||||
Page routes for the merchant billing portal:
|
||||
- Dashboard (overview of stores, subscriptions)
|
||||
- Subscriptions list
|
||||
- Subscription detail per platform
|
||||
- Billing history / invoices
|
||||
- Login page
|
||||
|
||||
Authentication: merchant_token cookie or Authorization header.
|
||||
Login page uses optional auth to check if already logged in.
|
||||
|
||||
Login and dashboard routes have moved to core module
|
||||
(app/modules/core/routes/pages/merchant.py) to match the admin pattern.
|
||||
|
||||
Auto-discovered by the route system (merchant.py in routes/pages/ triggers
|
||||
registration under /merchants/billing/*).
|
||||
@@ -20,10 +20,7 @@ from fastapi import APIRouter, Depends, Path, Request
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import (
|
||||
get_current_merchant_from_cookie_or_header,
|
||||
get_current_merchant_optional,
|
||||
)
|
||||
from app.api.deps import get_current_merchant_from_cookie_or_header
|
||||
from app.core.database import get_db
|
||||
from app.modules.core.utils.page_context import get_context_for_frontend
|
||||
from app.modules.enums import FrontendType
|
||||
@@ -53,15 +50,6 @@ def _get_merchant_context(
|
||||
|
||||
Uses the module-driven context builder with FrontendType.MERCHANT,
|
||||
and adds the authenticated user to the context.
|
||||
|
||||
Args:
|
||||
request: FastAPI request
|
||||
db: Database session
|
||||
current_user: Authenticated merchant user context
|
||||
**extra_context: Additional template variables
|
||||
|
||||
Returns:
|
||||
Dict of context variables for template rendering
|
||||
"""
|
||||
return get_context_for_frontend(
|
||||
FrontendType.MERCHANT,
|
||||
@@ -73,26 +61,14 @@ def _get_merchant_context(
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# DASHBOARD
|
||||
# BILLING ROOT
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse, include_in_schema=False)
|
||||
async def merchant_dashboard_page(
|
||||
request: Request,
|
||||
current_user: UserContext = Depends(get_current_merchant_from_cookie_or_header),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Render merchant dashboard page.
|
||||
|
||||
Shows an overview of the merchant's stores and subscriptions.
|
||||
"""
|
||||
context = _get_merchant_context(request, db, current_user)
|
||||
return templates.TemplateResponse(
|
||||
"billing/merchant/dashboard.html",
|
||||
context,
|
||||
)
|
||||
@router.get("/", response_class=RedirectResponse, include_in_schema=False)
|
||||
async def merchant_billing_root():
|
||||
"""Redirect /merchants/billing/ to subscriptions page."""
|
||||
return RedirectResponse(url="/merchants/billing/subscriptions", status_code=302)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
@@ -164,35 +140,3 @@ async def merchant_billing_history_page(
|
||||
"billing/merchant/billing-history.html",
|
||||
context,
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# LOGIN
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@router.get("/login", response_class=HTMLResponse, include_in_schema=False)
|
||||
async def merchant_login_page(
|
||||
request: Request,
|
||||
current_user: UserContext | None = Depends(get_current_merchant_optional),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Render merchant login page.
|
||||
|
||||
If the user is already authenticated as a merchant owner,
|
||||
redirects to the merchant dashboard.
|
||||
"""
|
||||
# Redirect to dashboard if already logged in
|
||||
if current_user is not None:
|
||||
return RedirectResponse(url="/merchants/billing/", status_code=302)
|
||||
|
||||
context = get_context_for_frontend(
|
||||
FrontendType.MERCHANT,
|
||||
request,
|
||||
db,
|
||||
)
|
||||
return templates.TemplateResponse(
|
||||
"billing/merchant/login.html",
|
||||
context,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user