256 lines
9.1 KiB
Python
256 lines
9.1 KiB
Python
from pathlib import Path
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.admin.services import AdminService
|
|
from app.config import get_settings as get_app_settings
|
|
from app.db import SecretORM, get_session
|
|
from app.google.auth import GoogleAuthError, GoogleAuthManager
|
|
from app.llm.ollama_client import OllamaClient
|
|
from app.models import (
|
|
AnythingLLMStatus,
|
|
AutomationRecord,
|
|
GoogleIntegrationStatus,
|
|
MemoryRecord,
|
|
OllamaStatus,
|
|
RuntimeSettings,
|
|
TelegramStatus,
|
|
UserProfileRecord,
|
|
UserRecord,
|
|
)
|
|
from app.tools.second_brain import SecondBrainTool
|
|
|
|
router = APIRouter(prefix="/admin", tags=["admin"])
|
|
|
|
|
|
class SecretPayload(BaseModel):
|
|
key: str
|
|
value: str
|
|
|
|
|
|
class GoogleClientPayload(BaseModel):
|
|
client_id: str
|
|
client_secret: str
|
|
|
|
|
|
def get_admin_service(session: Session = Depends(get_session)) -> AdminService:
|
|
return AdminService(session)
|
|
|
|
|
|
def get_google_auth_manager() -> GoogleAuthManager:
|
|
return GoogleAuthManager(get_app_settings(), Path(__file__).resolve().parents[2])
|
|
|
|
|
|
def sync_google_client_file(service: AdminService, manager: GoogleAuthManager) -> None:
|
|
settings = get_app_settings()
|
|
client_id_record = service.session.get(SecretORM, "google_client_id")
|
|
client_secret_record = service.session.get(SecretORM, "google_client_secret")
|
|
client_id = (client_id_record.value if client_id_record else settings.google_client_id).strip()
|
|
client_secret = (client_secret_record.value if client_secret_record else settings.google_client_secret).strip()
|
|
if client_id and client_secret:
|
|
manager.write_client_secrets_file(client_id, client_secret)
|
|
|
|
|
|
@router.get("/dashboard")
|
|
def get_dashboard(service: AdminService = Depends(get_admin_service)):
|
|
return service.dashboard()
|
|
|
|
|
|
@router.get("/settings", response_model=RuntimeSettings)
|
|
def get_runtime_settings(service: AdminService = Depends(get_admin_service)):
|
|
return service.get_runtime_settings()
|
|
|
|
|
|
@router.put("/settings", response_model=RuntimeSettings)
|
|
def put_settings(payload: RuntimeSettings, service: AdminService = Depends(get_admin_service)):
|
|
return service.update_runtime_settings(payload)
|
|
|
|
|
|
@router.get("/users", response_model=list[UserRecord])
|
|
def get_users(service: AdminService = Depends(get_admin_service)):
|
|
return service.list_users()
|
|
|
|
|
|
@router.post("/users", response_model=UserRecord)
|
|
def post_user(payload: UserRecord, service: AdminService = Depends(get_admin_service)):
|
|
return service.save_user(payload)
|
|
|
|
|
|
@router.get("/profiles", response_model=list[UserProfileRecord])
|
|
def get_profiles(service: AdminService = Depends(get_admin_service)):
|
|
return service.list_user_profiles()
|
|
|
|
|
|
@router.get("/automations", response_model=list[AutomationRecord])
|
|
def get_automations(service: AdminService = Depends(get_admin_service)):
|
|
return service.list_automations()
|
|
|
|
|
|
@router.get("/memory", response_model=list[MemoryRecord])
|
|
def get_memory(service: AdminService = Depends(get_admin_service)):
|
|
return service.list_memory()
|
|
|
|
|
|
@router.delete("/memory")
|
|
def delete_memory(service: AdminService = Depends(get_admin_service)):
|
|
service.clear_memory()
|
|
return {"status": "ok"}
|
|
|
|
|
|
@router.get("/secrets/{key}")
|
|
def get_secret(key: str, service: AdminService = Depends(get_admin_service)):
|
|
return {"key": key, "masked": service.get_secret_mask(key)}
|
|
|
|
|
|
@router.post("/secrets")
|
|
def post_secret(payload: SecretPayload, service: AdminService = Depends(get_admin_service)):
|
|
service.save_secret(payload.key, payload.value)
|
|
return {"status": "ok"}
|
|
|
|
|
|
@router.post("/integrations/google/client")
|
|
def post_google_client(
|
|
payload: GoogleClientPayload,
|
|
service: AdminService = Depends(get_admin_service),
|
|
manager: GoogleAuthManager = Depends(get_google_auth_manager),
|
|
):
|
|
service.save_secret("google_client_id", payload.client_id.strip())
|
|
service.save_secret("google_client_secret", payload.client_secret.strip())
|
|
sync_google_client_file(service, manager)
|
|
return {"status": "ok"}
|
|
|
|
|
|
@router.get("/integrations/llm", response_model=OllamaStatus)
|
|
@router.get("/integrations/ollama", response_model=OllamaStatus)
|
|
async def get_llm_status(service: AdminService = Depends(get_admin_service)):
|
|
runtime = service.get_runtime_settings()
|
|
settings = get_app_settings()
|
|
secret = service.session.get(SecretORM, "zai_api_key") if runtime.model_provider == "zai" else None
|
|
client = OllamaClient(
|
|
base_url=runtime.local_base_url if runtime.model_provider == "local" else settings.zai_base_url,
|
|
provider=runtime.model_provider,
|
|
api_key=secret.value if secret else settings.zai_api_key,
|
|
)
|
|
return await client.status(runtime.local_model if runtime.model_provider == "local" else runtime.zai_model)
|
|
|
|
|
|
@router.get("/integrations/telegram", response_model=TelegramStatus)
|
|
def get_telegram_status(service: AdminService = Depends(get_admin_service)):
|
|
return service.telegram_status()
|
|
|
|
|
|
@router.get("/integrations/anythingllm", response_model=AnythingLLMStatus)
|
|
async def get_anythingllm_status(service: AdminService = Depends(get_admin_service)):
|
|
runtime = service.get_runtime_settings()
|
|
settings = get_app_settings()
|
|
secret = service.session.get(SecretORM, "anythingllm_api_key")
|
|
tool = SecondBrainTool(
|
|
base_url=runtime.anythingllm_base_url,
|
|
workspace_slug=runtime.anythingllm_workspace_slug,
|
|
api_key=secret.value if secret else settings.anythingllm_api_key,
|
|
)
|
|
status = await tool.status()
|
|
return AnythingLLMStatus(
|
|
reachable=bool(status.get("reachable")),
|
|
workspace_found=bool(status.get("workspace_found")),
|
|
base_url=runtime.anythingllm_base_url,
|
|
workspace_slug=runtime.anythingllm_workspace_slug,
|
|
message=str(status.get("message", "")),
|
|
)
|
|
|
|
|
|
@router.get("/integrations/google", response_model=GoogleIntegrationStatus)
|
|
def get_google_status(
|
|
request: Request,
|
|
service: AdminService = Depends(get_admin_service),
|
|
manager: GoogleAuthManager = Depends(get_google_auth_manager),
|
|
):
|
|
sync_google_client_file(service, manager)
|
|
client_configured, connected, message = manager.oauth_status()
|
|
connect_url = str(request.url_for("google_oauth_connect"))
|
|
return GoogleIntegrationStatus(
|
|
client_configured=client_configured,
|
|
connected=connected,
|
|
connect_url=connect_url,
|
|
message=message,
|
|
)
|
|
|
|
|
|
@router.get("/integrations/google/connect", name="google_oauth_connect")
|
|
def google_oauth_connect(
|
|
request: Request,
|
|
service: AdminService = Depends(get_admin_service),
|
|
manager: GoogleAuthManager = Depends(get_google_auth_manager),
|
|
):
|
|
redirect_uri = str(request.url_for("google_oauth_callback"))
|
|
try:
|
|
sync_google_client_file(service, manager)
|
|
authorization_url = manager.begin_web_oauth(redirect_uri)
|
|
except GoogleAuthError as exc:
|
|
return HTMLResponse(
|
|
(
|
|
"<html><body style='font-family: sans-serif; padding: 24px;'>"
|
|
f"<h2>Google connect failed</h2><p>{exc}</p>"
|
|
"<p>Add your client_secret.json file, then try the connect button again.</p>"
|
|
"</body></html>"
|
|
),
|
|
status_code=400,
|
|
)
|
|
return RedirectResponse(url=authorization_url)
|
|
|
|
|
|
@router.get("/integrations/google/callback", response_class=HTMLResponse, name="google_oauth_callback")
|
|
def google_oauth_callback(
|
|
request: Request,
|
|
state: str | None = None,
|
|
error: str | None = None,
|
|
manager: GoogleAuthManager = Depends(get_google_auth_manager),
|
|
):
|
|
if error:
|
|
return HTMLResponse(
|
|
(
|
|
"<html><body style='font-family: sans-serif; padding: 24px;'>"
|
|
f"<h2>Google connect failed</h2><p>{error}</p>"
|
|
"<p>You can close this tab and try again from the WiseClaw admin panel.</p>"
|
|
"</body></html>"
|
|
),
|
|
status_code=400,
|
|
)
|
|
|
|
if not state:
|
|
return HTMLResponse(
|
|
(
|
|
"<html><body style='font-family: sans-serif; padding: 24px;'>"
|
|
"<h2>Google connect failed</h2><p>Missing OAuth state.</p>"
|
|
"<p>You can close this tab and try again from the WiseClaw admin panel.</p>"
|
|
"</body></html>"
|
|
),
|
|
status_code=400,
|
|
)
|
|
|
|
try:
|
|
manager.complete_web_oauth(str(request.url_for("google_oauth_callback")), state, str(request.url))
|
|
except Exception as exc:
|
|
return HTMLResponse(
|
|
(
|
|
"<html><body style='font-family: sans-serif; padding: 24px;'>"
|
|
f"<h2>Google connect failed</h2><p>{exc}</p>"
|
|
"<p>You can close this tab and try again from the WiseClaw admin panel.</p>"
|
|
"</body></html>"
|
|
),
|
|
status_code=400,
|
|
)
|
|
|
|
return HTMLResponse(
|
|
(
|
|
"<html><body style='font-family: sans-serif; padding: 24px;'>"
|
|
"<h2>Google account connected</h2>"
|
|
"<p>WiseClaw can now use your Gmail and Google Drive tools.</p>"
|
|
"<p>You can close this tab and refresh the admin panel.</p>"
|
|
"</body></html>"
|
|
)
|
|
)
|