|
# retoor <retoor@molodetz.nl>
|
|
|
|
import logging
|
|
from fastapi import APIRouter, Request
|
|
from fastapi.responses import HTMLResponse, RedirectResponse, Response
|
|
from devplacepy.templating import templates
|
|
from devplacepy.utils import (
|
|
get_current_user,
|
|
not_found,
|
|
is_admin as user_is_admin,
|
|
track_action,
|
|
)
|
|
from devplacepy.seo import base_seo_context, site_url, website_schema
|
|
from devplacepy.docs_api import render_group, build_services_group
|
|
from devplacepy.services.manager import service_manager
|
|
from devplacepy.database import get_setting
|
|
from devplacepy.constants import DEVII_GUEST_COOKIE
|
|
from devplacepy import docs_export, docs_live, docs_prose, docs_search
|
|
|
|
from devplacepy.routers.docs.pages import DOCS_PAGES, PAGES_BY_SLUG, nav_groups
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/docs")
|
|
async def docs_root(request: Request):
|
|
return RedirectResponse(url="/docs/index.html", status_code=302)
|
|
|
|
|
|
def _export_context(request: Request):
|
|
user = get_current_user(request)
|
|
is_admin = user_is_admin(user)
|
|
return (
|
|
is_admin,
|
|
site_url(request),
|
|
user.get("username") if user else "",
|
|
user.get("api_key") if user else "",
|
|
)
|
|
|
|
|
|
@router.get("/docs/download.md")
|
|
async def docs_download_md(request: Request):
|
|
is_admin, base, username, api_key = _export_context(request)
|
|
markdown = docs_export.build_markdown(is_admin, base, username, api_key)
|
|
return Response(
|
|
content=markdown,
|
|
media_type="text/markdown; charset=utf-8",
|
|
headers={"Content-Disposition": 'attachment; filename="devplace-docs.md"'},
|
|
)
|
|
|
|
|
|
@router.get("/docs/download.html")
|
|
async def docs_download_html(request: Request):
|
|
is_admin, base, username, api_key = _export_context(request)
|
|
markdown = docs_export.build_markdown(is_admin, base, username, api_key)
|
|
return HTMLResponse(
|
|
content=docs_export.build_html(markdown),
|
|
headers={"Content-Disposition": 'attachment; filename="devplace-docs.html"'},
|
|
)
|
|
|
|
|
|
def _agent_search_state(request: Request, user, is_admin: bool) -> str:
|
|
svc = service_manager.get_service("devii")
|
|
if svc is None or not svc.is_enabled():
|
|
return "disabled"
|
|
if user:
|
|
owner_kind, owner_id = "user", user["uid"]
|
|
else:
|
|
owner_kind = "guest"
|
|
owner_id = request.cookies.get(DEVII_GUEST_COOKIE) or ""
|
|
if owner_kind == "guest" and not svc.guests_enabled():
|
|
return "disabled"
|
|
if owner_id:
|
|
limit = svc.daily_limit_for(owner_kind, is_admin)
|
|
if limit > 0 and svc.spent_24h(owner_kind, owner_id) >= limit:
|
|
return "quota"
|
|
return "ok"
|
|
|
|
|
|
@router.get("/docs/{slug}.html", response_class=HTMLResponse)
|
|
async def docs_page(request: Request, slug: str):
|
|
user = get_current_user(request)
|
|
is_admin = user_is_admin(user)
|
|
base = site_url(request)
|
|
visible_pages = [p for p in DOCS_PAGES if not p.get("admin") or is_admin]
|
|
|
|
if slug == "search":
|
|
query = request.query_params.get("q", "")
|
|
mode = get_setting("docs_search_mode", "agent")
|
|
state = _agent_search_state(request, user, is_admin) if mode == "agent" else "off"
|
|
use_agent = mode == "agent" and state == "ok"
|
|
quota_fallback = mode == "agent" and state == "quota"
|
|
results = None if use_agent else docs_search.search(query, user=user, is_admin=is_admin)
|
|
seo_ctx = base_seo_context(
|
|
request,
|
|
title="Search - Documentation",
|
|
description="Search the DevPlace developer documentation.",
|
|
robots="noindex",
|
|
breadcrumbs=[
|
|
{"name": "Home", "url": "/feed"},
|
|
{"name": "Docs", "url": "/docs/index.html"},
|
|
{"name": "Search", "url": "/docs/search.html"},
|
|
],
|
|
schemas=[website_schema(base)],
|
|
)
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"docs_base.html",
|
|
{
|
|
**seo_ctx,
|
|
"request": request,
|
|
"user": user,
|
|
"pages": visible_pages,
|
|
"nav": nav_groups(visible_pages),
|
|
"current": "search",
|
|
"kind": "search",
|
|
"search_mode": "agent" if use_agent else "bm25",
|
|
"quota_fallback": quota_fallback,
|
|
"page_title_doc": "Search",
|
|
"search_query": query,
|
|
"search_results": results,
|
|
"base": base,
|
|
},
|
|
)
|
|
|
|
page = PAGES_BY_SLUG.get(slug)
|
|
if not page:
|
|
raise not_found("Documentation page not found")
|
|
if page.get("admin") and not is_admin:
|
|
raise not_found("Documentation page not found")
|
|
if user:
|
|
track_action(user["uid"], "docs.read", slug)
|
|
if page["kind"] == "live":
|
|
seo_ctx = base_seo_context(
|
|
request,
|
|
title=f"{page['title']} - Documentation",
|
|
description="Your live DevPlace stats, activity, and site data.",
|
|
robots="noindex",
|
|
breadcrumbs=[
|
|
{"name": "Home", "url": "/feed"},
|
|
{"name": "Docs", "url": "/docs/index.html"},
|
|
{"name": page["title"], "url": f"/docs/{slug}.html"},
|
|
],
|
|
schemas=[website_schema(base)],
|
|
)
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"docs_base.html",
|
|
{
|
|
**seo_ctx,
|
|
"request": request,
|
|
"user": user,
|
|
"pages": visible_pages,
|
|
"nav": nav_groups(visible_pages),
|
|
"current": slug,
|
|
"kind": "live",
|
|
"facts": docs_live.build_live_facts(user),
|
|
"page_title_doc": page["title"],
|
|
"base": base,
|
|
},
|
|
)
|
|
prose_html = None
|
|
group = None
|
|
if page["kind"] == "prose":
|
|
prose_html = docs_prose.render_prose(
|
|
slug,
|
|
{
|
|
"base": base,
|
|
"user": user,
|
|
"username": user.get("username") if user else "",
|
|
"api_key": user.get("api_key") if user else "",
|
|
},
|
|
)
|
|
elif page.get("dynamic"):
|
|
group = build_services_group(service_manager.describe_all(), base)
|
|
else:
|
|
group = render_group(
|
|
slug,
|
|
base,
|
|
user.get("username") if user else "",
|
|
user.get("api_key") if user else "",
|
|
)
|
|
seo_ctx = base_seo_context(
|
|
request,
|
|
title=f"{page['title']} - Documentation",
|
|
description="DevPlace developer documentation.",
|
|
breadcrumbs=[
|
|
{"name": "Home", "url": "/feed"},
|
|
{"name": "Docs", "url": "/docs/index.html"},
|
|
{"name": page["title"], "url": f"/docs/{slug}.html"},
|
|
],
|
|
schemas=[website_schema(base)],
|
|
)
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"docs_base.html",
|
|
{
|
|
**seo_ctx,
|
|
"request": request,
|
|
"user": user,
|
|
"pages": visible_pages,
|
|
"nav": nav_groups(visible_pages),
|
|
"current": slug,
|
|
"kind": page["kind"],
|
|
"group": group,
|
|
"prose_html": prose_html,
|
|
"page_title_doc": page["title"],
|
|
"base": base,
|
|
},
|
|
)
|