CI Pipeline / backend-test (push) Successful in 8m58s
CI Pipeline / frontend-test (push) Successful in 29s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / backend-test (pull_request) Successful in 8m56s
CI Pipeline / frontend-test (pull_request) Successful in 28s
CI Pipeline / frontend-build (pull_request) Successful in 32s
CI Pipeline / backend-lint (pull_request) Successful in 11s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / quality-gate (pull_request) Successful in 3s
The reviewer noted the static sitemap.xml had a hardcoded domain in a white-label app. Instead of writing files to disk, we now serve sitemap.xml and robots.txt as dynamic API endpoints that read KMTN_WEBSITE_URL at runtime. - Nginx proxies /sitemap.xml and /robots.txt to the backend API so search crawlers reach them at root paths. - Removed the startup filesystem-write that would fail in Docker (backend can't write to Nginx container's static dir). - Simplified sitemap_export.py to pure generation functions (no I/O). - Added /api/sitemap/regenerate admin endpoint for consistency.
61 lines
2.0 KiB
Python
61 lines
2.0 KiB
Python
"""Sitemap & robots.txt API: tenant-aware SEO files served by the backend.
|
|
|
|
Because this is a white-label app where each tenant has its own domain
|
|
(set via KMTN_WEBSITE_URL), sitemap.xml and robots.txt are generated
|
|
on-the-fly from the station config — no static files needed.
|
|
|
|
This matches the reviewer feedback:
|
|
'This is a whitelabel app - this file may need to be dynaimcally generated...'
|
|
|
|
Follows the same pattern as theme_export.py: content is derived from
|
|
KMTN_WEBSITE_URL at runtime, so every tenant gets their own correct URL.
|
|
"""
|
|
|
|
from typing import Any, Dict
|
|
|
|
from fastapi import APIRouter, Depends, Response, status as http_status
|
|
|
|
from app.auth import get_current_admin_user
|
|
from app.config import settings
|
|
from app.sitemap_export import build_robots_txt, build_sitemap_xml
|
|
from app.user_models import User
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/sitemap.xml")
|
|
async def get_sitemap() -> Response:
|
|
"""Return a dynamically-generated sitemap.xml. Public endpoint.
|
|
|
|
Uses KMTN_WEBSITE_URL so each tenant gets the correct domain.
|
|
"""
|
|
xml = build_sitemap_xml(settings.WEBSITE_URL)
|
|
return Response(content=xml, media_type="application/xml")
|
|
|
|
|
|
@router.get("/robots.txt")
|
|
async def get_robots() -> Response:
|
|
"""Return a dynamically-generated robots.txt. Public endpoint.
|
|
|
|
Uses KMTN_WEBSITE_URL so the Sitemap directive points to the
|
|
correct tenant domain.
|
|
"""
|
|
txt = build_robots_txt(settings.WEBSITE_URL)
|
|
return Response(content=txt, media_type="text/plain")
|
|
|
|
|
|
@router.post("/regenerate", status_code=http_status.HTTP_200_OK)
|
|
async def regenerate_sitemap(
|
|
_: User = Depends(get_current_admin_user),
|
|
) -> Dict[str, Any]:
|
|
"""Force regenerate the sitemap cache. Admin only.
|
|
|
|
In the dynamic model this is a no-op (files are generated per-request),
|
|
but kept for API consistency with the theme endpoint.
|
|
"""
|
|
return {
|
|
"status": "ok",
|
|
"note": "sitemap and robots.txt are dynamically generated per-request using KMTN_WEBSITE_URL",
|
|
"base_url": settings.WEBSITE_URL,
|
|
}
|