- Schedules web UI (/settings/crons): list, add, edit, pause/resume, delete jobs - cron task type: full orchestrator tool loop on a schedule, result → notification channel - parse_schedule: monthly/yearly formats (monthly:DD:HH:MM, yearly:MM:DD:HH:MM) - HA inbound webhook tools toggle: orchestrator loop vs. direct LLM, configurable in UI - ae_db_query/describe/show_view: SELECT-only Aether MariaDB access (admin, per-user creds) - /settings/integrations: admin-only page for Aether DB credentials - Schedules nav link added to all settings pages - pymysql added to requirements - Docs updated: HELP.md, MASTER.md, CLAUDE.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
71 lines
2.0 KiB
Python
71 lines
2.0 KiB
Python
"""
|
|
Help page router.
|
|
|
|
Routes:
|
|
GET /help → full-page help viewer (requires auth)
|
|
"""
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
|
|
import jwt
|
|
from fastapi import APIRouter, Request
|
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
|
|
from auth_utils import COOKIE_NAME, decode_token, _read_auth
|
|
from persona import list_user_personas
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
_STATIC = Path(__file__).parent.parent / "static"
|
|
|
|
|
|
_LAST_PERSONA_COOKIE = "cx_last_persona"
|
|
|
|
|
|
def _get_session_user(request: Request) -> str | None:
|
|
token = request.cookies.get(COOKIE_NAME)
|
|
if not token:
|
|
return None
|
|
try:
|
|
return decode_token(token)
|
|
except jwt.InvalidTokenError:
|
|
return None
|
|
|
|
|
|
def _preferred_persona(request: Request, username: str) -> str:
|
|
names = list_user_personas(username)
|
|
if not names:
|
|
return ""
|
|
cookie_val = request.cookies.get(_LAST_PERSONA_COOKIE, "")
|
|
if cookie_val in names:
|
|
return cookie_val
|
|
return names[0]
|
|
|
|
|
|
@router.get("/help", include_in_schema=False)
|
|
async def help_page(request: Request, persona: str = ""):
|
|
username = _get_session_user(request)
|
|
if not username:
|
|
return RedirectResponse("/login", status_code=302)
|
|
|
|
personas = list_user_personas(username)
|
|
# Use persona from query param if valid, else prefer last-visited from cookie
|
|
if persona and persona in personas:
|
|
back_persona = persona
|
|
else:
|
|
back_persona = _preferred_persona(request, username)
|
|
back_href = f"/{username}/{back_persona}" if back_persona else "/"
|
|
|
|
html = (_STATIC / "help.html").read_text()
|
|
config_tag = (
|
|
f'<script>window.HELP_CONFIG = '
|
|
f'{{user: "{username}", persona: "{back_persona}", backHref: "{back_href}"}};</script>'
|
|
)
|
|
html = html.replace("</head>", f"{config_tag}\n</head>", 1)
|
|
nav = '<a href="/settings/integrations" class="nav-link">Integrations</a>' \
|
|
if _read_auth(username).get("role", "user") == "admin" else ""
|
|
html = html.replace("{{ integrations_nav }}", nav)
|
|
return HTMLResponse(html)
|