11import os
22from enum import Enum
33
4- from pydantic import SecretStr
5- from pydantic_settings import BaseSettings
6- from starlette .config import Config
4+ from pydantic import SecretStr , computed_field , field_validator
5+ from pydantic_settings import BaseSettings , SettingsConfigDict
76
8- current_file_dir = os .path .dirname (os .path .realpath (__file__ ))
9- env_path = os .path .join (current_file_dir , ".." , ".." , ".env" )
10- config = Config (env_path )
117
12-
13- def str_setting_to_list (setting : str ) -> list [str ]:
14- if isinstance (setting , str ):
15- return [item .strip () for item in setting .split ("," ) if item .strip ()]
8+ def str_to_list (value : str ) -> list [str ]:
9+ if isinstance (value , str ):
10+ return [item .strip () for item in value .split ("," ) if item .strip ()]
1611 raise ValueError ("Invalid string setting for list conversion." )
1712
1813
1914class AppSettings (BaseSettings ):
20- APP_NAME : str = config ( "APP_NAME" , default = " FastAPI app")
21- APP_DESCRIPTION : str | None = config ( "APP_DESCRIPTION" , default = None )
22- APP_VERSION : str | None = config ( "APP_VERSION" , default = None )
23- LICENSE_NAME : str | None = config ( "LICENSE" , default = None )
24- CONTACT_NAME : str | None = config ( "CONTACT_NAME" , default = None )
25- CONTACT_EMAIL : str | None = config ( "CONTACT_EMAIL" , default = None )
15+ APP_NAME : str = " FastAPI app"
16+ APP_DESCRIPTION : str | None = None
17+ APP_VERSION : str | None = None
18+ LICENSE_NAME : str | None = None
19+ CONTACT_NAME : str | None = None
20+ CONTACT_EMAIL : str | None = None
2621
2722
2823class CryptSettings (BaseSettings ):
29- SECRET_KEY : SecretStr = config ( "SECRET_KEY" , cast = SecretStr )
30- ALGORITHM : str = config ( "ALGORITHM" , default = " HS256")
31- ACCESS_TOKEN_EXPIRE_MINUTES : int = config ( "ACCESS_TOKEN_EXPIRE_MINUTES" , default = 30 )
32- REFRESH_TOKEN_EXPIRE_DAYS : int = config ( "REFRESH_TOKEN_EXPIRE_DAYS" , default = 7 )
24+ SECRET_KEY : SecretStr
25+ ALGORITHM : str = " HS256"
26+ ACCESS_TOKEN_EXPIRE_MINUTES : int = 30
27+ REFRESH_TOKEN_EXPIRE_DAYS : int = 7
3328
3429
3530class DatabaseSettings (BaseSettings ):
3631 pass
3732
3833
3934class SQLiteSettings (DatabaseSettings ):
40- SQLITE_URI : str = config ( "SQLITE_URI" , default = " ./sql_app.db")
41- SQLITE_SYNC_PREFIX : str = config ( "SQLITE_SYNC_PREFIX" , default = " sqlite:///")
42- SQLITE_ASYNC_PREFIX : str = config ( "SQLITE_ASYNC_PREFIX" , default = " sqlite+aiosqlite:///")
35+ SQLITE_URI : str = " ./sql_app.db"
36+ SQLITE_SYNC_PREFIX : str = " sqlite:///"
37+ SQLITE_ASYNC_PREFIX : str = " sqlite+aiosqlite:///"
4338
4439
4540class MySQLSettings (DatabaseSettings ):
46- MYSQL_USER : str = config ("MYSQL_USER" , default = "username" )
47- MYSQL_PASSWORD : str = config ("MYSQL_PASSWORD" , default = "password" )
48- MYSQL_SERVER : str = config ("MYSQL_SERVER" , default = "localhost" )
49- MYSQL_PORT : int = config ("MYSQL_PORT" , default = 5432 )
50- MYSQL_DB : str = config ("MYSQL_DB" , default = "dbname" )
51- MYSQL_URI : str = f"{ MYSQL_USER } :{ MYSQL_PASSWORD } @{ MYSQL_SERVER } :{ MYSQL_PORT } /{ MYSQL_DB } "
52- MYSQL_SYNC_PREFIX : str = config ("MYSQL_SYNC_PREFIX" , default = "mysql://" )
53- MYSQL_ASYNC_PREFIX : str = config ("MYSQL_ASYNC_PREFIX" , default = "mysql+aiomysql://" )
54- MYSQL_URL : str | None = config ("MYSQL_URL" , default = None )
41+ MYSQL_USER : str = "username"
42+ MYSQL_PASSWORD : str = "password"
43+ MYSQL_SERVER : str = "localhost"
44+ MYSQL_PORT : int = 5432
45+ MYSQL_DB : str = "dbname"
46+ MYSQL_SYNC_PREFIX : str = "mysql://"
47+ MYSQL_ASYNC_PREFIX : str = "mysql+aiomysql://"
48+ MYSQL_URL : str | None = None
49+
50+ @computed_field
51+ @property
52+ def MYSQL_URI (self ) -> str :
53+ credentials = f"{ self .MYSQL_USER } :{ self .MYSQL_PASSWORD } "
54+ location = f"{ self .MYSQL_SERVER } :{ self .MYSQL_PORT } /{ self .MYSQL_DB } "
55+ return f"{ credentials } @{ location } "
5556
5657
5758class PostgresSettings (DatabaseSettings ):
58- POSTGRES_USER : str = config ("POSTGRES_USER" , default = "postgres" )
59- POSTGRES_PASSWORD : str = config ("POSTGRES_PASSWORD" , default = "postgres" )
60- POSTGRES_SERVER : str = config ("POSTGRES_SERVER" , default = "localhost" )
61- POSTGRES_PORT : int = config ("POSTGRES_PORT" , default = 5432 )
62- POSTGRES_DB : str = config ("POSTGRES_DB" , default = "postgres" )
63- POSTGRES_SYNC_PREFIX : str = config ("POSTGRES_SYNC_PREFIX" , default = "postgresql://" )
64- POSTGRES_ASYNC_PREFIX : str = config ("POSTGRES_ASYNC_PREFIX" , default = "postgresql+asyncpg://" )
65- POSTGRES_URI : str = f"{ POSTGRES_USER } :{ POSTGRES_PASSWORD } @{ POSTGRES_SERVER } :{ POSTGRES_PORT } /{ POSTGRES_DB } "
66- POSTGRES_URL : str | None = config ("POSTGRES_URL" , default = None )
59+ POSTGRES_USER : str = "postgres"
60+ POSTGRES_PASSWORD : str = "postgres"
61+ POSTGRES_SERVER : str = "localhost"
62+ POSTGRES_PORT : int = 5432
63+ POSTGRES_DB : str = "postgres"
64+ POSTGRES_SYNC_PREFIX : str = "postgresql://"
65+ POSTGRES_ASYNC_PREFIX : str = "postgresql+asyncpg://"
66+ POSTGRES_URL : str | None = None
67+
68+ @computed_field
69+ @property
70+ def POSTGRES_URI (self ) -> str :
71+ credentials = f"{ self .POSTGRES_USER } :{ self .POSTGRES_PASSWORD } "
72+ location = f"{ self .POSTGRES_SERVER } :{ self .POSTGRES_PORT } /{ self .POSTGRES_DB } "
73+ return f"{ credentials } @{ location } "
6774
6875
6976class FirstUserSettings (BaseSettings ):
70- ADMIN_NAME : str = config ( "ADMIN_NAME" , default = " admin")
71- ADMIN_EMAIL :
str = config ( "ADMIN_EMAIL" , default = " [email protected] ") 72- ADMIN_USERNAME : str = config ( "ADMIN_USERNAME" , default = " admin")
73- ADMIN_PASSWORD : str = config ( "ADMIN_PASSWORD" , default = " !Ch4ng3Th1sP4ssW0rd!")
77+ ADMIN_NAME : str = " admin"
78+ ADMIN_EMAIL :
str = " [email protected] " 79+ ADMIN_USERNAME : str = " admin"
80+ ADMIN_PASSWORD : str = " !Ch4ng3Th1sP4ssW0rd!"
7481
7582
7683class TestSettings (BaseSettings ):
7784 ...
7885
7986
8087class RedisCacheSettings (BaseSettings ):
81- REDIS_CACHE_HOST : str = config ("REDIS_CACHE_HOST" , default = "localhost" )
82- REDIS_CACHE_PORT : int = config ("REDIS_CACHE_PORT" , default = 6379 )
83- REDIS_CACHE_URL : str = f"redis://{ REDIS_CACHE_HOST } :{ REDIS_CACHE_PORT } "
88+ REDIS_CACHE_HOST : str = "localhost"
89+ REDIS_CACHE_PORT : int = 6379
90+
91+ @computed_field
92+ @property
93+ def REDIS_CACHE_URL (self ) -> str :
94+ return f"redis://{ self .REDIS_CACHE_HOST } :{ self .REDIS_CACHE_PORT } "
8495
8596
8697class ClientSideCacheSettings (BaseSettings ):
87- CLIENT_CACHE_MAX_AGE : int = config ( "CLIENT_CACHE_MAX_AGE" , default = 60 )
98+ CLIENT_CACHE_MAX_AGE : int = 60
8899
89100
90101class RedisQueueSettings (BaseSettings ):
91- REDIS_QUEUE_HOST : str = config ( "REDIS_QUEUE_HOST" , default = " localhost")
92- REDIS_QUEUE_PORT : int = config ( "REDIS_QUEUE_PORT" , default = 6379 )
102+ REDIS_QUEUE_HOST : str = " localhost"
103+ REDIS_QUEUE_PORT : int = 6379
93104
94105
95106class RedisRateLimiterSettings (BaseSettings ):
96- REDIS_RATE_LIMIT_HOST : str = config ("REDIS_RATE_LIMIT_HOST" , default = "localhost" )
97- REDIS_RATE_LIMIT_PORT : int = config ("REDIS_RATE_LIMIT_PORT" , default = 6379 )
98- REDIS_RATE_LIMIT_URL : str = f"redis://{ REDIS_RATE_LIMIT_HOST } :{ REDIS_RATE_LIMIT_PORT } "
107+ REDIS_RATE_LIMIT_HOST : str = "localhost"
108+ REDIS_RATE_LIMIT_PORT : int = 6379
109+
110+ @computed_field
111+ @property
112+ def REDIS_RATE_LIMIT_URL (self ) -> str :
113+ return f"redis://{ self .REDIS_RATE_LIMIT_HOST } :{ self .REDIS_RATE_LIMIT_PORT } "
99114
100115
101116class DefaultRateLimitSettings (BaseSettings ):
102- DEFAULT_RATE_LIMIT_LIMIT : int = config ( "DEFAULT_RATE_LIMIT_LIMIT" , default = 10 )
103- DEFAULT_RATE_LIMIT_PERIOD : int = config ( "DEFAULT_RATE_LIMIT_PERIOD" , default = 3600 )
117+ DEFAULT_RATE_LIMIT_LIMIT : int = 10
118+ DEFAULT_RATE_LIMIT_PERIOD : int = 3600
104119
105120
106121class CRUDAdminSettings (BaseSettings ):
107- CRUD_ADMIN_ENABLED : bool = config ( "CRUD_ADMIN_ENABLED" , default = True )
108- CRUD_ADMIN_MOUNT_PATH : str = config ( "CRUD_ADMIN_MOUNT_PATH" , default = " /admin")
122+ CRUD_ADMIN_ENABLED : bool = True
123+ CRUD_ADMIN_MOUNT_PATH : str = " /admin"
109124
110125 CRUD_ADMIN_ALLOWED_IPS_LIST : list [str ] | None = None
111126 CRUD_ADMIN_ALLOWED_NETWORKS_LIST : list [str ] | None = None
112- CRUD_ADMIN_MAX_SESSIONS : int = config ( "CRUD_ADMIN_MAX_SESSIONS" , default = 10 )
113- CRUD_ADMIN_SESSION_TIMEOUT : int = config ( "CRUD_ADMIN_SESSION_TIMEOUT" , default = 1440 )
114- SESSION_SECURE_COOKIES : bool = config ( "SESSION_SECURE_COOKIES" , default = True )
127+ CRUD_ADMIN_MAX_SESSIONS : int = 10
128+ CRUD_ADMIN_SESSION_TIMEOUT : int = 1440
129+ SESSION_SECURE_COOKIES : bool = True
115130
116- CRUD_ADMIN_TRACK_EVENTS : bool = config ( "CRUD_ADMIN_TRACK_EVENTS" , default = True )
117- CRUD_ADMIN_TRACK_SESSIONS : bool = config ( "CRUD_ADMIN_TRACK_SESSIONS" , default = True )
131+ CRUD_ADMIN_TRACK_EVENTS : bool = True
132+ CRUD_ADMIN_TRACK_SESSIONS : bool = True
118133
119- CRUD_ADMIN_REDIS_ENABLED : bool = config ( "CRUD_ADMIN_REDIS_ENABLED" , default = False )
120- CRUD_ADMIN_REDIS_HOST : str = config ( "CRUD_ADMIN_REDIS_HOST" , default = " localhost")
121- CRUD_ADMIN_REDIS_PORT : int = config ( "CRUD_ADMIN_REDIS_PORT" , default = 6379 )
122- CRUD_ADMIN_REDIS_DB : int = config ( "CRUD_ADMIN_REDIS_DB" , default = 0 )
123- CRUD_ADMIN_REDIS_PASSWORD : str | None = config ( "CRUD_ADMIN_REDIS_PASSWORD" , default = " None")
124- CRUD_ADMIN_REDIS_SSL : bool = config ( "CRUD_ADMIN_REDIS_SSL" , default = False )
134+ CRUD_ADMIN_REDIS_ENABLED : bool = False
135+ CRUD_ADMIN_REDIS_HOST : str = " localhost"
136+ CRUD_ADMIN_REDIS_PORT : int = 6379
137+ CRUD_ADMIN_REDIS_DB : int = 0
138+ CRUD_ADMIN_REDIS_PASSWORD : str | None = " None"
139+ CRUD_ADMIN_REDIS_SSL : bool = False
125140
126141
127- class EnvironmentOption (Enum ):
142+ class EnvironmentOption (str , Enum ):
128143 LOCAL = "local"
129144 STAGING = "staging"
130145 PRODUCTION = "production"
131146
132147
133148class EnvironmentSettings (BaseSettings ):
134- ENVIRONMENT : EnvironmentOption = config ( "ENVIRONMENT" , default = EnvironmentOption .LOCAL )
149+ ENVIRONMENT : EnvironmentOption = EnvironmentOption .LOCAL
135150
136151
137152class CORSSettings (BaseSettings ):
138- CORS_ORIGINS : list [str ] = config ("CORS_ORIGINS" , cast = str_setting_to_list , default = "*" )
139- CORS_METHODS : list [str ] = config ("CORS_METHODS" , cast = str_setting_to_list , default = "*" )
140- CORS_HEADERS : list [str ] = config ("CORS_HEADERS" , cast = str_setting_to_list , default = "*" )
153+ CORS_ORIGINS : list [str ] | str = "*"
154+ CORS_METHODS : list [str ] | str = "*"
155+ CORS_HEADERS : list [str ] | str = "*"
156+
157+ _normalize_to_list = field_validator ("CORS_ORIGINS" , "CORS_METHODS" , "CORS_HEADERS" , mode = "before" )(str_to_list )
141158
142159
143160class Settings (
@@ -156,7 +173,12 @@ class Settings(
156173 EnvironmentSettings ,
157174 CORSSettings ,
158175):
159- pass
176+ model_config = SettingsConfigDict (
177+ env_file = os .path .join (os .path .dirname (os .path .realpath (__file__ )), ".." , ".." , ".env" ),
178+ env_file_encoding = "utf-8" ,
179+ case_sensitive = True ,
180+ extra = "ignore" ,
181+ )
160182
161183
162184settings = Settings ()
0 commit comments