Sito web completo in Python: frontend e backend con l'IA

Tradotto dall'originale in spagnolo. Leggi in spagnolo

Oggi puoi costruire siti web moderni usando solo Python, sia nel frontend sia nel backend, e integrare l’intelligenza artificiale per creare esperienze avanzate. Qui ti mostro come farlo in modo sicuro, usando come esempio un generatore di storie per bambini.

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


Architettura

  • Frontend: NiceGUI per un’interfaccia web moderna, senza JavaScript.
  • Backend: FastAPI per la logica.
  • IA: Azure OpenAI (tramite LangChain) per generare storie o qualsiasi altro compito di linguaggio naturale.

Esempio: generatore di storie con l’IA

Il codice seguente mostra come integrare frontend e backend e come proteggere i tuoi endpoint con la validazione.

Backend (FastAPI + Azure OpenAI tramite 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()

Sicurezza e buone pratiche per la produzione

  • HTTPS: Non esporre mai il tuo sito senza HTTPS. Usa Nginx come reverse proxy e un certificato SSL (per esempio Let’s Encrypt).
  • Sanificazione e validazione: L’esempio usa una validazione robusta (lunghezza, caratteri consentiti, campi obbligatori) per evitare attacchi di injection e abusi.
  • Reverse proxy (Nginx):
    Esempio di 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

Pubblicato in: IA