# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Cata-Log - the central hub for grocery store catalogs
# Copyright (C) 2026 David Aderbauer & The Cata-Log Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import functools
from ipaddress import IPv4Address
from pathlib import Path
from typing import Any
from platformdirs import user_data_path, user_log_path
from pydantic import Field, IPvAnyAddress, NonNegativeInt, PositiveInt, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
[docs]
class Settings(BaseSettings):
"""Configuration for the Cata-Log server. You can configure the settings with these command-line arguments or environment variables starting with *CATA_LOG_*."""
model_config = SettingsConfigDict(
env_prefix="CATA_LOG_",
cli_kebab_case=True,
cli_parse_args=True,
cli_ignore_unknown_args=True, # otherwise sphinx build fails
)
username: str = Field(default="admin", description="Username for authentication")
password: str = Field(
default="",
description="Password, keep this empty to allow no authentication at all.",
)
data_path: Path = Field(
default=user_data_path("cata-log-hub", appauthor=False, ensure_exists=False),
description="Path to the directory for Cata-Log's data. Use the following path options for detailed control.",
)
storage_path: Path = Field(
default=data_path.default / "storage",
description="Path to the storage for catalog page files.",
)
database_path: Path = Field(
default=data_path.default / "db",
description="Path to the directory for the database files.",
)
plugin_path: Path = Field(
default=data_path.default / "plugins",
description="Path to the plugin directory.",
)
logs_path: Path = Field(
default=user_log_path("cata-log-hub", appauthor=False, ensure_exists=False),
description="Path to the logfiles.",
)
external_database_url: str = Field(
default="",
description="URL of an external database. Only set this is you want to use an external database.",
)
dev_mode: bool = Field(
default=False, description="Whether to run in development mode."
)
public_get: bool = Field(
default=False,
description="Set this to allow access to all GET endpoints without authentication.",
)
request_timeout: PositiveInt = Field(
default=10, description="Timeout for requests to provider servers."
)
retry_delay: PositiveInt = Field(
default=1800,
description="Number of seconds to wait before retrying a failed job.",
)
expiration_days: NonNegativeInt = Field(
default=14,
description="Number of days after creation until old catalogs are deleted.",
)
log_level: str = Field(default="WARNING", description="Global loglevel")
log_file_backup_count: NonNegativeInt = Field(
default=5, description="Number of backup logfiles to keep."
)
log_file_maxsize: NonNegativeInt = Field(
default=2097152, description="Maximum size of a single logfile."
) # 2 MB
api_default_pagination_page_size: PositiveInt = Field(
default=50, description="Default size of a single paginated API response."
)
api_max_pagination_page_size: PositiveInt = Field(
default=100, description="Maximum size of a single paginated API response."
)
host: IPvAnyAddress = Field(
default=IPv4Address("127.0.0.1"),
description="The host address that Cata-Log should be served under.",
)
port: int = Field(
default=2424, ge=1, le=64435, description="Portnumber for the server"
)
forwarded_allow_ips: str = Field(
default="localhost,127.0.0.1",
description="Comma separated list of IP Addresses to trust with proxy headers",
)
workers: PositiveInt = Field(
default=1, description="Number of webworker processes to run."
)
[docs]
@field_validator("*", mode="after")
@classmethod
def ensure_dirs(cls, value: Any) -> Any: # noqa: ANN401 # no reason to be precise here
"""Create non-existent paths.
Returns:
The path.
"""
if isinstance(value, Path):
value.mkdir(exist_ok=True, parents=True)
return value
[docs]
@functools.lru_cache
def get_settings() -> Settings:
"""Get the current settings."""
return Settings()