|
1 | | -import collections |
2 | 1 | import os |
3 | 2 | import secrets |
4 | 3 | from datetime import timedelta |
5 | 4 | from functools import cache |
6 | 5 | from typing import Any, List, Optional |
7 | 6 |
|
8 | | -from pydantic_settings import BaseSettings |
| 7 | +from pydantic import Field |
| 8 | +from pydantic.dataclasses import dataclass |
| 9 | +from pydantic_settings import BaseSettings, SettingsConfigDict |
9 | 10 |
|
10 | | -DatabaseSettings = collections.namedtuple( |
11 | | - "DatabaseSettings", "uri pool_size pool_pre_ping max_overflow" |
12 | | -) |
| 11 | + |
| 12 | +# hashable cache key for use in tiled.authn_database.connection_pool |
| 13 | +@dataclass(unsafe_hash=True) |
| 14 | +class DatabaseSettings: |
| 15 | + uri: Optional[str] = None |
| 16 | + pool_size: int = 5 |
| 17 | + pool_pre_ping: bool = True |
| 18 | + max_overflow: int = 5 |
13 | 19 |
|
14 | 20 |
|
15 | 21 | class Settings(BaseSettings): |
16 | 22 | tree: Any = None |
17 | | - allow_anonymous_access: bool = bool( |
18 | | - int(os.getenv("TILED_ALLOW_ANONYMOUS_ACCESS", False)) |
19 | | - ) |
20 | | - allow_origins: List[str] = [ |
21 | | - item for item in os.getenv("TILED_ALLOW_ORIGINS", "").split() if item |
22 | | - ] |
| 23 | + allow_anonymous_access: bool = False |
| 24 | + allow_origins: List[str] = Field(default_factory=list) |
23 | 25 | authenticator: Any = None |
24 | 26 | # These 'single user' settings are only applicable if authenticator is None. |
25 | | - single_user_api_key: str = os.getenv( |
26 | | - "TILED_SINGLE_USER_API_KEY", secrets.token_hex(32) |
27 | | - ) |
28 | | - single_user_api_key_generated: bool = not ( |
29 | | - "TILED_SINGLE_USER_API_KEY" in os.environ |
30 | | - ) |
| 27 | + single_user_api_key: str = secrets.token_hex(32) |
| 28 | + single_user_api_key_generated: bool = "TILED_SINGLE_USER_API_KEY" not in os.environ |
31 | 29 | # The TILED_SERVER_SECRET_KEYS may be a single key or a ;-separated list of |
32 | 30 | # keys to support key rotation. The first key will be used for encryption. Each |
33 | 31 | # key will be tried in turn for decryption. |
34 | | - secret_keys: List[str] = os.getenv( |
35 | | - "TILED_SERVER_SECRET_KEYS", secrets.token_hex(32) |
36 | | - ).split(";") |
37 | | - access_token_max_age: timedelta = timedelta( |
38 | | - seconds=int(os.getenv("TILED_ACCESS_TOKEN_MAX_AGE", 15 * 60)) # 15 minutes |
39 | | - ) |
40 | | - refresh_token_max_age: timedelta = timedelta( |
41 | | - seconds=int( |
42 | | - os.getenv("TILED_REFRESH_TOKEN_MAX_AGE", 7 * 24 * 60 * 60) |
43 | | - ) # 7 days |
44 | | - ) |
45 | | - session_max_age: Optional[timedelta] = timedelta( |
46 | | - seconds=int(os.getenv("TILED_SESSION_MAX_AGE", 365 * 24 * 60 * 60)) # 365 days |
47 | | - ) |
| 32 | + secret_keys: List[str] = [secrets.token_hex(32)] |
| 33 | + access_token_max_age: timedelta = 15 * 60 # 15 minutes |
| 34 | + refresh_token_max_age: timedelta = 7 * 24 * 60 * 60 # 7 days |
| 35 | + session_max_age: timedelta = 365 * 24 * 60 * 60 # 365 days |
48 | 36 | # Put a fairly low limit on the maximum size of one chunk, keeping in mind |
49 | 37 | # that data should generally be chunked. When we implement async responses, |
50 | 38 | # we can raise this global limit. |
51 | | - response_bytesize_limit: int = int( |
52 | | - os.getenv("TILED_RESPONSE_BYTESIZE_LIMIT", 300_000_000) |
53 | | - ) # 300 MB |
54 | | - reject_undeclared_specs: bool = bool( |
55 | | - int(os.getenv("TILED_REJECT_UNDECLARED_SPECS", 0)) |
56 | | - ) |
57 | | - database_uri: Optional[str] = os.getenv("TILED_DATABASE_URI") |
58 | | - database_init_if_not_exists: bool = int( |
59 | | - os.getenv("TILED_DATABASE_INIT_IF_NOT_EXISTS", False) |
60 | | - ) |
61 | | - database_pool_size: Optional[int] = int(os.getenv("TILED_DATABASE_POOL_SIZE", 5)) |
62 | | - database_pool_pre_ping: Optional[bool] = bool( |
63 | | - int(os.getenv("TILED_DATABASE_POOL_PRE_PING", 1)) |
64 | | - ) |
65 | | - database_max_overflow: Optional[int] = int( |
66 | | - os.getenv("TILED_DATABASE_MAX_OVERFLOW", 5) |
67 | | - ) |
| 39 | + response_bytesize_limit: int = 300_000_000 # 300 MB |
| 40 | + reject_undeclared_specs: bool = False |
| 41 | + database_settings: DatabaseSettings = Field(DatabaseSettings(), alias="database") |
| 42 | + database_init_if_not_exists: bool = False |
68 | 43 | expose_raw_assets: bool = True |
69 | 44 |
|
70 | | - @property |
71 | | - def database_settings(self): |
72 | | - # The point of this alias is to return a hashable cache key for use in |
73 | | - # the module tiled.authn_database.connection_pool. |
74 | | - return DatabaseSettings( |
75 | | - uri=self.database_uri, |
76 | | - pool_size=self.database_pool_size, |
77 | | - pool_pre_ping=self.database_pool_pre_ping, |
78 | | - max_overflow=self.database_max_overflow, |
79 | | - ) |
| 45 | + model_config = SettingsConfigDict( |
| 46 | + env_prefix="TILED_", nested_model_default_partial_update=True |
| 47 | + ) |
80 | 48 |
|
81 | 49 |
|
82 | 50 | @cache |
|
0 commit comments