5
5
from fastapi .responses import RedirectResponse
6
6
from fastapi .staticfiles import StaticFiles
7
7
from fastapi .templating import Jinja2Templates
8
- from fastapi .exceptions import RequestValidationError , HTTPException , StarletteHTTPException
9
- from sqlmodel import Session
10
- from routers import authentication , organization , role , user
8
+ from fastapi .exceptions import RequestValidationError , StarletteHTTPException
9
+ from routers import authentication , organization , role , user , dashboard , terms_of_service , privacy_policy , about
11
10
from utils .auth import (
12
- HTML_PASSWORD_PATTERN ,
13
- get_user_with_relations ,
14
- get_optional_user ,
15
11
NeedsNewTokens ,
16
- get_user_from_reset_token ,
17
12
PasswordValidationError ,
18
- AuthenticationError
13
+ AuthenticationError ,
14
+ get_optional_user
19
15
)
16
+ from utils .db import set_up_db
20
17
from utils .models import User
21
- from utils .db import get_session , set_up_db
22
- from utils .images import MAX_FILE_SIZE , MIN_DIMENSION , MAX_DIMENSION , ALLOWED_CONTENT_TYPES
23
18
24
19
logger = logging .getLogger ("uvicorn.error" )
25
20
logger .setLevel (logging .DEBUG )
@@ -33,23 +28,35 @@ async def lifespan(app: FastAPI):
33
28
# Optional shutdown logic
34
29
35
30
31
+ # Initialize the FastAPI app
36
32
app : FastAPI = FastAPI (lifespan = lifespan )
37
33
38
- # Mount static files (e.g., CSS, JS)
34
+ # Mount static files (e.g., CSS, JS) and initialize Jinja2 templates
39
35
app .mount ("/static" , StaticFiles (directory = "static" ), name = "static" )
40
-
41
- # Initialize Jinja2 templates
42
36
templates = Jinja2Templates (directory = "templates" )
43
37
44
38
39
+ # --- Include Routers ---
40
+
41
+
42
+ app .include_router (authentication .router )
43
+ app .include_router (organization .router )
44
+ app .include_router (role .router )
45
+ app .include_router (user .router )
46
+ app .include_router (dashboard .router )
47
+ app .include_router (terms_of_service .router )
48
+ app .include_router (privacy_policy .router )
49
+ app .include_router (about .router )
50
+
51
+
45
52
# --- Exception Handling Middlewares ---
46
53
47
54
48
55
# Handle AuthenticationError by redirecting to login page
49
56
@app .exception_handler (AuthenticationError )
50
57
async def authentication_error_handler (request : Request , exc : AuthenticationError ):
51
58
return RedirectResponse (
52
- url = "/login" ,
59
+ url = app . url_path_for ( "read_login" ) ,
53
60
status_code = status .HTTP_303_SEE_OTHER
54
61
)
55
62
@@ -146,160 +153,21 @@ async def general_exception_handler(request: Request, exc: Exception):
146
153
)
147
154
148
155
149
- # --- Unauthenticated Routes ---
150
-
151
-
152
- # Define a dependency for common parameters
153
- async def common_unauthenticated_parameters (
154
- request : Request ,
155
- user : Optional [User ] = Depends (get_optional_user ),
156
- error_message : Optional [str ] = None ,
157
- ) -> dict :
158
- return {"request" : request , "user" : user , "error_message" : error_message }
156
+ # --- Home Page ---
159
157
160
158
161
159
@app .get ("/" )
162
160
async def read_home (
163
- params : dict = Depends (common_unauthenticated_parameters )
164
- ):
165
- if params ["user" ]:
166
- return RedirectResponse (url = "/dashboard" , status_code = 302 )
167
- return templates .TemplateResponse (params ["request" ], "index.html" , params )
168
-
169
-
170
- @app .get ("/login" )
171
- async def read_login (
172
- params : dict = Depends (common_unauthenticated_parameters ),
173
- email_updated : Optional [str ] = "false"
174
- ):
175
- if params ["user" ]:
176
- return RedirectResponse (url = "/dashboard" , status_code = 302 )
177
- params ["email_updated" ] = email_updated
178
- return templates .TemplateResponse (params ["request" ], "authentication/login.html" , params )
179
-
180
-
181
- @app .get ("/register" )
182
- async def read_register (
183
- params : dict = Depends (common_unauthenticated_parameters )
184
- ):
185
- if params ["user" ]:
186
- return RedirectResponse (url = "/dashboard" , status_code = 302 )
187
-
188
- params ["password_pattern" ] = HTML_PASSWORD_PATTERN
189
- return templates .TemplateResponse (params ["request" ], "authentication/register.html" , params )
190
-
191
-
192
- @app .get ("/forgot_password" )
193
- async def read_forgot_password (
194
- params : dict = Depends (common_unauthenticated_parameters ),
195
- show_form : Optional [str ] = "true" ,
196
- ):
197
- params ["show_form" ] = show_form == "true"
198
-
199
- return templates .TemplateResponse (params ["request" ], "authentication/forgot_password.html" , params )
200
-
201
-
202
- @app .get ("/about" )
203
- async def read_about (params : dict = Depends (common_unauthenticated_parameters )):
204
- return templates .TemplateResponse (params ["request" ], "about.html" , params )
205
-
206
-
207
- @app .get ("/privacy_policy" )
208
- async def read_privacy_policy (params : dict = Depends (common_unauthenticated_parameters )):
209
- return templates .TemplateResponse (params ["request" ], "privacy_policy.html" , params )
210
-
211
-
212
- @app .get ("/terms_of_service" )
213
- async def read_terms_of_service (params : dict = Depends (common_unauthenticated_parameters )):
214
- return templates .TemplateResponse (params ["request" ], "terms_of_service.html" , params )
215
-
216
-
217
- @app .get ("/auth/reset_password" )
218
- async def read_reset_password (
219
- email : str ,
220
- token : str ,
221
- params : dict = Depends (common_unauthenticated_parameters ),
222
- session : Session = Depends (get_session )
223
- ):
224
- authorized_user , _ = get_user_from_reset_token (email , token , session )
225
-
226
- # Raise informative error to let user know the token is invalid and may have expired
227
- if not authorized_user :
228
- raise HTTPException (status_code = 400 , detail = "Invalid or expired token" )
229
-
230
- params ["email" ] = email
231
- params ["token" ] = token
232
- params ["password_pattern" ] = HTML_PASSWORD_PATTERN
233
-
234
- return templates .TemplateResponse (params ["request" ], "authentication/reset_password.html" , params )
235
-
236
-
237
- # --- Authenticated Routes ---
238
-
239
-
240
- # Define a dependency for common parameters
241
- async def common_authenticated_parameters (
242
161
request : Request ,
243
- user : User = Depends (get_user_with_relations ),
244
- error_message : Optional [str ] = None
245
- ) -> dict :
246
- return {"request" : request , "user" : user , "error_message" : error_message }
247
-
248
-
249
- # Redirect to home if user is not authenticated
250
- @app .get ("/dashboard" )
251
- async def read_dashboard (
252
- params : dict = Depends (common_authenticated_parameters )
162
+ user : Optional [User ] = Depends (get_optional_user )
253
163
):
254
- return templates .TemplateResponse (params ["request" ], "dashboard/index.html" , params )
255
-
256
-
257
- @app .get ("/profile" )
258
- async def read_profile (
259
- params : dict = Depends (common_authenticated_parameters ),
260
- email_update_requested : Optional [str ] = "false" ,
261
- email_updated : Optional [str ] = "false"
262
- ):
263
- # Add image constraints to the template context
264
- params .update ({
265
- "max_file_size_mb" : MAX_FILE_SIZE / (1024 * 1024 ), # Convert bytes to MB
266
- "min_dimension" : MIN_DIMENSION ,
267
- "max_dimension" : MAX_DIMENSION ,
268
- "allowed_formats" : list (ALLOWED_CONTENT_TYPES .keys ()),
269
- "email_update_requested" : email_update_requested ,
270
- "email_updated" : email_updated
271
- })
272
- return templates .TemplateResponse (params ["request" ], "users/profile.html" , params )
273
-
274
-
275
- @app .get ("/organizations/{org_id}" )
276
- async def read_organization (
277
- org_id : int ,
278
- params : dict = Depends (common_authenticated_parameters )
279
- ):
280
- # Get the organization only if the user is a member of it
281
- org = next (
282
- (org for org in params ["user" ].organizations if org .id == org_id ),
283
- None
164
+ if user :
165
+ return RedirectResponse (url = "/dashboard" , status_code = 302 )
166
+ return templates .TemplateResponse (
167
+ "index.html" ,
168
+ {"request" : request , "user" : user }
284
169
)
285
- if not org :
286
- raise organization .OrganizationNotFoundError ()
287
-
288
- # Eagerly load roles and users
289
- org .roles
290
- org .users
291
- params ["organization" ] = org
292
-
293
- return templates .TemplateResponse (params ["request" ], "users/organization.html" , params )
294
-
295
170
296
- # --- Include Routers ---
297
-
298
-
299
- app .include_router (authentication .router )
300
- app .include_router (organization .router )
301
- app .include_router (role .router )
302
- app .include_router (user .router )
303
171
304
172
if __name__ == "__main__" :
305
173
import uvicorn
0 commit comments