Coverage for src/kwai/core/settings.py: 97%

97 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2024-01-01 00:00 +0000

1"""Module for the settings of this application.""" 

2 

3import os 

4import tomllib 

5 

6from functools import lru_cache 

7from typing import Annotated, Any, Literal, Optional, Union 

8 

9from pydantic import BaseModel, Field 

10 

11 

12ENV_SETTINGS_FILE = "KWAI_SETTINGS_FILE" 

13 

14 

15class SettingsException(Exception): 

16 """Raised when a problem occurred while loading the settings.""" 

17 

18 

19class ApplicationSettingsModel(BaseModel): 

20 """Settings that apply to all applications.""" 

21 

22 default: bool = False # Set to true for selecting the default application 

23 vite_server: str | None = None # Only required in development. 

24 variables: dict[str, Any] | None = None 

25 

26 

27class AdminApplicationSettings(ApplicationSettingsModel): 

28 """Settings for the admin application.""" 

29 

30 name: Literal["admin"] 

31 

32 

33class AuthenticationApplicationSettings(ApplicationSettingsModel): 

34 """Settings for the auth application.""" 

35 

36 name: Literal["auth"] 

37 

38 

39class AuthorApplicationSettings(ApplicationSettingsModel): 

40 """Settings for the author application.""" 

41 

42 name: Literal["author"] 

43 

44 

45class ClubApplicationSettings(ApplicationSettingsModel): 

46 """Settings for the club application.""" 

47 

48 name: Literal["club"] 

49 

50 

51class CoachApplicationSettings(ApplicationSettingsModel): 

52 """Settings for the portal application.""" 

53 

54 name: Literal["coach"] 

55 

56 

57class PortalApplicationSettings(ApplicationSettingsModel): 

58 """Settings for the portal application.""" 

59 

60 name: Literal["portal"] 

61 

62 

63Application = Annotated[ 

64 Union[ 

65 AdminApplicationSettings, 

66 AuthenticationApplicationSettings, 

67 AuthorApplicationSettings, 

68 ClubApplicationSettings, 

69 CoachApplicationSettings, 

70 PortalApplicationSettings, 

71 ], 

72 Field(discriminator="name"), 

73] 

74 

75 

76class FrontendSettings(BaseModel): 

77 """Settings for the frontend.""" 

78 

79 test: bool = False 

80 path: str 

81 apps: list[Application] 

82 

83 

84class FilesSettings(BaseModel): 

85 """Settings for files (upload).""" 

86 

87 path: str 

88 

89 

90class AdminSettings(BaseModel): 

91 """Settings for the administrator of the website.""" 

92 

93 name: str 

94 email: str 

95 

96 

97class ContactSettings(BaseModel): 

98 """Settings for the contact of the club.""" 

99 

100 street: str 

101 city: str 

102 email: str 

103 

104 

105class WebsiteSettings(BaseModel): 

106 """Settings about the website.""" 

107 

108 url: str 

109 email: str 

110 name: str 

111 copyright: Optional[str] = None 

112 admin: Optional[AdminSettings] = None 

113 contact: Optional[ContactSettings] = None 

114 

115 

116class DatabaseSettings(BaseModel): 

117 """Settings for the database connection.""" 

118 

119 host: str 

120 name: str 

121 user: str 

122 password: str 

123 

124 

125class CORSSettings(BaseModel): 

126 """Settings for configuring CORS.""" 

127 

128 origins: list[str] = Field(default_factory=list) 

129 methods: list[str] = Field(default_factory=lambda: ["*"]) 

130 headers: list[str] = Field(default_factory=lambda: ["*"]) 

131 

132 

133class LoggerSettings(BaseModel): 

134 """Settings for the logger.""" 

135 

136 file: str = "kwai.log" 

137 level: str = "DEBUG" 

138 retention: str = "7 days" 

139 rotation: str = "1 day" 

140 

141 

142class EmailSettings(BaseModel): 

143 """Settings for sending emails.""" 

144 

145 host: str 

146 port: int 

147 ssl: bool = True 

148 tls: bool = True 

149 user: str | None = None 

150 password: str | None = None 

151 address: str = Field(alias="from") 

152 

153 

154class RedisSettings(BaseModel): 

155 """Settings for Redis.""" 

156 

157 host: str = "127.0.0.1" 

158 port: int = 6379 

159 password: str | None = None 

160 logger: LoggerSettings | None = None 

161 

162 

163class SecuritySettings(BaseModel): 

164 """Setting or security.""" 

165 

166 access_token_expires_in: int = 60 # minutes 

167 refresh_token_expires_in: int = 43200 # 30 days 

168 jwt_algorithm: str = "HS256" 

169 jwt_secret: str 

170 jwt_refresh_secret: str 

171 

172 

173class Settings(BaseModel): 

174 """Class with settings.""" 

175 

176 frontend: FrontendSettings 

177 

178 files: FilesSettings 

179 

180 security: SecuritySettings 

181 

182 logger: LoggerSettings | None = None 

183 

184 cors: CORSSettings | None = None 

185 

186 db: DatabaseSettings 

187 

188 website: WebsiteSettings 

189 

190 email: EmailSettings 

191 

192 redis: RedisSettings 

193 

194 

195@lru_cache 

196def get_settings() -> Settings: 

197 """Dependency function for creating the Settings instance. 

198 

199 The settings are cached with lru_cache, which means the file is only loaded ounce. 

200 

201 :raises: 

202 core.settings.SettingsException: Raised when the env variable is not set, or 

203 when the file 

204 can't be read. 

205 """ 

206 if ENV_SETTINGS_FILE in os.environ: 

207 settings_file = os.environ.get(ENV_SETTINGS_FILE, "") 

208 try: 

209 with open(settings_file, mode="rb") as file_handle: 

210 return Settings.model_validate(tomllib.load(file_handle)) 

211 except OSError as exc: 

212 raise SettingsException(f"Could not load {settings_file}") from exc 

213 raise SettingsException( 

214 f"{ENV_SETTINGS_FILE} should be set as environment variable" 

215 )