|
26 | 26 | from pydantic import BaseModel |
27 | 27 | from supabase import Client, create_client |
28 | 28 |
|
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 | +) |
30 | 37 |
|
31 | | -# Mount static files |
32 | 38 | app.mount("/static", StaticFiles(directory="static"), name="static") |
33 | 39 |
|
34 | 40 | templates = Jinja2Templates(directory="templates") |
|
486 | 492 | "communities-card-desc": ( |
487 | 493 | "Groupe local basé à Lomé, rencontre mensuelle et ateliers." |
488 | 494 | ), |
| 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.", |
489 | 510 | }, |
490 | 511 | "en": { |
491 | 512 | "site-title": "Python Togo", |
|
771 | 792 | "communities-card-desc": ( |
772 | 793 | "Local group based in Lomé, monthly meetups and workshops." |
773 | 794 | ), |
| 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.", |
774 | 808 | }, |
775 | 809 | } |
776 | 810 | DONATE_URL = "https://www.paypal.com/donate/?hosted_button_id=A6547S7YGMZ4A" |
777 | 811 |
|
778 | 812 |
|
| 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 | + |
779 | 968 | def get_language(request: Request) -> str: |
780 | 969 | """ |
781 | 970 | Determine the preferred language for the request. |
|
0 commit comments