diff --git a/api/ovdashboard_api/config.py b/api/ovdashboard_api/config.py index 77f2bd7..55f1fd0 100644 --- a/api/ovdashboard_api/config.py +++ b/api/ovdashboard_api/config.py @@ -23,6 +23,7 @@ class TickerConfig(BaseModel): Section "[ticker]" in "config.txt". """ + file_name: str = "ticker" separator: str = " +++ " comment_marker: str = "#" color: str = "primary" @@ -55,6 +56,9 @@ class Config(BaseModel): Main representation of "config.txt". """ + image_dir: str = "image" + text_dir: str = "text" + ticker: TickerConfig = TickerConfig() image: ImageConfig = ImageConfig() calendar: CalendarConfig = CalendarConfig() diff --git a/api/ovdashboard_api/dav_common.py b/api/ovdashboard_api/dav_common.py index f93db1e..c401436 100644 --- a/api/ovdashboard_api/dav_common.py +++ b/api/ovdashboard_api/dav_common.py @@ -68,6 +68,23 @@ def webdav_check() -> bool: return True +def webdav_ensure_path(remote_path: str) -> None: + remote_path = f"{SETTINGS.webdav_prefix}/{remote_path}" + + if _WEBDAV_CLIENT.check(remote_path): + _logger.debug( + "WebDAV path %s found.", + repr(remote_path), + ) + return + + _logger.info( + "WebDAV path %s not found, creating ...", + repr(remote_path), + ) + _WEBDAV_CLIENT.mkdir(remote_path) + + @lru_cache(maxsize=SETTINGS.cache_size) def webdav_resource(remote_path: Any) -> WebDAVResource: """ diff --git a/api/ovdashboard_api/routers/_common.py b/api/ovdashboard_api/routers/_common.py index ce55049..3a95969 100644 --- a/api/ovdashboard_api/routers/_common.py +++ b/api/ovdashboard_api/routers/_common.py @@ -4,6 +4,7 @@ Dependables for defining Routers. import re from dataclasses import dataclass +from logging import getLogger from typing import Iterator, Protocol from fastapi import HTTPException, status @@ -12,6 +13,8 @@ from webdav3.exceptions import RemoteResourceNotFound from ..config import Config from ..dav_common import caldav_list, webdav_list +_logger = getLogger(__name__) + class NameLister(Protocol): """ @@ -37,7 +40,7 @@ class FileNameLister: File names listed will be in `remote_path` and will match the RegEx `re`. """ - remote_path: str + path_name: str re: re.Pattern[str] @property @@ -45,14 +48,20 @@ class FileNameLister: return { **_RESPONSE_OK, status.HTTP_404_NOT_FOUND: { - "description": f"{self.remote_path!r} not found", + "description": f"{self.path_name!r} not found", "content": None, }, } + @property + async def remote_path(self) -> str: + cfg = await Config.get() + + return str(cfg.dict()[self.path_name]) + async def __call__(self) -> Iterator[str]: try: - file_names = await webdav_list(self.remote_path) + file_names = await webdav_list(await self.remote_path) return ( name @@ -61,6 +70,10 @@ class FileNameLister: ) except RemoteResourceNotFound: + _logger.error( + "WebDAV path %s lost!", + repr(await self.remote_path), + ) raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) diff --git a/api/ovdashboard_api/routers/image.py b/api/ovdashboard_api/routers/image.py index 2ef8ab9..0633375 100644 --- a/api/ovdashboard_api/routers/image.py +++ b/api/ovdashboard_api/routers/image.py @@ -8,6 +8,7 @@ Router "image" provides: import re from io import BytesIO +from logging import getLogger from typing import Iterator from fastapi import APIRouter, Depends @@ -15,13 +16,16 @@ from fastapi.responses import StreamingResponse from PIL import Image from ..config import Config +from ..dav_common import webdav_ensure_path from ..dav_file import DavFile from ._common import FileNameLister, PrefixFinder, PrefixUnique +_logger = getLogger(__name__) + router = APIRouter(prefix="/image", tags=["image"]) image_lister = FileNameLister( - remote_path="img", + path_name="image_dir", re=re.compile( r"\.(gif|jpe?g|tiff?|png|bmp)$", flags=re.IGNORECASE, @@ -32,6 +36,13 @@ image_finder = PrefixFinder(image_lister) image_unique = PrefixUnique(image_finder) +@router.on_event("startup") +async def start_router() -> None: + _logger.debug(f"{router.prefix} router starting.") + + webdav_ensure_path(await image_lister.remote_path) + + @router.get( "/list", response_model=list[str], @@ -65,7 +76,7 @@ async def get_image( ) -> StreamingResponse: cfg = await Config.get() - dav_file = DavFile(f"{image_lister.remote_path}/{name}") + dav_file = DavFile(f"{await image_lister.remote_path}/{name}") img = Image.open( BytesIO(await dav_file.as_bytes) ).convert( diff --git a/api/ovdashboard_api/routers/text.py b/api/ovdashboard_api/routers/text.py index 30eb511..4b3034b 100644 --- a/api/ovdashboard_api/routers/text.py +++ b/api/ovdashboard_api/routers/text.py @@ -10,19 +10,23 @@ Router "text" provides: """ import re +from logging import getLogger from typing import Iterator from fastapi import APIRouter, Depends from markdown import markdown from ..config import Config +from ..dav_common import webdav_ensure_path from ..dav_file import DavFile from ._common import FileNameLister, PrefixFinder, PrefixUnique +_logger = getLogger(__name__) + router = APIRouter(prefix="/text", tags=["text"]) text_lister = FileNameLister( - remote_path="text", + path_name="text_dir", re=re.compile( r"\.(txt|md)$", flags=re.IGNORECASE, @@ -33,8 +37,20 @@ text_finder = PrefixFinder(text_lister) text_unique = PrefixUnique(text_finder) +@router.on_event("startup") +async def start_router() -> None: + _logger.debug(f"{router.prefix} router starting.") + + webdav_ensure_path(await text_lister.remote_path) + + async def get_ticker_lines() -> Iterator[str]: - ticker = await DavFile("text/ticker.txt").as_string + cfg = await Config.get() + file_name = await text_unique(cfg.ticker.file_name) + + ticker = await DavFile( + f"{await text_lister.remote_path}/{file_name}", + ).as_string return ( line.strip() @@ -105,7 +121,9 @@ async def find_texts( async def get_text_content( name: str = Depends(text_unique), ) -> str: - return await DavFile(f"{text_lister.remote_path}/{name}").as_string + return await DavFile( + f"{await text_lister.remote_path}/{name}", + ).as_string @router.get(