"""Configuration"""
from __future__ import annotations
from typing import ClassVar, Optional, List, Dict
from pathlib import Path
from enum import StrEnum
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
[docs]
class KeyType(StrEnum):
"""Valid key types for certbot/mkcert"""
[docs]
class ProductSettings(BaseSettings):
"""Configs for each product"""
[docs]
api_port: int = Field(description="port for the API")
[docs]
class MWConfig(BaseSettings):
"""Config for MiniWerk"""
# Things you must or should define
[docs]
domain: str = Field(description="Domain under which we operate")
[docs]
le_email: str = Field(description="email given to letsencrypt")
[docs]
le_test: bool = Field(default=True, description="Use LE staging/test env")
[docs]
subdomains: str = Field(default="mtls", description="Comma separated list of extra subdomains to get certs for")
[docs]
products: str = Field(
default="fake,tak", description="Comma separated list of products to create manifests and get subdomains for"
)
[docs]
fake: ProductSettings = Field(
description="Setting for fakeproduct intration API", default_factory=lambda: ProductSettings(api_port=4625)
)
[docs]
tak: ProductSettings = Field(
description="Setting for TAK intration API", default_factory=lambda: ProductSettings(api_port=4626)
)
[docs]
rasenmaeher: ProductSettings = Field(
description="Setting for RASENMAEHER API", default_factory=lambda: ProductSettings(api_port=443)
)
[docs]
le_cert_name: str = Field(default="rasenmaeher", description="--cert-name for LE, used to determine directory name")
[docs]
le_copy_path: Path = Field(default="/le_certs", description="Where to copy letsencrypt certs and keys")
[docs]
data_path: Path = Field(default="/data/persistent", description="Where do we keep our data")
[docs]
manifests_base: Path = Field(
default="/pvarkishares", description="Path for manifests etc, each product gets a subdir"
)
[docs]
mkcert: bool = Field(default=False, description="Use mkcert instead of certbot")
[docs]
ci: bool = Field(default=False, alias="CI", description="Are we running in CI")
[docs]
keytype: KeyType = Field(default="ecdsa", description="Which key types to use, rsa or ecdsa (default)")
[docs]
model_config = SettingsConfigDict(env_prefix="mw_", env_file=".env", extra="ignore", env_nested_delimiter="__")
[docs]
_singleton: ClassVar[Optional[MWConfig]] = None
@classmethod
[docs]
def singleton(cls) -> MWConfig:
"""Return singleton"""
if not MWConfig._singleton:
MWConfig._singleton = MWConfig() # type: ignore[call-arg]
return MWConfig._singleton
@property
[docs]
def le_config_path(self) -> Path:
"""LE configuration dir"""
return self.data_path / "le" / "conf"
@property
[docs]
def mkcert_path(self) -> Path:
"""mkcert certs dir"""
return self.data_path / "mkcert"
@property
[docs]
def le_work_path(self) -> Path:
"""LE work dir (doesn't seem to actually hold anything persistent)"""
return self.data_path / "le" / "work"
@property
[docs]
def le_cert_dir(self) -> Path:
"""The "live" dir for the cert we have, remember the "file" here are symlinks"""
return self.le_config_path / "live" / self.le_cert_name
@property
[docs]
def mk_cert_dir(self) -> Path:
"""The "live" dir for the cert we have"""
return self.mkcert_path / self.le_cert_name
@property
[docs]
def fqdns(self) -> List[str]:
"""Main domain and all subdomains and FQDNs"""
ret = [f"{subd.strip()}.{self.domain}" for subd in str(self.subdomains).split(",")]
for proddomain in [f"{prod.strip()}.{self.domain}" for prod in str(self.products).split(",")]:
ret.append(proddomain)
ret += [f"{subd.strip()}.{proddomain}" for subd in str(self.subdomains).split(",")]
ret.append(self.domain)
return ret
@property
[docs]
def product_manifest_paths(self) -> Dict[str, Path]:
"""Paths for product manifests keyed by product"""
return {prod.strip(): self.manifests_base / prod.strip() for prod in str(self.products).split(",")}