Skip to content

Commit e3eb215

Browse files
authored
Merge pull request #6 from WassCodeur/main
Enhance error handling with custom bilingual error pages and update FastAPI app metadata
2 parents d9a6439 + f06ae5c commit e3eb215

2 files changed

Lines changed: 230 additions & 2 deletions

File tree

main.py

Lines changed: 191 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,15 @@
2626
from pydantic import BaseModel
2727
from supabase import Client, create_client
2828

29-
app = FastAPI(title="Python Togo")
29+
app = FastAPI(
30+
summary="Python Togo official website.",
31+
description="The Python Software Community Togo's official website.",
32+
docs_url=None,
33+
redoc_url=None,
34+
title="Python Togo",
35+
version="1.0.0",
36+
)
3037

31-
# Mount static files
3238
app.mount("/static", StaticFiles(directory="static"), name="static")
3339

3440
templates = Jinja2Templates(directory="templates")
@@ -486,6 +492,21 @@
486492
"communities-card-desc": (
487493
"Groupe local basé à Lomé, rencontre mensuelle et ateliers."
488494
),
495+
"error-404-title": "Page non trouvée",
496+
"error-404-heading": "Erreur 404",
497+
"error-404-message": "Désolé, la page que vous recherchez n'existe pas.",
498+
"error-404-home": "Retour à l'accueil",
499+
"error-500-title": "Erreur serveur",
500+
"error-500-heading": "Erreur 500",
501+
"error-500-message": (
502+
"Une erreur interne s'est produite. Veuillez réessayer plus tard."
503+
),
504+
"error-403-title": "Accès interdit",
505+
"error-403-heading": "Erreur 403",
506+
"error-403-message": "Vous n'avez pas la permission d'accéder à cette page.",
507+
"error-generic-title": "Erreur",
508+
"error-generic-heading": "Une erreur s'est produite",
509+
"error-generic-message": "Quelque chose s'est mal passé. Veuillez réessayer.",
489510
},
490511
"en": {
491512
"site-title": "Python Togo",
@@ -771,11 +792,179 @@
771792
"communities-card-desc": (
772793
"Local group based in Lomé, monthly meetups and workshops."
773794
),
795+
"error-404-title": "Page not found",
796+
"error-404-heading": "Error 404",
797+
"error-404-message": "Sorry, the page you are looking for does not exist.",
798+
"error-404-home": "Back to home",
799+
"error-500-title": "Server error",
800+
"error-500-heading": "Error 500",
801+
"error-500-message": "An internal error occurred. Please try again later.",
802+
"error-403-title": "Access forbidden",
803+
"error-403-heading": "Error 403",
804+
"error-403-message": "You do not have permission to access this page.",
805+
"error-generic-title": "Error",
806+
"error-generic-heading": "An error occurred",
807+
"error-generic-message": "Something went wrong. Please try again.",
774808
},
775809
}
776810
DONATE_URL = "https://www.paypal.com/donate/?hosted_button_id=A6547S7YGMZ4A"
777811

778812

