Un site web complet en Python : frontend et backend avec l'IA

Traduit de l'original en espagnol. Lire en espagnol

Aujourd’hui, vous pouvez créer des sites web modernes uniquement en Python, aussi bien côté frontend que côté backend, et intégrer l’intelligence artificielle pour proposer des expériences avancées. Je vous montre ici comment le faire en toute sécurité, en prenant comme exemple un générateur d’histoires pour enfants.

Live Demo: https://bedtimestory.zerogap.uk/


Architecture

  • Frontend : NiceGUI pour une interface web moderne, sans JavaScript.
  • Backend : FastAPI pour la logique.
  • IA : Azure OpenAI (via LangChain) pour générer des histoires ou toute autre tâche de langage naturel.

Exemple : générateur d’histoires avec l’IA

Le code suivant montre comment relier frontend et backend, et comment protéger vos endpoints grâce à la validation.

Backend (FastAPI + Azure OpenAI via LangChain)

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
from pydantic import BaseModel, validator
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from langchain_community.chat_models import AzureChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
import os
import re
from dotenv import load_dotenv

app = FastAPI()

def whitelist(text, max_length=50, pattern=r"[^a-zA-Z0-9áéíóúÁÉÍÓÚüÜñÑ\s.,;:!?'\"()-]"):
    """Allow only letters, numbers, punctuation and spaces up to max_length."""
    if text is None:
        return None
    # Remove any disallowed character
    clean = re.sub(pattern, "", text)
    # Truncate and strip
    return clean.strip()[:max_length]

class StoryRequest(BaseModel):
    theme: str = None
    main_character: str = None

    @validator('theme', 'main_character')
    def validate_field(cls, v):
        if v is not None:
            # Only allow reasonable length and allowed characters
            v = whitelist(v)
            if len(v) == 0:
                raise ValueError("Field cannot be empty after sanitization.")
        return v

@app.post("/generate")
async def generate_story(request: StoryRequest, fastapi_request: Request):
    # Only allow local requests for security
    client_host = fastapi_request.client.host
    if client_host not in ("127.0.0.1", "::1", "localhost"):
        raise HTTPException(status_code=403, detail="Forbidden")

    load_dotenv()
    endpoint = "https://your-private-resource.openai.azure.com/"
    deployment = "my-private-deployment"
    api_version = "2024-12-01-preview"
    credential = DefaultAzureCredential()
    token_provider = get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default")

    prompt = f"Write a children's story about {request.theme or 'friendship'}, starring a {request.main_character or 'dragon'}."
    system_prompt = "You are a creative children's storyteller. Only return the story."

    messages = [
        SystemMessage(content=system_prompt),
        HumanMessage(content=prompt)
    ]

    chat = AzureChatOpenAI(
        azure_endpoint=endpoint,
        api_version=api_version,
        deployment_name=deployment,
        azure_ad_token_provider=token_provider,
    )
    response = chat(messages)

    return JSONResponse({"text": response.content})

Frontend (NiceGUI)

from nicegui import ui

def generate_story():
    import httpx
    try:
        resp = httpx.post("http://localhost:8080/generate", json={})
        story = resp.json().get("text", "No story generated.")
        output_label.text = story
    except Exception as e:
        output_label.text = f"Error: {e}"

with ui.element('div').style('background-color: rgb(232 226 254);') \
          .classes('flex items-center justify-center absolute top-0 left-0 full-width full-height'):
    with ui.element('div').classes('full-width full-height flex flex-col items-center justify-center relative'):
        ui.image('assets/home.jpg').props('fit=contain').classes(
            'rounded-2xl w-full h-auto object-contain max-w-[95vw] max-h-[95vh]'
        )
        with ui.row().classes(
            'absolute bottom-0 left-[10vw] right-[10vw] px-8 flex flex-col md:flex-row flex-wrap items-center justify-between z-10 mb-[8vh]'
        ):
            ui.button(
                'SURPRISE ME!',
                on_click=generate_story
            ).classes(
                '!bg-yellow-400 !hover:bg-yellow-400 text-white rounded-full '
                'px-6 py-3 text-base font-bold flex items-center shadow-lg mr-4'
            ).props('icon=play_circle size=md')

output_label = ui.label("").classes("mt-6 text-lg")

ui.run()

Sécurité et bonnes pratiques pour la production

  • HTTPS : N’exposez jamais votre site sans HTTPS. Utilisez Nginx comme reverse proxy et un certificat SSL (par exemple Let’s Encrypt).
  • Assainissement et validation : L’exemple applique une validation robuste (longueur, caractères autorisés, champs obligatoires) pour éviter les attaques par injection et les abus.
  • Reverse proxy (Nginx) :
    Exemple de base :
  server {
      listen 443 ssl;
      server_name tu-dominio.com;

      ssl_certificate /etc/letsencrypt/live/tu-dominio.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/tu-dominio.com/privkey.pem;

      location / {
          proxy_pass http://127.0.0.1:8081;  # puerto NiceGUI
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
      }
  }

Maximiliano Díaz Doglia

AI Platform Engineer & Full-Stack Developer
Building Enterprise Integrations & Automations

Publié dans : IA