155 lines
4.8 KiB
Python
155 lines
4.8 KiB
Python
import os
|
|
from httpx import (
|
|
AsyncClient,
|
|
Auth,
|
|
BasicAuth,
|
|
Request,
|
|
Response,
|
|
)
|
|
import logging
|
|
|
|
from .notes_client import NotesClient
|
|
from .webdav_client import WebDAVClient
|
|
from .controllers.notes_search import NotesSearchController
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def log_request(request: Request):
|
|
logger.info(
|
|
"Request event hook: %s %s - Waiting for content",
|
|
request.method,
|
|
request.url,
|
|
)
|
|
logger.info("Request body: %s", request.content)
|
|
logger.info("Headers: %s", request.headers)
|
|
|
|
|
|
def log_response(response: Response):
|
|
response.read() # Explicitly read the stream before accessing .text
|
|
logger.info("Response [%s] %s", response.status_code, response.text)
|
|
|
|
|
|
class NextcloudClient:
|
|
"""Main Nextcloud client that orchestrates all app clients."""
|
|
|
|
def __init__(self, base_url: str, username: str, auth: Auth | None = None):
|
|
self.username = username
|
|
self._client = AsyncClient(
|
|
base_url=base_url,
|
|
auth=auth,
|
|
# event_hooks={"request": [log_request], "response": [log_response]},
|
|
)
|
|
|
|
# Initialize app clients
|
|
self.notes = NotesClient(self._client, username)
|
|
self.webdav = WebDAVClient(self._client, username)
|
|
|
|
# Initialize controllers
|
|
self._notes_search = NotesSearchController()
|
|
|
|
@classmethod
|
|
def from_env(cls):
|
|
logger.info("Creating NC Client using env vars")
|
|
|
|
host = os.environ["NEXTCLOUD_HOST"]
|
|
username = os.environ["NEXTCLOUD_USERNAME"]
|
|
password = os.environ["NEXTCLOUD_PASSWORD"]
|
|
# Pass username to constructor
|
|
return cls(base_url=host, username=username, auth=BasicAuth(username, password))
|
|
|
|
async def capabilities(self):
|
|
response = await self._client.get(
|
|
"/ocs/v2.php/cloud/capabilities",
|
|
headers={"OCS-APIRequest": "true", "Accept": "application/json"},
|
|
)
|
|
response.raise_for_status()
|
|
|
|
return response.json()
|
|
|
|
# Convenience methods that delegate to subclients
|
|
async def notes_get_settings(self):
|
|
"""Get Notes app settings."""
|
|
return await self.notes.get_settings()
|
|
|
|
async def notes_get_all(self):
|
|
"""Get all notes."""
|
|
return await self.notes.get_all_notes()
|
|
|
|
async def notes_get_note(self, *, note_id: int):
|
|
"""Get a specific note."""
|
|
return await self.notes.get_note(note_id)
|
|
|
|
async def notes_create_note(
|
|
self,
|
|
*,
|
|
title: str | None = None,
|
|
content: str | None = None,
|
|
category: str | None = None,
|
|
):
|
|
"""Create a new note."""
|
|
return await self.notes.create_note(
|
|
title=title, content=content, category=category
|
|
)
|
|
|
|
async def notes_update_note(
|
|
self,
|
|
*,
|
|
note_id: int,
|
|
etag: str,
|
|
title: str | None = None,
|
|
content: str | None = None,
|
|
category: str | None = None,
|
|
):
|
|
"""Update a note."""
|
|
return await self.notes.update(
|
|
note_id=note_id, etag=etag, title=title, content=content, category=category
|
|
)
|
|
|
|
async def notes_append_content(self, *, note_id: int, content: str):
|
|
"""Append content to an existing note with a separator."""
|
|
return await self.notes.append_content(note_id=note_id, content=content)
|
|
|
|
async def notes_search_notes(self, *, query: str):
|
|
"""Search notes using token-based matching with relevance ranking."""
|
|
all_notes = await self.notes.get_all_notes()
|
|
return self._notes_search.search_notes(all_notes, query)
|
|
|
|
async def notes_delete_note(self, *, note_id: int):
|
|
"""Delete a note and its attachments."""
|
|
return await self.notes.delete_note(note_id)
|
|
|
|
async def add_note_attachment(
|
|
self,
|
|
*,
|
|
note_id: int,
|
|
filename: str,
|
|
content: bytes,
|
|
category: str | None = None,
|
|
mime_type: str | None = None,
|
|
):
|
|
"""Add/Update an attachment to a note via WebDAV PUT."""
|
|
return await self.webdav.add_note_attachment(
|
|
note_id=note_id,
|
|
filename=filename,
|
|
content=content,
|
|
category=category,
|
|
mime_type=mime_type,
|
|
)
|
|
|
|
async def get_note_attachment(
|
|
self, *, note_id: int, filename: str, category: str | None = None
|
|
):
|
|
"""Fetch a specific attachment from a note via WebDAV GET."""
|
|
return await self.webdav.get_note_attachment(
|
|
note_id=note_id, filename=filename, category=category
|
|
)
|
|
|
|
def _get_webdav_base_path(self) -> str:
|
|
"""Helper to get the base WebDAV path for the authenticated user."""
|
|
return f"/remote.php/dav/files/{self.username}"
|
|
|
|
async def close(self):
|
|
"""Close the HTTP client."""
|
|
await self._client.aclose()
|