diff --git a/docker-compose.yml b/docker-compose.yml index e68ba8cc11e504af1411bf3aa95f4684e445656d..d997a5dca8139e8dc9310823b7a8b975afee966a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,14 +11,11 @@ services: build: context: . ports: - - ${port:-8080}:${port:-8080} + - ${REPDAV_PORT:-8080}:${REPDAV_PORT:-8080} environment: - host: ${host:-0.0.0.0} - port: ${port:-8080} - sentry_dsn: ${sentry_dsn} - tg_auth_wsdl: "https://textgridlab.org/1.0/tgauth/wsdl/tgextra.wsdl" - tg_auth_address: "https://textgridlab.org/1.0/tgauth/tgextra.php" - tg_nav_address: "https://textgridlab.org/1.0/tgsearch/navigation/" - tg_host: "https://textgridlab.org/" + REPDAV_HOST: ${REPDAV_HOST:-0.0.0.0} + REPDAV_PORT: ${REPDAV_PORT:-8080} + SENTRY_DSN: ${SENTRY_DSN} + TEXTGRID_HOST: ${TEXTGRID_HOST} volumes: - $PWD/src:/app diff --git a/src/main.py b/src/main.py index 1bd0276e17ec7206b897ea9d8e3a589317adc3ad..3bf7cd8d74c5afa3e07b2c6c66d06eb864a3b7c6 100644 --- a/src/main.py +++ b/src/main.py @@ -4,8 +4,6 @@ """Configure and start the WSGI DAV Server.""" import logging -import os - import sentry_sdk from cheroot import wsgi from wsgidav.wsgidav_app import WsgiDAVApp @@ -14,13 +12,11 @@ from repdav.config import AppConfig app_config = AppConfig() if app_config.dsn: - sentry_sdk.init( - app_config.dsn, - traces_sample_rate=1.0 - ) + sentry_sdk.init(app_config.dsn, traces_sample_rate=1.0) -logging.basicConfig(level=logging.DEBUG, - format='%(name)s %(levelname)s %(asctime)s %(message)s') +logging.basicConfig( + level=logging.DEBUG, format="%(name)s %(levelname)s %(asctime)s %(message)s" +) _logger = logging.getLogger(__name__) _logger.propagate = True @@ -31,12 +27,13 @@ tmp_logger.setLevel(logging.DEBUG) # Configuration of the WsgiDAVApp. # https://wsgidav.readthedocs.io/en/latest/user_guide_configure.html -# TODO: move to config.py config = { - "host": os.getenv("host") or "localhost", - "port": int(os.getenv("port") or "8080"), + "host": app_config.host, + "port": app_config.port, "provider_mapping": { - "/": {"class": "repdav.textgrid_named_dav_provider.TextgridNamedResourceProvider"}, + "/": { + "class": "repdav.textgrid_named_dav_provider.TextgridNamedResourceProvider" + }, }, "verbose": 4, "http_authenticator": { @@ -47,7 +44,8 @@ config = { "default_to_digest": False, # Name of a header field that will be accepted as authorized user "trusted_auth_header": None, - } + }, + "tg_host": app_config.tg_host, } app = WsgiDAVApp(config) diff --git a/src/repdav/config.py b/src/repdav/config.py index 1d5553fae4789f091489e0b3ea022e2c15d22ab4..b3329bff835776b27920ecf623db28404ef786c4 100644 --- a/src/repdav/config.py +++ b/src/repdav/config.py @@ -3,63 +3,51 @@ # SPDX-License-Identifier: CC0-1.0 import os +from typing import Optional +from urllib.parse import urlparse -from .errors import EnvNotSetError -# TODO: remove "tg_auth_wsdl", "tg_auth_address", "tg_nav_address" +def check_url(url): + """Check if `url` can be parsed.""" + if url: + result = urlparse(url) + if result.scheme and result.netloc: + return url + raise ValueError(f"{url} is not a valid URL.") -def lookup_env_name(internal_name: str) -> str: - mapping = { - "_auth_wsdl": "tg_auth_wsdl", - "_auth_address": "tg_auth_address", - "_dsn": "sentry_dsn", - "_host": "tg_host", - "_nav_address": "tg_nav_address", - } - return mapping[internal_name] +def check_port(port): + """Check if port is set correctly to a non-privileged value.""" + if 1000 <= port <= 65535: + return port + raise ValueError(f"{port} is not a valid Port.") -# TODO check for trailing "/", add if missing! -# TODO check URLs for validity - class AppConfig: - # TODO: configure the app according to the set environment (i.e. prod or dev) - def __init__(self) -> None: - self._dsn = os.getenv(lookup_env_name("_dsn")) - - @property - def dsn(self) -> str: - return self._dsn + """Settings that can be be configured from the environment.""" - -class TextgridConfig: def __init__(self) -> None: - self._auth_wsdl = os.getenv(lookup_env_name("_auth_wsdl")) - self._auth_address = os.getenv(lookup_env_name("_auth_address")) - self._nav_address = os.getenv(lookup_env_name("_nav_address")) - self._host = os.getenv(lookup_env_name("_host")) + self._dsn = check_url(os.getenv("SENTRY_DSN")) + self._host = os.getenv("REPDAV_HOST") or "localhost" + self._port = check_port(int(os.getenv("REPDAV_PORT") or 8080)) + self._tg_host = check_url(os.getenv("TEXTGRID_HOST")) @property - def auth_wsdl(self) -> str: - if self._auth_wsdl: - return self._auth_wsdl - raise EnvNotSetError(lookup_env_name("_auth_wsdl")) + def dsn(self) -> Optional[str]: + """Sentry DSN.""" + return self._dsn @property - def auth_address(self) -> str: - if self._auth_address: - return self._auth_address - raise EnvNotSetError(lookup_env_name("_auth_address")) + def host(self) -> str: + """Service hostname.""" + return self._host @property - def nav_address(self) -> str: - if self._nav_address: - return self._nav_address - raise EnvNotSetError(lookup_env_name("_nav_address")) + def port(self) -> int: + """Service port.""" + return self._port @property - def host(self) -> str: - if self._host: - return self._host - raise EnvNotSetError(lookup_env_name("_host")) + def tg_host(self) -> Optional[str]: + """Textgrid host URL.""" + return self._tg_host diff --git a/src/repdav/textgrid_dav_provider.py b/src/repdav/textgrid_dav_provider.py index cbdf7861d23316598dff54860ed1a5e6f6e05e6e..cc8a89a58b2048a558f8ef15d895624a865a446a 100644 --- a/src/repdav/textgrid_dav_provider.py +++ b/src/repdav/textgrid_dav_provider.py @@ -21,6 +21,16 @@ from repdav.stream_tools import FileLikeQueue _logger = logging.getLogger(__name__) +def tg_config(environ): + """Initialize Textgrid configuration either with or without host setting. This + works around an [issue with tgclients](https://gitlab.gwdg.de/dariah-de/textgridrep/textgrid-python-clients/-/issues/59). + """ + tg_host = environ["wsgidav.config"]["tg_host"] + if tg_host: + return TextgridConfig(tg_host) + return TextgridConfig() + + class TextgridRoot(DAVCollection): """Top level collection that incorporates Textgrid projects. @@ -30,8 +40,9 @@ class TextgridRoot(DAVCollection): def __init__(self, path, environ): DAVCollection.__init__(self, path, environ) self._sid = environ["wsgidav.auth.user_name"] - config = TextgridConfig() + config = tg_config(environ) self._auth = TextgridAuth(config) + self.projects = () def get_display_info(self): return {"type": "Textgrid root collection"} @@ -83,7 +94,7 @@ class TextgridProject(DAVCollection): _logger.debug("Called TextgridProject.__init__(self, %s, environ).", path) DAVCollection.__init__(self, path, environ) self._sid = environ["wsgidav.auth.user_name"] - config = TextgridConfig() + config = tg_config(environ) self._tgsearch = TextgridSearch(config.search) self._project_id = self.path.split("/")[-1] @@ -175,7 +186,7 @@ class TextgridAggregation(DAVCollection): DAVCollection.__init__(self, path, environ) self._sid = environ["wsgidav.auth.user_name"] self._info = info - config = TextgridConfig() + config = tg_config(environ) self._tgsearch = TextgridSearch(config.search) self._tguri = self.path.split("/")[-1] @@ -261,7 +272,7 @@ class TextgridResource(DAVNonCollection): self._tguri = self.path.split("/")[-1] self._info = info self.upload_thread = None - config = TextgridConfig() + config = tg_config(environ) self._crud = TextgridCRUD(config.crud) def get_content_length(self): @@ -273,7 +284,9 @@ class TextgridResource(DAVNonCollection): return self._info[self.name]["format"] def get_content(self): - _logger.debug("Called TextgridResource.get_content(self) with path: %s", self.path) + _logger.debug( + "Called TextgridResource.get_content(self) with path: %s", self.path + ) return io.BytesIO(self._crud.read_data(self._tguri, self._sid).content) def get_content_title(self):