813+
@app.exception_handler(404)
814+
async def not_found_handler(request: Request, exc: HTTPException):
815+
"""Handle 404 errors with a custom bilingual page.
816+
817+
Parameters
818+
----------
819+
request : fastapi.Request
820+
The incoming HTTP request.
821+
exc : fastapi.HTTPException
822+
The HTTP exception that was raised. Contains status code and detail.
823+
Returns
824+
-------
825+
fastapi.responses.TemplateResponse
826+
The rendered 404 error page with appropriate context.
827+
"""
828+
lang = get_language(request)
829+
return templates.TemplateResponse(
830+
request=request,
831+
name="error.html",
832+
status_code=404,
833+
context=ctx(
834+
request,
835+
{
836+
"status_code": 404,
837+
"title_key": "error-404-title",
838+
"heading_key": "error-404-heading",
839+
"message_key": "error-404-message",
840+
"detail": None,
841+
"meta_title": TRANSLATIONS[lang]["error-404-title"] + " — Python Togo",
842+
"meta_description": TRANSLATIONS[lang]["error-404-message"],
843+
},
844+
),
845+
)
846+
847+
848+
@app.exception_handler(500)
849+
async def internal_error_handler(request: Request, exc: Exception):
850+
"""Handle 500 errors with a custom bilingual page.
851+
852+
Parameters
853+
----------
854+
request : fastapi.Request
855+
The incoming HTTP request.
856+
exc : Exception
857+
The exception that was raised.
858+
Returns
859+
-------
860+
fastapi.responses.TemplateResponse
861+
The rendered 500 error page with appropriate context.
862+
"""
863+
lang = get_language(request)
864+
return templates.TemplateResponse(
865+
request=request,
866+
name="error.html",
867+
status_code=500,
868+
context=ctx(
869+
request,
870+
{
871+
"status_code": 500,
872+
"title_key": "error-500-title",
873+
"heading_key": "error-500-heading",
874+
"message_key": "error-500-message",
875+
"detail": None,
876+
"meta_title": TRANSLATIONS[lang]["error-500-title"] + " — Python Togo",
877+
"meta_description": TRANSLATIONS[lang]["error-500-message"],
878+
},
879+
),
880+
)
881+
882+
883+
@app.exception_handler(403)
884+
async def forbidden_handler(request: Request, exc: HTTPException):
885+
"""Handle 403 errors with a custom bilingual page.
886+
887+
Parameters
888+
----------
889+
request : fastapi.Request
890+
The incoming HTTP request.
891+
exc : fastapi.HTTPException
892+
The HTTP exception that was raised. Contains status code and detail.
893+
Returns
894+
-------
895+
fastapi.responses.TemplateResponse
896+
The rendered 403 error page with appropriate context.
897+
"""
898+
lang = get_language(request)
899+
return templates.TemplateResponse(
900+
request=request,
901+
name="error.html",
902+
status_code=403,
903+
context=ctx(
904+
request,
905+
{
906+
"status_code": 403,
907+
"title_key": "error-403-title",
908+
"heading_key": "error-403-heading",
909+
"message_key": "error-403-message",
910+
"detail": None,
911+
"meta_title": TRANSLATIONS[lang]["error-403-title"] + " — Python Togo",
912+
"meta_description": TRANSLATIONS[lang]["error-403-message"],
913+
},
914+
),
915+
)
916+
917+
918+
@app.exception_handler(HTTPException)
919+
async def http_exception_handler(request: Request, exc: HTTPException):
920+
"""Handle generic HTTP exceptions with a custom bilingual page.
921+
Parameters
922+
----------
923+
request : fastapi.Request
924+
The incoming HTTP request.
925+
exc : fastapi.HTTPException
926+
The HTTP exception that was raised. Contains status code and detail.
927+
Returns
928+
-------
929+
fastapi.responses.TemplateResponse
930+
The rendered error page with appropriate status code and context.
931+
"""
932+
lang = get_language(request)
933+
934+
error_map = {
935+
404: ("error-404-title", "error-404-heading", "error-404-message"),
936+
403: ("error-403-title", "error-403-heading", "error-403-message"),
937+
500: ("error-500-title", "error-500-heading", "error-500-message"),
938+
}
939+
940+
if exc.status_code in error_map:
941+
title_key, heading_key, message_key = error_map[exc.status_code]
942+
else:
943+
title_key, heading_key, message_key = (
944+
"error-generic-title",
945+
"error-generic-heading",
946+
"error-generic-message",
947+
)
948+
949+
return templates.TemplateResponse(
950+
request=request,
951+
name="error.html",
952+
status_code=exc.status_code,
953+
context=ctx(
954+
request,
955+
{
956+
"status_code": exc.status_code,
957+
"title_key": title_key,
958+
"heading_key": heading_key,
959+
"message_key": message_key,
960+
"detail": exc.detail if hasattr(exc, "detail") else None,
961+
"meta_title": TRANSLATIONS[lang][title_key] + " — Python Togo",
962+
"meta_description": TRANSLATIONS[lang][message_key],
963+
},
964+
),
965+
)
966+
967+
779968
def get_language(request: Request) -> str:
780969
"""
781970
Determine the preferred language for the request.

templates/error.html

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{% extends "base.html" %}
2+
3+
{% block title %}{{ t[title_key] }} — {{ t['site-title'] }}{% endblock %}
4+
5+
{% block content %}
6+
<div class="container" style="max-width: 800px; margin-top: 10px; margin-bottom: 10px; text-align: center;">
7+
<div >
8+
<div style="font-size: 120px; font-weight: bold; color: #056D1E; margin-bottom: 10px;">
9+
{{ status_code }}
10+
</div>
11+
<h1 style="font-size: 2.5rem; color: #333; margin-bottom: 20px;">
12+
{{ t[heading_key] }}
13+
</h1>
14+
<p style="font-size: 1.2rem; color: #666; margin-bottom: 10px;">
15+
{{ t[message_key] }}
16+
</p>
17+
{% if detail %}
18+
<p style="font-size: 1rem; color: #999; margin-bottom: 20px; font-style: italic;">
19+
{{ detail }}
20+
</p>
21+
{% endif %}
22+
<div style="margin-top: 40px;">
23+
<a href="/"
24+
style="display: inline-block;
25+
background-color: #056D1E;
26+
color: white;
27+
padding: 12px 30px;
28+
text-decoration: none;
29+
border-radius: 5px;
30+
font-size: 1.1rem;
31+
transition: background-color 0.3s;">
32+
{{ t['error-404-home'] }}
33+
</a>
34+
</div>
35+
36+
</div>
37+
</div>
38+
39+
{% endblock %}

0 commit comments

Comments
 (0)