import json
from actingweb.handlers import base_handler
[docs]
class RootHandler(base_handler.BaseHandler):
def _wants_html(self) -> bool:
"""Check if client prefers HTML response (browser)."""
# Check Accept header - browsers typically send text/html
# Use get_header() for case-insensitive lookup (FastAPI normalizes to lowercase)
accept = self.request.get_header("Accept") or ""
if "text/html" in accept:
return True
return False
[docs]
def get(self, actor_id):
if self.request.get("_method") == "DELETE":
self.delete(actor_id)
return
# Authenticate and authorize separately like DELETE method
auth_result = self.authenticate_actor(actor_id, "")
if not auth_result.success:
# For browser requests, redirect to /login instead of returning auth error
# This provides better UX than throwing users directly into OAuth flow
if self._wants_html():
# Build full URL for redirect - config.root includes protocol, host, and any base path
full_login_url = f"{self.config.root.rstrip('/')}/login"
self.response.set_redirect(full_login_url)
self.response.headers["Location"] = full_login_url
self.response.set_status(302, "Found")
return # Response already set (either our redirect or auth error)
if not auth_result.authorize("GET", "/"):
return # Response already set
myself = auth_result.actor
# Content negotiation: redirect for browsers, JSON for API clients
if self._wants_html():
# Browser request - redirect based on config
actor_id_str = myself.id or ""
if self.config.ui:
# Web UI enabled - redirect to /www
redirect_path = f"/{actor_id_str}/www"
else:
# Web UI disabled - redirect to /app (for SPAs)
redirect_path = f"/{actor_id_str}/app"
if self.response:
# Build full URL for redirect - config.root includes protocol, host, and any base path
# The integration uses response.redirect directly for RedirectResponse
full_redirect_url = f"{self.config.root.rstrip('/')}{redirect_path}"
self.response.set_redirect(full_redirect_url)
self.response.headers["Location"] = full_redirect_url
self.response.set_status(302, "Found")
return
# API client - return JSON
pair = {
"id": myself.id,
"creator": myself.creator,
"passphrase": myself.passphrase,
}
trustee_root = myself.store.trustee_root if myself.store else None
if trustee_root and len(trustee_root) > 0:
pair["trustee_root"] = trustee_root
out = json.dumps(pair)
if self.response:
self.response.write(out)
self.response.headers["Content-Type"] = "application/json"
self.response.set_status(200)
[docs]
def delete(self, actor_id):
# Alternative: more control with AuthResult
auth_result = self.authenticate_actor(actor_id, "")
if not auth_result.success:
return # Response already set
if not auth_result.authorize("DELETE", "/"):
return # Response already set
myself = auth_result.actor
# Execute actor deletion lifecycle hook
if self.hooks:
actor_interface = self._get_actor_interface(myself)
if actor_interface:
self.hooks.execute_lifecycle_hooks("actor_deleted", actor_interface)
myself.delete()
self.response.set_status(204)
return