Coverage for packages/kwai-core/src/kwai_core/settings.py: 96%

70 statements  

« prev     ^ index     » next       coverage.py v7.11.0, 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 anyio import Path 

10from pydantic import BaseModel, Field 

11 

12 

13ENV_SETTINGS_FILE = "KWAI_SETTINGS_FILE" 

14 

15 

16class SettingsException(Exception): 

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

18 

19 

20class ApplicationSettingsModel(BaseModel): 

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

22 

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

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

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

26 

27 

28class AdminApplicationSettings(ApplicationSettingsModel): 

29 """Settings for the admin application.""" 

30 

31 name: Literal["admin"] 

32 

33 

34class AuthenticationApplicationSettings(ApplicationSettingsModel): 

35 """Settings for the auth application.""" 

36 

37 name: Literal["auth"] 

38 

39 

40class AuthorApplicationSettings(ApplicationSettingsModel): 

41 """Settings for the author application.""" 

42 

43 name: Literal["author"] 

44 

45 

46class ClubApplicationSettings(ApplicationSettingsModel): 

47 """Settings for the club application.""" 

48 

49 name: Literal["club"] 

50 

51 

52class CoachApplicationSettings(ApplicationSettingsModel): 

53 """Settings for the portal application.""" 

54 

55 name: Literal["coach"] 

56 

57 

58class PortalApplicationSettings(ApplicationSettingsModel): 

59 """Settings for the portal application.""" 

60 

61 name: Literal["portal"] 

62 

63 

64Application = Annotated[ 

65 Union[ 

66 AdminApplicationSettings, 

67 AuthenticationApplicationSettings, 

68 AuthorApplicationSettings, 

69 ClubApplicationSettings, 

70 CoachApplicationSettings, 

71 PortalApplicationSettings, 

72 ], 

73 Field(discriminator="name"), 

74] 

75 

76 

77class FrontendSettings(BaseModel): 

78 """Settings for the frontend.""" 

79 

80 test: bool = False 

81 path: str 

82 apps: list[Application] 

83 

84 

85class FilesSettings(BaseModel): 

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

87 

88 path: str 

89 

90 

91class AdminSettings(BaseModel): 

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

93 

94 name: str 

95 email: str 

96 

97 

98class ContactSettings(BaseModel): 

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

100 

101 street: str 

102 city: str 

103 email: str 

104 

105 

106class WebsiteSettings(BaseModel): 

107 """Settings about the website.""" 

108 

109 url: str 

110 email: str 

111 name: str 

112 copyright: Optional[str] = None 

113 admin: Optional[AdminSettings] = None 

114 contact: Optional[ContactSettings] = None 

115 privacy_policy: Optional[str] = None 

116 

117 

118class DatabaseSettings(BaseModel): 

119 """Settings for the database connection.""" 

120 

121 host: str 

122 name: str 

123 user: str 

124 password: str 

125 

126 

127class CORSSettings(BaseModel): 

128 """Settings for configuring CORS.""" 

129 

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

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

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

133 

134 

135class LoggerSettings(BaseModel): 

136 """Settings for the logger.""" 

137 

138 file: os.PathLike[str] = Field(default=Path("kwai.log")) 

139 level: str = "DEBUG" 

140 retention: str = "7 days" 

141 rotation: str = "1 day" 

142 

143 

144class EmailSettings(BaseModel): 

145 """Settings for sending emails.""" 

146 

147 host: str 

148 port: int 

149 ssl: bool = True 

150 tls: bool = True 

151 user: str | None = None 

152 password: str | None = None 

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

154 

155 

156class RedisSettings(BaseModel): 

157 """Settings for Redis.""" 

158 

159 host: str = "127.0.0.1" 

160 port: int = 6379 

161 password: str | None = None 

162 logger: LoggerSettings | None = None 

163 

164 

165class GoogleSSOSettings(BaseModel): 

166 """Settings for Google SSO.""" 

167 

168 client_id: str 

169 client_secret: str 

170 

171 

172class SecuritySettings(BaseModel): 

173 """Setting or security.""" 

174 

175 access_token_expires_in: int = 60 # minutes 

176 refresh_token_expires_in: int = 43200 # 30 days 

177 jwt_algorithm: str = "HS256" 

178 jwt_secret: str 

179 jwt_refresh_secret: str 

180 

181 google: GoogleSSOSettings | None = None 

182 

183 

184class Settings(BaseModel): 

185 """Class with settings.""" 

186 

187 openapi_url: str = "/openapi.json" 

188 

189 frontend: FrontendSettings 

190 

191 files: FilesSettings 

192 

193 security: SecuritySettings 

194 

195 logger: LoggerSettings | None = None 

196 

197 cors: CORSSettings | None = None 

198 

199 db: DatabaseSettings 

200 

201 website: WebsiteSettings 

202 

203 email: EmailSettings 

204 

205 redis: RedisSettings 

206 

207 

208@lru_cache 

209def get_settings() -> Settings: 

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

211 

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

213 

214 :raises: 

215 kwai_core.settings.SettingsException: Raised when the env variable is not set, or 

216 when the file 

217 can't be read. 

218 """ 

219 if ENV_SETTINGS_FILE in os.environ: 

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

221 try: 

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

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

224 except OSError as exc: 

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

226 raise SettingsException( 

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

228 )